今天在微信公众号中看到一篇关于讲解MySQL中临时表的文章,根据文章做了一个简单小结,在写sql的时候尽可能注意这些问题。
1. 外部临时表:通过CREATE TEMPORARY TABLE 创建,只对当前用户可见,当前会话结束的时候,该临时表会自动关闭。这种临时表的命名与非临时表可以同名(同名后非临时表将对当前会话不可见,直到临时表被删除)。
2. 内部临时表:会被MySQL自动创建并用来存储某些操作的中间结果。用来进行性能优化,对用户来说是不可见的,但是通过EXPLAIN或者SHOW STATUS可以查看MYSQL是否使用了内部临时表用来帮助完成某个操作。使用内部临时表需要创建表以及中间数据的存取代价,所以用户在写SQL语句的时候应该尽量的去避免使用临时表。
内部临时表有两种类型:
2.1 一种是HEAP临时表,这种临时表的所有数据都会存在内存中,对于这种表的操作不需要IO操作。
2.2 另一种是OnDisk临时表,这种临时表会将数据存储在磁盘上。OnDisk临时表用来处理中间结果比较大的操作。如果HEAP临时表存储的数据大于MAX_HEAP_TABLE_SIZE,HEAP临时表将会被自动转换成OnDisk临时表。
在书写SQL语句的时候能够尽量少的使用内部临时表进行查询优化,将有效的提高查询执行的效率。
会利用到内部临时表的10种操作(这10种操作在写sql的时候应该尽量避免):
1. 在SQL语句中使用SQL_BUFFER_RESULT
SQL_BUFFER_RESULT主要用来让MySQL尽早的释放表上的锁。因为如果数据量很大的话,需要较长时间将数据发送到客户端,通过将数据缓冲到临时表中可以有效的减少读锁对表的占用时间。
使用:select SQL_BUFFER_RESULT * from t1;
2. 如果SQL语句中包含了DERIVED_TABLE
在5.7中,由于采用了新的优化方式,我们需要使用 set optimizer_switch=’derived_merge=off’来禁止derived table合并到外层的Query中。
3. 如果我们查询系统表的话,系统表的数据将被存储到内部临时表中。
可以通过SHOW STATUS来查看是否利用到了内部临时表。
4. 如果DISTINCT语句没有被优化掉,即DISTINCT语句被优化转换为GROUP BY操作或者利用UNIQUE INDEX消除DISTINCT, 内部临时表将会被使用。
5. 如果查询带有ORDER BY语句,并且不能被优化掉。下面几种情况会利用到内部临时表缓存中间数据,然后对中间数据进行排序。
5.1 如果连接表使用BNL(Batched Nestloop)/BKA(Batched Key Access)
5.1.1 BNL默认是打开的
5.1.2 关掉BNL后,ORDER BY将直接使用filesort
5.2 ORDER BY的列不属于执行计划中第一个连接表的列
select * from t as t1, t as t2 order by t2.a
5.3 如果ORDER BY的表达式是个复杂表达式
5.3.1 如果排序表达式是SP或者UDF
5.3.2 ORDER BY的列包含聚集函数
5.3.3 ORDER BY的列中包含有SCALAR SUBQUERY,当然该SCALAR SUBQUERY没有被优化掉。
5.4 如果查询既带有ORDER BY同时也有GROUP BY语句,但是两个语句使用的列不相同。
如果是5.7,我们需要将sql_mode设置为非only_full_group_by模式,否则会报错。
6. 如果查询带有GROUP BY语句,并且不能被优化掉
6.1 如果连接表使用BNL(Batched Nestloop)/BKA(Batched Key Access)
6.2 如果GROUP BY的列不属于执行计划中的第一个连接表
6.3 如果GROUP BY语句使用的列与ORDER BY语句使用的列不同
6.4 如果GROUP BY带有ROLLUP并且是基于多表外连接
6.5 如果GROUP BY语句使用的列来自于SCALAR SUBQUERY,并且没有被优化掉
7. IN表达式转换为semi-join进行优化
7.1 如果semi-join执行方式为Materialization
7.2 如果semi-join执行方式为Duplicate Weedout
8. 如果查询语句带有UNION,MySQL将利用内部临时表帮助UNION操作消除重复。
9. 如果查询语句使用多表更新
10. 如果聚集函数中包含如下函数,内部临时表也会被利用
10.1 count(distinct *)
10.2 group_concat
参考链接: