MySQL临时表分为外部临时表和内部临时表两种。用户通过CREATETEMPORARYTABLE创建一个外部临时表。SQL语句执行过程中,MySQL自己创建了一个内部临时表。如果explain输出结果的Extra列中出现Usingtemporary,说明SQL语句执行过程中使用了内部临时表。为了描述方便,本文后面内容中的临时表和内部临时表含义相同,都是指MySQL在SQL语句执行过程中自己创建的临时表。1.准备本文使用了两个示例表:t_recbuf和t_internal_tmp_table。两个表的结构完全一样。t_recbuf的表结构如下:CREATETABLE`t_recbuf`(`id`int(10)unsignedNOTNULLAUTO_INCREMENT,`i1`int(10)unsignedDEFAULT'0',`str1`varchar(32)DEFAULT'',`str2`varchar(255)DEFAULT'',`c1`char(11)DEFAULT'',`e1`enum('北京','上海','广州','深圳','天津','杭州','成都','重庆','苏州','南京','恰尔滨','沉阳','长春','厦门','福州','南昌','泉州','德清','长沙','武汉')DEFAULT'北京',`s1`set('eat','drink','play','music','clothes','food','living','walking','前后','左右','上下','里外','远近','长度','黑白','水星','金星','地球','火星','木星','土星','天王星','海王星','冥王星')DEFAULT'',`bit1`bit(8)DEFAULTb'0',`bit2`bit(17)DEFAULTb'0',`blob1`blob,`d1`decimal(10,2)DEFAULTNULL,PRIMARYKEY(`id`))ENGINE=InnoDBAUTO_INCREMENT=2001DEFAULTCHARSET=utf8;2、哪些场景会用到临时表?MySQL使用临时表的场景有很多,下面列举其中的一些场景:orderby和groupby字段是不同的。join语句中orderby或groupby字段不属于执行计划中的第一个表。包含distinct关键字的聚合函数,例如:count(distincti1)、sum(distincti1)等。使用union或uniondistinct关键字的SQL语句。派生表(说明输出的select_type列的值为DERIVED)。子查询半连接物化(将子查询结果保存在临时表中,然后与主查询进行join连接)。子查询物化(半连接物化以外的场景,如非关联子查询、半连接去重等)。insert...select语句的源表和目标表是同一张表,例如:insertintot_recbuf(i1,str1)selecti1,str1fromt_recbuf)。上面列出的场景是基于官方文档做的一些改动。您不必担心您是否记住了上述场景。这是判断SQL语句是否使用临时表最方便快捷的方法。查看执行计划是最方便快捷的方式。只要在explain输出的Extra列中出现Usingtemporary,就说明使用了临时表。3、临时表用的是哪个存储引擎?MySQL临时表可以选择3种存储引擎:MEMORY、MyISAM、InnoDB。MEMORY是内存引擎,数据和索引都存储在内存中;MyISAM和InnoDB是磁盘存储引擎,数据和索引都存储在磁盘中。SQL执行过程中,如果需要使用临时表,MySQL默认使用MEMORY存储引擎。有两个条件会影响MySQL的默认行为。如果满足以下2个条件中的任何一个,临时表将使用MyISAM或InnoDB存储引擎。情况1,写入临时表的字段包含大对象(BLOB)字段。关于哪些类型的字段属于大对象,可以看看这篇文章:MySQL大对象(BLOB)与字符串double技术。情况2,系统变量big_tables的值为ON,也就是说如果要使用临时表,必须使用MyISAM或者InnoDB存储引擎。但是在big_tables=ON的前提下,如果能确定某条SQL语句写入临时表的数据会很小,MEMORY存储引擎完全够用,可以对单条SQL语句进行特殊处理.在SQL语句中加入SQL_SMALL_RESULT提示,告诉MySQL:我只需要MEMORY存储引擎。SQL_SMALL_RESULT的用法是这样的:selectSQL_SMALL_RESULT*fromt_recbuf前面已经介绍过了。MySQL如何选择内存和磁盘存储引擎?如果MySQL决定使用磁盘存储引擎,使用MyISAM还是InnoDB?这个选择很简单。如果系统变量internal_tmp_disk_storage_engine的值为MyISAM,则选择MyISAM存储引擎,如果值为InnoDB,则使用InnoDB存储引擎。internal_tmp_disk_storage_engine的值只能从MyISAM和InnoDB中选择,默认为InnoDB。选择存储引擎4.内存临时表变成磁盘临时表。MEMORY存储引擎表的记录是定长的,不支持大对象(BLOB)字段。变长类型的字段(VARCHAR、VARBINARY)也会按照定义的最大长度存储,实际上相当于CHAR和BINARY字段。内存临时表插入记录占用的空间加上待插入记录占用的空间,如果超过阈值,临时表的存储引擎将从内存存储引擎变为磁盘存储引擎。内存占用超过阈值临时表占用内存空间的阈值由系统变量tmp_table_size和max_heap_table_size中较小的一个决定。tmp_table_size的默认大小为16M,最小可以设置为1K,最大值为超大值。max_heap_table_size默认为16M,最小可以设置为16K,最大也是一个超级巨大的值。由于MEMORY引擎的固定记录长度,判断临时内存表占用的空间是否超过阈值非常简单。将临时表存储引擎改为磁盘存储引擎的过程如下:创建一个MyISAM或InnoDB临时表,选择哪种存储引擎由internal_tmp_disk_storage_engine控制。将内存临时表中的所有记录一一复制到磁盘临时表中。将原计划插入到内存临时表但还没有插入到磁盘临时表中的记录插入。删除内存临时表。直到内存临时表满了才创建磁盘临时表,比如创建磁盘临时表,代价太大。如果我们一开始就知道执行SQL语句需要临时表,而内存临时表不能存放那么多记录,直接告诉MySQL使用磁盘临时表岂不是可以节省很多开销桌子?是的,如果我们一开始就知道SQL语句中数据量大会导致使用临时磁盘表,在SQL语句中添加SQL_BIG_RESULT(这个在MySQL中叫做hint),在MySQL选择存储引擎的时候对于临时表,它会直接选择磁盘存储引擎。SQL_BIG_RESULT的用法是这样的:selectSQL_BIG_RESULTe1,min(i1)fromt_internal_tmp_tablegroupbye1如果我们在SQL语句中加入SQL_BIG_RESULT提示,查询优化器通过使用磁盘临时表评估执行成本,它也可能来到使用磁盘临时表如果对t_internal_tmp_table表中的记录进行排序后成本高于groupby的成本,则会选择先对t_internal_tmp_table表中的记录进行排序,然后对排序后的记录进行groupby操作,所以首先不需要内存临时表和磁盘临时表。5.哪些字段写入临时表?从哪些字段写入临时表来看,临时表可以分为两类:为整个SQL语句服务的临时表。为单个聚合函数提供服务的临时表。对于为整个SQL语句服务的临时表,在SQL语句执行过程中,需要将存储引擎返回给Server层的字段写入到临时表中。写入临时表的字段内容可以是字段值,也可以是根据字段值计算函数的结果。以两条SQL为例进行说明。selecte1,count(i1)fromt_internal_tmp_tablegroupbye1ExampleSQL1.SQL执行过程中,MySQL会将t_internal_tmp_table表的e1字段值和count(i1)的计算结果写入临时表。selecta.e1,b.c1,count(a.i1)astfromt_internal_tmp_tableasainnerjoint_recbufasbona.id=b.idgroupbya.e1,b.c1withrollup示例SQL2,由于汇总存在,聚合函数的计算结果不能写入临时表,但是聚合函数的参数中的字段值应该写入临时表。SQL执行过程中,MySQL会将t_internal_tmp_table表的e1、i1字段值和t_recbuf表的c1字段值写入临时表。将t_internal_tmp_table和t_recbuf两张表的连接查询得到的记录全部写入临时表后,对临时表中的记录进行groupby和聚合(count)操作。对于为单个聚合函数服务的临时表,SQL语句执行过程中只会将聚合函数中的字段写入临时表。下面以一条SQL为例进行说明。selecte1,count(distincti1)astfromt_internal_tmp_tablegroupbye1ExampleSQL3,临时表只对count(distincti1)中的i1字段进行去重,所以只会将t_internal_tmp_table表的i1字段值写入临时表,并且会在临时表中为i1字段创建唯一索引,对i1字段进行去重。6.索引了哪些字段?MySQL使用临时表,可能是为了groupby分组和聚合,也可能是为了记录的去重(distinct),或者只是为了存储子查询的执行结果,避免重复执行子查询。对于groupby和distinct,为了保证临时表中的一个groupbygroup只有一条记录,distinct字段只保留一条内容相同的记录,并会为对应的创建唯一索引临时表中的字段。非常重要的注意事项:临时表中最多会有一个索引,要么用于groupby,要么用于distinct。6.1groupbyselecte1,count(i1)fromt_internal_tmp_tablegroupbye1这是上一节(5.哪些字段写入临时表?)的例子SQL1,写入e1字段值和count(i1)的计算在临时表结果中(每个组中i1字段的值不为NULL的记录数)。为了保证e1字段的每个值在临时表中只有一条记录,MySQL会为e1字段创建一个唯一索引,索引名称为
