MySQL45讲读书笔记 36讲为什么临时表可以重名

一 序

   本文属于极客时间MySQL45讲读书笔记系列。本文老师主要讲临时表。

今天我们就从这个问题说起:临时表有哪些特征,为什么它适合这个场景?

这里,我需要先帮你厘清一个容易误解的问题:有的人可能会认为,临时表就是内存表。但是,这两个概念可是完全不同的。

  • 内存表,指的是使用Memory引擎的表,建表语法是create table … engine=memory。这种表的数据都保存在内存里,系统重启的时候会被清空,但是表结构还在。除了这两个特性看上去比较“奇怪”外,从其他的特征上看,它就是一个正常的表。

  • 而临时表,可以使用各种引擎类型 。如果是使用InnoDB引擎或者MyISAM引擎的临时表,写数据的时候是写到磁盘上的。当然,临时表也可以使用Memory引擎。

弄清楚了内存表和临时表的区别以后,我们再来看看临时表有哪些特征。

临时表的特性

为了便于理解,我们来看下下面这个操作序列:

图1 临时表特性示例

可以看到,临时表在使用上有以下几个特点:

  1. 建表语法是create temporary table …。
  2. 一个临时表只能被创建它的session访问,对其他线程不可见。所以,图中session A创建的临时表t,对于session B就是不可见的。
  3. 临时表可以与普通表同名。
  4. session A内有同名的临时表和普通表的时候,show create语句,以及增删改查语句访问的是临时表。
  5. show tables命令不显示临时表。

由于临时表只能被创建它的session访问,所以在这个session结束的时候,会自动删除临时表。也正是由于这个特性,临时表就特别适合我们文章开头的join优化这种场景。为什么呢?

原因主要包括以下两个方面:

  1. 不同session的临时表是可以重名的,如果有多个session同时执行join优化,不需要担心表名重复导致建表失败的问题。

  2. 不需要担心数据删除问题。如果使用普通表,在流程执行过程中客户端发生了异常断开,或者数据库发生异常重启,还需要专门来清理中间过程中生成的数据表。而临时表由于会自动回收,所以不需要这个额外的操作。

临时表的应用

由于不用担心线程之间的重名冲突,临时表经常会被用在复杂查询的优化过程中。

老师讲的分表后分分表维度的跨表查询的一种方案。

为什么临时表可以重名?

你可能会问,不同线程可以创建同名的临时表,这是怎么做到的呢?

接下来,我们就看一下这个问题。

我们在执行

create temporary table temp_t(id int primary key)engine=innodb;

这个语句的时候,MySQL要给这个InnoDB表创建一个frm文件保存表结构定义,还要有地方保存表数据。

这个frm文件放在临时文件目录下,文件名的后缀是.frm,前缀是“#sql{进程id}_{线程id}_序列号”。你可以使用select @@tmpdir命令,来显示实例的临时文件目录。

而关于表中数据的存放方式,在不同的MySQL版本中有着不同的处理方式:

  • 在5.6以及之前的版本里,MySQL会在临时文件目录下创建一个相同前缀、以.ibd为后缀的文件,用来存放数据文件;
  • 而从 5.7版本开始,MySQL引入了一个临时文件表空间,专门用来存放临时文件的数据。因此,我们就不需要再创建ibd文件了。

从文件名的前缀规则,我们可以看到,其实创建一个叫作t1的InnoDB临时表,MySQL在存储上认为我们创建的表名跟普通表t1是不同的,因此同一个库下面已经有普通表t1的情况下,还是可以再创建一个临时表t1的。

为了便于后面讨论,我先来举一个例子。

图4 临时表的表名

这个进程的进程号是1234,session A的线程id是4,session B的线程id是5。所以你看到了,session A和session B创建的临时表,在磁盘上的文件不会重名。

