MySQL · 特性分析 · 内部临时表

本文涉及的产品
RDS PostgreSQL Serverless,0.5-4RCU 50GB 3个月
推荐场景:
对影评进行热评分析
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
简介:

MySQL中的两种临时表

外部临时表

通过CREATE TEMPORARY TABLE 创建的临时表,这种临时表称为外部临时表。这种临时表只对当前用户可见,当前会话结束的时候,该临时表会自动关闭。这种临时表的命名与非临时表可以同名(同名后非临时表将对当前会话不可见,直到临时表被删除)。

内部临时表

内部临时表是一种特殊轻量级的临时表,用来进行性能优化。这种临时表会被MySQL自动创建并用来存储某些操作的中间结果。这些操作可能包括在优化阶段或者执行阶段。这种内部表对用户来说是不可见的,但是通过EXPLAIN或者SHOW STATUS可以查看MYSQL是否使用了内部临时表用来帮助完成某个操作。内部临时表在SQL语句的优化过程中扮演着非常重要的角色, MySQL中的很多操作都要依赖于内部临时表来进行优化。但是使用内部临时表需要创建表以及中间数据的存取代价,所以用户在写SQL语句的时候应该尽量的去避免使用临时表。

内部临时表有两种类型:一种是HEAP临时表,这种临时表的所有数据都会存在内存中,对于这种表的操作不需要IO操作。另一种是OnDisk临时表,顾名思义,这种临时表会将数据存储在磁盘上。OnDisk临时表用来处理中间结果比较大的操作。如果HEAP临时表存储的数据大于MAX_HEAP_TABLE_SIZE(详情请参考MySQL手册中系统变量部分),HEAP临时表将会被自动转换成OnDisk临时表。OnDisk临时表在5.7中可以通过INTERNAL_TMP_DISK_STORAGE_ENGINE系统变量选择使用MyISAM引擎或者InnoDB引擎。

本篇文章主要介绍哪些操作可能会利用到内部临时表。如果用户在书写SQL语句的时候能够尽量少的使用内部临时表进行查询优化,将有效的提高查询执行的效率。

首先我们定义一个表t1,

CREATE TABLE t1( a int, b int); INSERT INTO t1 VALUES(1,2),(3,4);


下面所有的操作都是基于表t1进行举例的。

  • 在SQL语句中使用SQL_BUFFER_RESULT hint

