`

(转)MySQL 特性分析之内部临时表

 
阅读更多
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将利用内部临时表进行中间结果缓存,如果数据量比较大的话,内部临时表将会把数据存储在磁盘上,这样显然会对性能有所影响。为了尽可能的减少性能损失,我们需要尽量避免上述情况的出现。

来源:数据库内核月
原文:http://mysql.taobao.org/monthly/2016/06/07/
如有侵权或不周之处,敬请劳烦联系若飞(微信:1321113940)马上删除,谢谢!


分享到:
评论

相关推荐

    MySQL中的两种临时表

    内部临时表则由MySQL系统根据需要自动创建,用于存储查询的中间结果。这些表对用户不可见,但可以通过`EXPLAIN`或`SHOW STATUS`来检查MySQL是否使用了它们。内部临时表主要用于性能优化,包括在查询优化阶段和执行...

    Mysql临时表原理及创建方法解析

    MySQL临时表是数据库管理系统中的一种特殊表格,它主要用于在单个会话期间存储临时数据,以便在多个查询之间共享和处理。临时表的生命周期仅限于创建它的会话,一旦会话结束,临时表及其所有数据都会被自动删除,这...

    MySQL两种临时表的用法详解

    内部临时表则是MySQL为了优化查询性能而自动创建的,用户无法直接看到或控制这些表。它们用于存储查询过程中的中间结果,比如在执行联接、排序、分组等操作时。内部临时表分为HEAP表和OnDisk表,前者存储在内存中,...

    36.为什么临时表可以重名?1

    【MySQL临时表】是数据库操作中的一个重要工具,尤其在处理复杂查询和优化性能时发挥着关键作用。临时表与内存表虽然名字相似,但其实两者性质不同。内存表使用Memory引擎,数据存储在内存中,系统重启后数据丢失,...

    MySQL 内存分析

    tmpdir和tmpfs是与临时表相关的路径和文件系统,这些也会影响到MySQL的内存管理。 InnoDB存储引擎是MySQL中广泛使用的存储引擎之一,它有自己的内存管理机制。例如,InnoDB的缓冲池(Buffer Pool)就是一个重要的...

    Mysql实用手册 网易内部

    createtable`用于查询创建表(`CREA TETABLE`)的完整语法,包括临时表的创建、表选项以及分区选项等细节。 通过这种方式,用户可以快速找到所需的信息,从而更加高效地使用MySQL。 --- #### 第2章:表类型(存储...

    Oracle兼容性和全局临时表的实现.pptx

    Oracle兼容性和全局临时表的实现主要关注的是在数据库迁移过程中如何确保原有的功能特性和性能不受影响,特别是针对全局临时表的使用。Oracle数据库系统中,全局临时表是一种特殊类型的表,它的数据只对创建它的会话...

    MySQL 5.1中文手冊

    5.4. mysql_fix_privilege_tables:升级MySQL系统表 5.5. MySQL服务器关机进程 5.6. 一般安全问题 5.6.1. 通用安全指南 5.6.2. 使MySQL在攻击者面前保持安全 5.6.3. Mysqld安全相关启动选项 5.6.4. LOAD DATA LOCAL...

    MySQL优化深度分析及问题集锦课堂笔记

    MySQL有很多配置参数用于调优,例如tmp_table_size决定了内部临时表的大小。max_connections限制了可以同时打开的连接数。query_cache_type为no时,关闭了查询缓存功能。而innodb_buffer_pool是InnoDB存储引擎中最...

    MySQL 5.1参考手册

    5.4. mysql_fix_privilege_tables:升级MySQL系统表 5.5. MySQL服务器关机进程 5.6. 一般安全问题 5.6.1. 通用安全指南 5.6.2. 使MySQL在攻击者面前保持安全 5.6.3. Mysqld安全相关启动选项 5.6.4. LOAD DATA LOCAL...

    MySQL 5.1参考手册 (中文版)

    5.4. mysql_fix_privilege_tables:升级MySQL系统表 5.5. MySQL服务器关机进程 5.6. 一般安全问题 5.6.1. 通用安全指南 5.6.2. 使MySQL在攻击者面前保持安全 5.6.3. Mysqld安全相关启动选项 5.6.4. LOAD DATA LOCAL...

    mysql5.1中文手册

    MySQL的的主要特性 1.4.3. MySQL稳定性 1.4.4. MySQL表最大能达到多少 1.4.5. 2000年兼容性 1.5. MaxDB数据库管理系统概述 1.5.1. 什么是MaxDB? 1.5.2. MaxDB的历史 1.5.3. MaxDB的特性 ...

    MySQL 5.1参考手册中文版

    5.4. mysql_fix_privilege_tables:升级MySQL系统表 5.5. MySQL服务器关机进程 5.6. 一般安全问题 5.6.1. 通用安全指南 5.6.2. 使MySQL在攻击者面前保持安全 5.6.3. Mysqld安全相关启动选项 5.6.4. LOAD DATA ...

    MySQL 5.1官方简体中文参考手册

    5.4. mysql_fix_privilege_tables:升级MySQL系统表 5.5. MySQL服务器关机进程 5.6. 一般安全问题 5.6.1. 通用安全指南 5.6.2. 使MySQL在攻击者面前保持安全 5.6.3. Mysqld安全相关启动选项 5.6.4. LOAD DATA LOCAL...

    mysql-overview_rev5.json

    - **Memory**:数据存储在内存中,适合临时表。 - **Merge**:合并多个MyISAM表,用于空间优化。 五、MySQL的备份与恢复 MySQL提供了多种备份策略,如使用`mysqldump`工具进行逻辑备份,或者利用二进制日志进行增量...

    mysql-8.0.16-winx64-debug-test.zip

    3. **通用表表达式(CTE)**:MySQL 8.0引入了通用表表达式,允许在查询中定义临时结果集,这在复杂的查询和子查询中非常有用。 4. **InnoDB引擎改进**:InnoDB存储引擎在8.0.16版本中进行了大量优化,包括更快的索引...

    深入理解MySQL核心技术

    Memory将数据存储在内存中,适用于临时表。书中会深入探讨各引擎的工作原理和适用场景。 3. **SQL查询优化**:书中讲解了如何编写高效的SQL语句,包括索引的使用、查询优化器的选择、JOIN操作的优化等。还会讨论...

    MySQL5.1参考手册官方简体中文版

    5.4. mysql_fix_privilege_tables:升级MySQL系统表 5.5. MySQL服务器关机进程 5.6. 一般安全问题 5.6.1. 通用安全指南 5.6.2. 使MySQL在攻击者面前保持安全 5.6.3. Mysqld安全相关启动选项 5.6.4. LOAD DATA LOCAL...

    MySQL 版本升级 8.0.21升到8.0.23.docx

    这涉及到更新内部表结构、索引和其他元数据。在升级过程中,MySQL会自动处理这些迁移,但需要确保有足够的磁盘空间以应对临时文件。 **6. 兼容性与变更** 从8.0.21到8.0.23的升级,可能会包含一些性能优化、安全...

Global site tag (gtag.js) - Google Analytics