MySQL维护数据表,除了物理上要有文件外,内存里面也有一套机制区别不同的表,每个表都对应一个table_def_key。

  • 一个普通表的table_def_key的值是由“库名+表名”得到的,所以如果你要在同一个库下创建两个同名的普通表,创建第二个表的过程中就会发现table_def_key已经存在了。
  • 而对于临时表,table_def_key在“库名+表名”基础上,又加入了“server_id+thread_id”。

也就是说,session A和sessionB创建的两个临时表t1,它们的table_def_key不同,磁盘文件名也不同,因此可以并存。

在实现上,每个线程都维护了自己的临时表链表。这样每次session内操作表的时候,先遍历链表,检查是否有这个名字的临时表,如果有就优先操作临时表,如果没有再操作普通表;在session结束的时候,对链表里的每个临时表,执行 “DROP TEMPORARY TABLE +表名”操作。

除了上面通过显式的执行命令create temporary table创建的表,

优化器隐式创建临时表

   这种临时表,是数据库为了辅助某些复杂SQL的执行而创建的辅助表,是否需要临时表,一般都是由优化器决定。与用户显式创建的临时表直接创建磁盘文件不同,如果需要优化器觉得SQL需要临时表辅助,会先使用内存临时表,如果超过配置的内存(min(tmp_table_size, max_heap_table_siz)),就会转化成磁盘临时表,这种磁盘临时表就类似用户显式创建的,引擎类型通过参数internal_tmp_disk_storage_engine控制。一般稍微复杂一点的查询,包括且不限于order by, group by, distinct等,都会用到这种隐式创建的临时表。用户可以通过explain命令,在Extra列中,看是否有Using temporary这样的字样,如果有,就肯定要用临时表。

 In some cases, the server creates internal temporary tables while processing statements. Users have no direct control over when this occurs.

The server creates temporary tables under conditions such as these:

  • Evaluation of UNION statements, with some exceptions described later.

  • Evaluation of some views, such those that use the TEMPTABLE algorithm, UNION, or aggregation.

  • Evaluation of derived tables (see Section 13.2.10.8, “Derived Tables”).

  • Tables created for subquery or semijoin materialization (see Section 8.2.2, “Optimizing Subqueries, Derived Tables, and View References”).

  • Evaluation of statements that contain an ORDER BY clause and a different GROUP BY clause, or for which the ORDER BY or GROUP BY contains columns from tables other than the first table in the join queue.

  • Evaluation of DISTINCT combined with ORDER BY may require a temporary table.

  • For queries that use the SQL_SMALL_RESULT modifier, MySQL uses an in-memory temporary table, unless the query also contains elements (described later) that require on-disk storage.

  • To evaluate INSERT ... SELECT statements that select from and insert into the same table, MySQL creates an internal temporary table to hold the rows from the SELECT, then inserts those rows into the target table. See Section 13.2.5.1, “INSERT ... SELECT Statement”.

  • Evaluation of multiple-table UPDATE statements.

  • Evaluation of GROUP_CONCAT() or COUNT(DISTINCT) expressions.

 

小结

今天这篇文章,我和你介绍了临时表的用法和特性。

在实际应用中,临时表一般用于处理比较复杂的计算逻辑。由于临时表是每个线程自己可见的,所以不需要考虑多个线程执行同一个处理逻辑时,临时表的重名问题。在线程退出的时候,临时表也能自动删除,省去了收尾和异常处理的工作。

在binlog_format='row’的时候,临时表的操作不记录到binlog中,也省去了不少麻烦,这也可以成为你选择binlog_format时的一个考虑因素。

另外,还有使用比如order by操作,会调用filesort函数。这个函数也会先使用内存(sort_buffer_size)排序,如果不够,就会创建一个临时文件。还有从MySQL的版本来看,也是临时表的数据和undo逐步分开,也是为了性能考虑。

看了这么多,还是觉得临时表挺陌生的,应该有些排查性能用得到。

参考:

https://dev.mysql.com/doc/refman/5.7/en/internal-temporary-tables.html

http://mysql.taobao.org/monthly/2019/04/01/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章