SQL_BUFFER_RESULT主要用来让MySQL尽早的释放表上的锁。因为如果数据量很大的话,需要较长时间将数据发送到客户端,通过将数据缓冲到临时表中可以有效的减少读锁对表的占用时间。
例如:


	mysql> explain format=json select SQL_BUFFER_RESULT * from t1;
	EXPLAIN
	{
	  "query_block": {
		"select_id": 1,
		"cost_info": {
		  "query_cost": "2.00"
		},
		"buffer_result": {
		  "using_temporary_table": true,
		  "table": {
			"table_name": "t1",
			"access_type": "ALL",
		...


  • 如果SQL语句中包含了DERIVED_TABLE。

在5.7中,由于采用了新的优化方式,我们需要使用 set optimizer_switch=’derived_merge=off’来禁止derived table合并到外层的Query中。
例如:

mysql> explain format=json select * from (select * from t1) as tt;
	EXPLAIN
	{
	  "query_block": {
		"select_id": 1,
		"cost_info": {
		  "query_cost": "2.40"
		},
		"table": {
		  "table_name": "tt",
		  "access_type": "ALL",
		  ...
		  "materialized_from_subquery": {
			"using_temporary_table": true,
		...


  • 如果我们查询系统表的话,系统表的数据将被存储到内部临时表中。

我们当前不能使用EXPLAIN来查看是否读取系统表数据需要利用到内部临时表,但是可以通过SHOW STATUS来查看是否利用到了内部临时表。
例如:

mysql> select * from information_schema.character_sets;
	mysql> show status like 'CREATE%';
  • 如果DISTINCT语句没有被优化掉,即DISTINCT语句被优化转换为GROUP BY操作或者利用UNIQUE INDEX消除DISTINCT, 内部临时表将会被使用。
  • mysql> explain format=json select distinct a from t1;
    	EXPLAIN
    	{
    	{
    	  "query_block": {
    		"select_id": 1,
    		"cost_info": {
    		  "query_cost": "1.60"
    		},
    		"duplicates_removal": {
    		  "using_temporary_table": true,
    		...

  • 如果查询带有ORDER BY语句,并且不能被优化掉。下面几种情况会利用到内部临时表缓存中间数据,然后对中间数据进行排序。

1)如果连接表使用BNL(Batched Nestloop)/BKA(Batched Key Access)
例如:

1))BNL默认是打开的


mysql> explain format=json select * from t1, t1 as t2 order by t1.a;
EXPLAIN
{
  "query_block": {
  "select_id": 1,
  "cost_info": {
	"query_cost": "22.00"
  },
  "ordering_operation": {
	"using_temporary_table": true,
  ...


2))关掉BNL后,ORDER BY将直接使用filesort。

mysql> set optimizer_switch='block_nested_loop=off';
Query OK, 0 rows affected (0.00 sec)
mysql> explain format=json select * from t1, t1 as t2 order by t1.a;
EXPLAIN
{
   "query_block": {
	"select_id": 1,
	"cost_info": {
	  "query_cost": "25.00"
	},
	"ordering_operation": {
	  "using_filesort": true,
	...


2)ORDER BY的列不属于执行计划中第一个连接表的列。
例如:

mysql> explain format=json select * from t as t1, t as t2 order by t2.a;
EXPLAIN
{
   "query_block": {
	"select_id": 1,
	"cost_info": {
	  "query_cost": "25.00"
	},
	"ordering_operation": {
	  "using_temporary_table": true,
	...


3)如果ORDER BY的表达式是个复杂表达式。

那么什么样的ORDER BY表达式,MySQL认为是复杂表达式呢?

1))如果排序表达式是SP或者UDF。
例如:

drop function if exists func1;
delimiter |
create function func1(x int)
returns int deterministic
begin
declare z1, z2 int;
set z1 = x;
set z2 = z1+2;
return z2;
end|
delimiter ;
explain format=json select * from t1 order by func1(a);
{
	"query_block": {
	"select_id": 1,
	"cost_info": {
	  "query_cost": "2.20"
	},
	"ordering_operation": {
	  "using_temporary_table": true,
	...


2))ORDER BY的列包含聚集函数

为了简化执行计划,我们利用INDEX来优化GROUP BY语句。
例如:

 create index idx1 on t1(a);
  explain format=json SELECt a FROM t1 group by a order by sum(a);
  | {
	   "query_block": {
		"select_id": 1,
		"cost_info": {
		  "query_cost": "1.20"
		},
		"ordering_operation": {
		  "using_temporary_table": true,
		  "using_filesort": true,
		  "grouping_operation": {
			"using_filesort": false,
		...
  drop index idx1 on t1;

3))ORDER BY的列中包含有SCALAR SUBQUERY,当然该SCALAR SUBQUERY没有被优化掉。
例如:


explain format=json select (select rand() from t1 limit 1) as a from t1 order by a;		
| {
	  "query_block": {
		"select_id": 1,
		"cost_info": {
		  "query_cost": "1.20"
		},
		"ordering_operation": {
		  "using_temporary_table": true,
		  "using_filesort": true,
			...


4) 如果查询既带有ORDER BY同时也有GROUP BY语句,但是两个语句使用的列不相同。

注意: 如果是5.7,我们需要将sql_mode设置为非only_full_group_by模式,否则会报错。

同样为了简化执行计划,我们利用INDEX来优化GROUP BY语句。
例如:

set sql_mode='';
create index idx1 on t1(b);
explain format=json select t1.a from t1 group by t1.b order by 1;
| {
	 "query_block": {
		"select_id": 1,
		"cost_info": {
		  "query_cost": "1.40"
		},
	"ordering_operation": {
		  "using_temporary_table": true,
		  "using_filesort": true,
		  "grouping_operation": {
			"using_filesort": false,
	...
drop index idx1 on t1;


  • 如果查询带有GROUP BY语句,并且不能被优化掉。下面几种情况会利用到内部临时表缓存中间数据,然后对中间数据进行GROUP BY。

1)如果连接表使用BNL(Batched Nestloop)/BKA(Batched Key Access)。
例如:

explain format=json select t2.a from t1, t1 as t2 group by t1.a;
	| {
		"query_block": {
		"select_id": 1,
		"cost_info": {
		  "query_cost": "8.20"
		},
		"grouping_operation": {
		  "using_temporary_table": true,
		  "using_filesort": true,
		  "cost_info": {
			"sort_cost": "4.00"
		...


2) 如果GROUP BY的列不属于执行计划中的第一个连接表。
例如:

explain format=json select t2.a from t1, t1 as t2 group by t2.a;
	| {
		"query_block": {
		"select_id": 1,
		"cost_info": {
		  "query_cost": "8.20"
		},
		"grouping_operation": {
		  "using_temporary_table": true,
		  "using_filesort": true,
		  "nested_loop": [
		...


3) 如果GROUP BY语句使用的列与ORDER BY语句使用的列不同。
例如:

set sql_mode='';
	explain format=json select t1.a from t1 group by t1.b order by t1.a;
	| {
	   "query_block": {
		"select_id": 1,
		"cost_info": {
		  "query_cost": "1.40"
		},
		"ordering_operation": {
		  "using_filesort": true,
		  "grouping_operation": {
			"using_temporary_table": true,
			"using_filesort": false,
		...


4) 如果GROUP BY带有ROLLUP并且是基于多表外连接。
例如:

explain format=json select sum(t1.a) from t1 left join t1 as t2 on true group by t1.a with rollup;
	| {
		"query_block": {
		"select_id": 1,
		"cost_info": {
		  "query_cost": "7.20"
		},
		"grouping_operation": {
		  "using_temporary_table": true,
		  "using_filesort": true,
		  "cost_info": {
			"sort_cost": "4.00"
		  },
		...


5) 如果GROUP BY语句使用的列来自于SCALAR SUBQUERY,并且没有被优化掉。
例如:

explain format=json select (select avg(a) from t1) as a from t1 group by a;
	| {
		"query_block": {
		"select_id": 1,
		"cost_info": {
		  "query_cost": "3.40"
		},
		"grouping_operation": {
		  "using_temporary_table": true,
		  "using_filesort": true,
		  "cost_info": {
			"sort_cost": "2.00"
		  },
		...


  • IN表达式转换为semi-join进行优化
    1) 如果semi-join执行方式为Materialization
    例如:

  • set optimizer_switch='firstmatch=off,duplicateweedout=off';
    explain format=json select * from t1 where a in (select b from t1);
    | {
    	"query_block": {
    	"select_id": 1,
    	"cost_info": {
    	  "query_cost": "5.60"
    	},
    	"nested_loop": [
    	  {
    		 "rows_examined_per_scan": 1,
    			  "materialized_from_subquery": {
    				"using_temporary_table": true,
    				"query_block": {
    				  "table": {
    					"table_name": "t1",
    					"access_type": "ALL",
    
    	... 


2) 如果semi-join执行方式为Duplicate Weedout
例如:

set optimizer_switch='firstmatch=off';
	explain format=json select * from t1 where a in (select b from t1);
	 | {
		"query_block": {
		"select_id": 1,
		"cost_info": {
		  "query_cost": "4.80"
		},
		"duplicates_removal": {
		  "using_temporary_table": true,
		  "nested_loop": [
				{
		...


  • 如果查询语句带有UNION,MySQL将利用内部临时表帮助UNION操作消除重复。
    例如:
  • explain format=json select * from t1 union select * from t1;
    	| {
    		"query_block": {
    		"union_result": {
    		  "using_temporary_table": true,
    		  "table_name": "<union1,2>",
    		...


  • 如果查询语句使用多表更新。
    这里Explain不能看到内部临时表被利用,所以需要查看status。
    例如:
  • update t1, t1 as t2 set t1.a=3;
    show status like 'CREATE%';


  • 如果聚集函数中包含如下函数,内部临时表也会被利用。

  • 1) count(distinct *)
    例如:
    	explain format=json select count(distinct a) from t1;
    2) group_concat
    例如:
    	explain format=json select group_concat(b) from t1;


总之,上面列出了10种情况,MySQL将利用内部临时表进行中间结果缓存,如果数据量比较大的话,内部临时表将会把数据存储在磁盘上,这样显然会对性能有所影响。为了尽可能的减少性能损失,我们需要尽量避免上述情况的出现。

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
20天前
|
SQL 关系型数据库 MySQL
【MySQL】SQL分析的几种方法
以上就是SQL分析的几种方法。需要注意的是,这些方法并不是孤立的,而是相互关联的。在实际的SQL分析中,我们通常需要结合使用这些方法,才能找出最佳的优化策略。同时,SQL分析也需要对数据库管理系统,数据,业务需求有深入的理解,这需要时间和经验的积累。
42 12
|
2月前
|
关系型数据库 MySQL OLAP
无缝集成 MySQL,解锁秒级 OLAP 分析性能极限,完成任务可领取三合一数据线!
通过 AnalyticDB MySQL 版、DMS、DTS 和 RDS MySQL 版协同工作,解决大规模业务数据统计难题,参与活动完成任务即可领取三合一数据线(限量200个),还有机会抽取蓝牙音箱大奖!
|
4月前
|
SQL 关系型数据库 MySQL
MySQL事务日志-Undo Log工作原理分析
事务的持久性是交由Redo Log来保证,原子性则是交由Undo Log来保证。如果事务中的SQL执行到一半出现错误,需要把前面已经执行过的SQL撤销以达到原子性的目的,这个过程也叫做"回滚",所以Undo Log也叫回滚日志。
170 7
MySQL事务日志-Undo Log工作原理分析
|
4月前
|
关系型数据库 MySQL 数据库
mysql慢查询每日汇报与分析
通过启用慢查询日志、提取和分析慢查询日志,可以有效识别和优化数据库中的性能瓶颈。结合适当的自动化工具和优化措施,可以显著提高MySQL数据库的性能和稳定性。希望本文的详解和示例能够为数据库管理人员提供有价值的参考,帮助实现高效的数据库管理。
108 11
|
5月前
|
SQL 安全 关系型数据库
【MySQL基础篇】事务(事务操作、事务四大特性、并发事务问题、事务隔离级别)
事务是MySQL中一组不可分割的操作集合,确保所有操作要么全部成功,要么全部失败。本文利用SQL演示并总结了事务操作、事务四大特性、并发事务问题、事务隔离级别。
2315 2
【MySQL基础篇】事务(事务操作、事务四大特性、并发事务问题、事务隔离级别)
|
3月前
|
缓存 NoSQL 关系型数据库
MySQL原理简介—4.深入分析Buffer Pool
本文介绍了MySQL的Buffer Pool机制,包括其作用、配置方法及内部结构。Buffer Pool是MySQL用于缓存磁盘数据页的关键组件,能显著提升数据库读写性能。默认大小为128MB,可根据服务器配置调整(如32GB内存可设为2GB)。它通过free链表管理空闲缓存页,flush链表记录脏页,并用LRU链表区分冷热数据以优化淘汰策略。此外,还探讨了多Buffer Pool实例、chunk动态调整等优化并发性能的方法,以及如何通过`show engine innodb status`查看Buffer Pool状态。关键词:MySQL内存数据更新机制。
|
5月前
|
SQL 关系型数据库 MySQL
MySQL 窗口函数详解:分析性查询的强大工具
MySQL 窗口函数从 8.0 版本开始支持,提供了一种灵活的方式处理 SQL 查询中的数据。无需分组即可对行集进行分析,常用于计算排名、累计和、移动平均值等。基本语法包括 `function_name([arguments]) OVER ([PARTITION BY columns] [ORDER BY columns] [frame_clause])`,常见函数有 `ROW_NUMBER()`, `RANK()`, `DENSE_RANK()`, `SUM()`, `AVG()` 等。窗口框架定义了计算聚合值时应包含的行。适用于复杂数据操作和分析报告。
291 11
|
7月前
|
存储 缓存 关系型数据库
MySQL事务日志-Redo Log工作原理分析
事务的隔离性和原子性分别通过锁和事务日志实现,而持久性则依赖于事务日志中的`Redo Log`。在MySQL中,`Redo Log`确保已提交事务的数据能持久保存,即使系统崩溃也能通过重做日志恢复数据。其工作原理是记录数据在内存中的更改,待事务提交时写入磁盘。此外,`Redo Log`采用简单的物理日志格式和高效的顺序IO,确保快速提交。通过不同的落盘策略,可在性能和安全性之间做出权衡。
2028 14
MySQL事务日志-Redo Log工作原理分析
|
6月前
|
关系型数据库 MySQL
mysql事务特性
原子性:一个事务内的操作统一成功或失败 一致性:事务前后的数据总量不变 隔离性:事务与事务之间相互不影响 持久性:事务一旦提交发生的改变不可逆
|
6月前
|
存储 关系型数据库 MySQL
MySQL 8.0特性-自增变量的持久化
【11月更文挑战第8天】在 MySQL 8.0 之前,自增变量(`AUTO_INCREMENT`)的行为在服务器重启后可能会发生变化,导致意外结果。MySQL 8.0 引入了自增变量的持久化特性,将其信息存储在数据字典中,确保重启后的一致性。这提高了开发和管理的稳定性,减少了主键冲突和数据不一致的风险。默认情况下,MySQL 8.0 启用了这一特性,但在升级时需注意行为变化。
144 1

相关产品

  • 云数据库 RDS MySQL 版