暂无图片
暂无图片
1
暂无图片
暂无图片
暂无图片

MySQL内部临时表

原创 杨明翰 2021-10-30
2600

不同于create temporary table 创建的临时表,内存临时表是指MySQL在处理语句时内部创建的用于存储中间结果的临时表。用户无法控制内部临时表的创建,MySQL优化器会自行判断是否使用内部临时表。通常 group by、order by、union、CTE、distinct、insert……select、窗口函数 和多表的update等语句可能会使用到内部临时表。

要确定语句是否使用了临时表,可以通过explain返回的结果来确认。通常MySQL会优先在内存中创建内部临时表,但在某些情况下会直接在磁盘创建内部临时表。例如表中存在BLOB或TEXT列;以及在union或union all中select的字段存在大于512的字符串等等情况。

内部临时表在内存中使用TempTable或Memory引擎,在磁盘上使用InnoDB引擎组织存储数据。

内存内部临时表

MySQL8.0开始TempTable引擎作为默认的内存内部临时表引擎,代替以前的Memory引擎。新增参数internal_tmp_mem_storage_engine用以控制内存内部临时表的引擎,该参数可以设置全局或session级别配置,并且支持动态修改。

Command-Line Format --internal-tmp-mem-storage-engine=#
System Variable internal_tmp_mem_storage_engine
Scope Global, Session
Dynamic Yes
SET_VAR Hint Applies Yes
Type Enumeration
Default Value TempTable
Valid Values MEMORY/TempTable

TempTable 存储引擎为 VARCHAR 和 VARBINARY 列提供高效的存储,并且从8.0.13开始支持存储Blob列。TempTable将Null标志,数据长度和数据的指针存储在单元格中,每一行维护一个单元格数组。列值按顺序连续存放在单个内存区域中,存储在数组后。每个单元格占16个字节。如内存临时表使用Memory引擎,对 VARCHAR 和 VARBINARY 列转换成定长格式CHAR 和 BINARY,填充到最大列长度存储。

参数temptable_max_ram用来控制TempTable引擎使用的最大内存大小,默认1G,可以动态调整。

Command-Line Format --temptable-max-ram=#
System Variable temptable_max_ram
Scope Global
Dynamic Yes
SET_VAR Hint Applies No
Type Integer
Default Value 1073741824
Minimum Value 2097152
Maximum Value 2^64-1

参数temptable_use_mmap决定当TempTable存储引擎占用的内存量超过temptable_max_ram时,是在磁盘创建内存映射临时文件还是创建InnoDB临时表。

Command-Line Format --temptable-use-mmap[={OFF|ON}]
Introduced 8.0.16
Deprecated 8.0.26
System Variable temptable_use_mmap
Scope Global
Dynamic Yes
SET_VAR Hint Applies No
Type Boolean
Default Value ON

参数 temptable_max_mmap确定磁盘创建内存映射临时文件的大小,超过该大小后将会创建InnoDB临时表。temptable_use_mmap=OFF 与 temptable_max_mmap=0 等价

Command-Line Format --temptable-max-mmap=#
Introduced 8.0.23
System Variable temptable_max_mmap
Scope Global
Dynamic Yes
SET_VAR Hint Applies No
Type Integer
Default Value 1073741824
Minimum Value 0
Maximum Value 2^64-1

内存映射临时文件的存储路径受参数tmpdir控制,当创建和打开后将会立刻删除,无法在tmpdir定义的目录中可见。只有当临时文件被TempTable 存储引擎关闭,或者mysqld进程关闭后占用的空间才会被回收。需要注意的是,数据不会在内存和内存映射临时文件间移动。如果内存用量没有超过temptable_max_ram,则新数据写入内存,反之亦然。例如表的数据写入临时文件一部分后内存有空闲了,则剩余数据会写入内存。

但设置temptable_use_mmap=OFF 或者 temptable_max_mmap=0后,innodb引擎临时表将作为TempTable存储的溢出存储,超过temptable_max_ram限制的内存表将转换为InnoDB磁盘内部临时表表,并且属于该表的任何行都从内存移动到 InnoDB 磁盘内部临时表。

如internal_tmp_mem_storage_engine=memory,内存中临时表的最大大小由 tmp_table_size 或 max_heap_table_size 值定义,以较小者为准。超过后将转换成磁盘临时表。

磁盘内部临时表

从MySQL8.0.16开始,磁盘内存临时表都使用InnoDB引擎。8.0.15之前参数internal_tmp_disk_storage_engine可以控制磁盘内部临时表的引擎。注意MyISAM不支持CTE。

Command-Line Format --internal-tmp-disk-storage-engine=#
Removed 8.0.16
System Variable internal_tmp_disk_storage_engine
Scope Global
Dynamic Yes
SET_VAR Hint Applies No
Type Enumeration
Default Value INNODB
Valid Values MYISAM``INNODB

如果内部临时表占用磁盘空间较多,可以调整temptable_max_mmap控制内存映射临时文件的大小,也可以调整innodb_temp_data_file_path限制innodb临时表空间的最大大小。

监控临时表的创建

状态值 Created_tmp_tables 反应Memory引擎的内部临时表创建次数;

状态值 Created_tmp_disk_tables 反应磁盘内部临时表的创建次数。注意:该指标不反应内存映射临时文件的创建。

mysql> show global status like 'Created_tmp_tables'; +--------------------+-------+ | Variable_name | Value | +--------------------+-------+ | Created_tmp_tables | 7 | +--------------------+-------+ 1 row in set (0.00 sec) mysql> show global status like 'Created_tmp_disk_tables'; +-------------------------+-------+ | Variable_name | Value | +-------------------------+-------+ | Created_tmp_disk_tables | 0 | +-------------------------+-------+ 1 row in set (0.00 sec)
复制

对于TempTable引擎,在performance_schema.memory_summary_global_by_event_name视图中可以根据EVENT_NAME分别查看内存和磁盘使用情况

前提是要打开相关的instrument

#查看instrument状态 mysql> select * from performance_schema.setup_instruments where NAME LIKE 'memory/temptable/%'; +--------------------------------+---------+-------+-------------------+------------+---------------+ | NAME | ENABLED | TIMED | PROPERTIES | VOLATILITY | DOCUMENTATION | +--------------------------------+---------+-------+-------------------+------------+---------------+ | memory/temptable/physical_disk | YES | NULL | | 0 | NULL | | memory/temptable/physical_ram | YES | NULL | global_statistics | 0 | NULL | +--------------------------------+---------+-------+-------------------+------------+---------------+ 2 rows in set (0.00 sec) #开启 UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME LIKE 'memory/temptable/%'; #关闭 UPDATE performance_schema.setup_instruments SET ENABLED = 'NO' WHERE NAME LIKE 'memory/temptable/%';
复制

查看TempTable引擎内存和磁盘使用情况

#TempTable引擎分配的内存量 mysql> SELECT * FROM performance_schema.memory_summary_global_by_event_name WHERE EVENT_NAME = 'memory/temptable/physical_ram'\G *************************** 1. row *************************** EVENT_NAME: memory/temptable/physical_ram COUNT_ALLOC: 3 #分配次数 COUNT_FREE: 2 #free次数 SUM_NUMBER_OF_BYTES_ALLOC: 3145728 #分配的总字节数 SUM_NUMBER_OF_BYTES_FREE: 2097152 #清理的总字节数 LOW_COUNT_USED: 0 #CURRENT_COUNT_USED值的低水位线 CURRENT_COUNT_USED: 1 #当前正在使用的数量 = COUNT_ALLOC - COUNT_FREE HIGH_COUNT_USED: 1 #CURRENT_COUNT_USED值的高水位线 LOW_NUMBER_OF_BYTES_USED: 0 #CURRENT_NUMBER_OF_BYTES_USED值的低水位线 CURRENT_NUMBER_OF_BYTES_USED: 1048576 #正在使用的字节数 HIGH_NUMBER_OF_BYTES_USED: 1048576 #CURRENT_NUMBER_OF_BYTES_USED值的高水位线 1 row in set (0.00 sec) #TempTable引擎溢出后分配的磁盘内存映射临时文件量 mysql> SELECT * FROM performance_schema.memory_summary_global_by_event_name WHERE EVENT_NAME = 'memory/temptable/physical_disk'\G *************************** 1. row *************************** EVENT_NAME: memory/temptable/physical_disk COUNT_ALLOC: 0 COUNT_FREE: 0 SUM_NUMBER_OF_BYTES_ALLOC: 0 SUM_NUMBER_OF_BYTES_FREE: 0 LOW_COUNT_USED: 0 CURRENT_COUNT_USED: 0 HIGH_COUNT_USED: 0 LOW_NUMBER_OF_BYTES_USED: 0 CURRENT_NUMBER_OF_BYTES_USED: 0 HIGH_NUMBER_OF_BYTES_USED: 0 1 row in set (0.00 sec)
复制

简单测试

查看当前配置

#使用TempTable作为内存内部临时表 mysql> select @@global.internal_tmp_mem_storage_engine; +------------------------------------------+ | @@global.internal_tmp_mem_storage_engine | +------------------------------------------+ | TempTable作为内存内部临时表 | +------------------------------------------+ 1 row in set (0.00 sec) #溢出后在磁盘创建内存映射临时文件 mysql> select @@global.temptable_use_mmap; +-----------------------------+ | @@global.temptable_use_mmap | +-----------------------------+ | 1 | +-----------------------------+ 1 row in set (0.00 sec) #TempTablez最大内存 2M mysql> select @@global.temptable_max_ram; +----------------------------+ | @@global.temptable_max_ram | +----------------------------+ | 2097152 | +----------------------------+ 1 row in set (0.00 sec) #磁盘内存映射临时文件 4M mysql> select @@global.temptable_max_mmap; +-----------------------------+ | @@global.temptable_max_mmap | +-----------------------------+ | 4194304 | +-----------------------------+ 1 row in set (0.00 sec) #InnoDB临时表空间大小14M mysql> select @@global.innodb_temp_data_file_path; +-------------------------------------+ | @@global.innodb_temp_data_file_path | +-------------------------------------+ | ibtmp1:12M:autoextend:max:14M | +-------------------------------------+ 1 row in set (0.00 sec)
复制

构建一个查询,从执行计划看会使用临时表

mysql> explain select * from sbtest3 a,sbtest4 b,sbtest6 c where a.c=b.c and b.k=c.k order by a.id,c.k\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: a partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 98704 filtered: 100.00 Extra: Using temporary; Using filesort *************************** 2. row *************************** id: 1 select_type: SIMPLE table: b partitions: NULL type: ALL possible_keys: k_4 key: NULL key_len: NULL ref: NULL rows: 98704 filtered: 10.00 Extra: Using where; Using join buffer (hash join) *************************** 3. row *************************** id: 1 select_type: SIMPLE table: c partitions: NULL type: ref possible_keys: k_6 key: k_6 key_len: 4 ref: test_recovery_2.b.k rows: 5 filtered: 100.00 Extra: NULL 3 rows in set, 1 warning (0.00 sec)
复制

多执行几次查询后再检查下内存表的创建情况

#TempTable共分配了10次内存表,最大内存使用量达到上限temptable_max_ram 2M mysql> SELECT * FROM performance_schema.memory_summary_global_by_event_name WHERE EVENT_NAME = 'memory/temptable/physical_ram'\G *************************** 1. row *************************** EVENT_NAME: memory/temptable/physical_ram COUNT_ALLOC: 10 COUNT_FREE: 8 SUM_NUMBER_OF_BYTES_ALLOC: 10485760 SUM_NUMBER_OF_BYTES_FREE: 8388608 LOW_COUNT_USED: 0 CURRENT_COUNT_USED: 2 HIGH_COUNT_USED: 2 LOW_NUMBER_OF_BYTES_USED: 0 CURRENT_NUMBER_OF_BYTES_USED: 2097152 HIGH_NUMBER_OF_BYTES_USED: 2097152 1 row in set (0.00 sec) #TempTable共分配了35次磁盘空间存储临时内存映射文件,最分配大小为4M,达到temptable_max_mmap参数定义上上限4M mysql> SELECT * FROM performance_schema.memory_summary_global_by_event_name WHERE EVENT_NAME = 'memory/temptable/physical_disk'\G *************************** 1. row *************************** EVENT_NAME: memory/temptable/physical_disk COUNT_ALLOC: 35 COUNT_FREE: 34 SUM_NUMBER_OF_BYTES_ALLOC: 56623104 SUM_NUMBER_OF_BYTES_FREE: 55574528 LOW_COUNT_USED: 0 CURRENT_COUNT_USED: 1 HIGH_COUNT_USED: 3 LOW_NUMBER_OF_BYTES_USED: 0 CURRENT_NUMBER_OF_BYTES_USED: 1048576 HIGH_NUMBER_OF_BYTES_USED: 4194304 1 row in set (0.00 sec) #有3InnoDB磁盘临时表创建 mysql> show global status like 'Created_tmp_%'; +-------------------------+-------+ | Variable_name | Value | +-------------------------+-------+ | Created_tmp_disk_tables | 3 | | Created_tmp_files | 2595 | | Created_tmp_tables | 53 | +-------------------------+-------+ 3 rows in set (0.00 sec) #查看innodb临时表空间,空间大小还未达最大值 mysql> SELECT FILE_NAME, TABLESPACE_NAME, ENGINE, INITIAL_SIZE, TOTAL_EXTENTS*EXTENT_SIZE AS TotalSizeBytes, DATA_FREE, MAXIMUM_SIZE FROM INFORMATION_SCHEMA.FILES WHERE TABLESPACE_NAME = 'innodb_temporary'\G *************************** 1. row *************************** FILE_NAME: /data/mysql/data/ibtmp1 TABLESPACE_NAME: innodb_temporary ENGINE: InnoDB INITIAL_SIZE: 12582912 #初始化的文件大小 TotalSizeBytes: 12582912 DATA_FREE: 6291456 #整个表空间的可用空间总量 MAXIMUM_SIZE: 14680064 #最大文件大小 1 row in set (0.01 sec)
复制
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
1人已赞赏
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论