MySQL中臨時表主要有兩類,包括外部臨時表和內部臨時表。外部臨時表是經過語句create temporary table...建立的臨時表,臨時表只在本會話有效,會話斷開後,臨時表數據會自動清理。內部臨時表主要有兩類,一類是information_schema中臨時表,另外一類是會話執行查詢時,若是執行計劃中包含有「Using temporary」時,會產生臨時表。內部臨時表與外部臨時表的一個區別在於,咱們看不到內部臨時表的表結構定義文件frm。而外部臨時表的表定義文件frm,通常是以#sql{進程id}_{線程id}_序列號組成,所以不一樣會話能夠建立同名的臨時表。mysql
臨時表與普通表的主要區別在因而否在實例,會話,或語句結束後,自動清理數據。好比,內部臨時表,咱們在一個查詢中,若是要存儲中間結果集,而查詢結束後,臨時表就會自動回收,不會影響用戶表結構和數據。另外就是,不一樣會話的臨時表能夠重名,全部多個會話執行查詢時,若是要使用臨時表,不會有重名的擔心。5.7引入了臨時表空間後,全部臨時表都存儲在臨時表空間(非壓縮)中,臨時表空間的數據能夠複用。臨時表並不是只支持Innodb引擎,還支持myisam引擎,memory引擎等。所以,臨時表咱們看不到實體(idb文件),但其實不必定是內存表,也可能存儲在臨時表空間中。sql
臨時表既能夠innodb引擎表,也能夠是memory引擎表。這裏所謂的內存表,是說memory引擎表,經過建表語句create table ...engine=memory,數據所有在內存,表結構經過frm管理,一樣的內部的memory引擎表,也是看不到frm文件中,甚至看不到information_schema在磁盤上的目錄。在MySQL內部,information_schema裏面的臨時表就包含兩類:innodb引擎的臨時表和memory引擎的臨時表。好比TABLES表屬於memory臨時表,而columns,processlist,屬於innodb引擎臨時表。內存表全部數據都在內存中,在內存中數據結構是一個數組(堆表),全部數據操做都在內存中完成,對於小數據量場景,速度比較快(不涉及物理IO操做)。但內存畢竟是有限的資源,所以,若是數據量比較大,則不適合用內存表,而是選擇用磁盤臨時表(innodb引擎),這種臨時表採用B+樹存儲結構(innodb引擎),innodb的bufferpool資源是共享的,臨時表的數據可能會對bufferpool的熱數據有必定的影響,另外,操做可能涉及到物理IO。memory引擎表實際上也是能夠建立索引的,包括Btree索引和Hash索引,因此查詢速度很快,主要缺陷是內存資源有限。數組
前面提到執行計劃中包含有「Using temporary」時,會使用臨時表,這裏列兩個主要的場景。數據結構
測試表結構以下:測試
mysql> show create table t1_normal\G *************************** 1. row ***************************
Table: t1_normal Create Table: CREATE TABLE `t1_normal` ( `id` int(11) NOT NULL AUTO_INCREMENT, `c1` int(11) DEFAULT NULL, `c2` int(11) DEFAULT NULL, `c3` int(11) DEFAULT NULL, `c4` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=770023 DEFAULT CHARSET=utf8
mysql> explain select * from t1_normal union select * from t1_normal; +----+--------------+------------+------------+------+---------------+------+---------+------+--------+----------+-----------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+--------------+------------+------------+------+---------------+------+---------+------+--------+----------+-----------------+ | 1 | PRIMARY | t1_normal | NULL | ALL | NULL | NULL | NULL | NULL | 523848 | 100.00 | NULL | | 2 | UNION | t1_normal | NULL | ALL | NULL | NULL | NULL | NULL | 523848 | 100.00 | NULL | | NULL | UNION RESULT | <union1,2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary | +----+--------------+------------+------------+------+---------------+------+---------+------+--------+----------+-----------------+大數據
union操做的含義是,取兩個子查詢結果的並集,重複的數據只保留一行,經過創建一個帶主鍵的臨時表,就能夠解決「去重」問題,經過臨時表存儲最終的結果集,因此能看到執行計劃中Extra這一項裏面有「Using temporary」。與union相關的一個操做是union all,後者也是將兩個子查詢結果合併,但不解決重複問題。因此對於union all,沒有「去重」的含義,所以也就不須要臨時表了。優化
mysql> explain select * from t1_normal union all select * from t1_normal; +----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------+
| 1 | PRIMARY | t1_normal | NULL | ALL | NULL | NULL | NULL | NULL | 523848 | 100.00 | NULL |
| 2 | UNION | t1_normal | NULL | ALL | NULL | NULL | NULL | NULL | 523848 | 100.00 | NULL |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------+
場景2:group byspa
mysql> explain select c1,count(*) as count from t1_normal group by c1; +----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+---------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+---------------------------------+
| 1 | SIMPLE | t1_normal | NULL | ALL | NULL | NULL | NULL | NULL | 523848 | 100.00 | Using temporary; Using filesort |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+---------------------------------+
group by的含義是按指定列分組,並默認按照指定列有序。上面的SQL語句含義是將t1_normal中的數據按c1列的值分組,統計每種c1列值的記錄數目。從執行計劃中咱們看到了"Using temporary;Using filesort",對於group by而言,咱們首先須要統計每一個值出現的數目,這就須要藉助臨時表來快速定位,若是不存在,則插入一條記錄,若是存在,並累加計數,因此看到了"Using temporary";而後又由於group by隱含了排序含義,因此還須要按照c1列進行對記錄排序,因此看到了"Using filesort"。線程
1).消除filesort3d
實際上,group by也能夠顯示消除「排序含義」。
mysql> explain select c1,count(*) as count from t1_normal group by c1 order by null; +----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-----------------+
| 1 | SIMPLE | t1_normal | NULL | ALL | NULL | NULL | NULL | NULL | 523848 | 100.00 | Using temporary |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-----------------+
2).消除臨時表
mysql> explain select SQL_BIG_RESULT c1,count(*) as count from t1_normal group by c1; +----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+----------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+----------------+ | 1 | SIMPLE | t1_normal | NULL | ALL | NULL | NULL | NULL | NULL | 523848 | 100.00 | Using filesort | +----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+----------------+
3).SQL_BIG_RESULT
同時咱們語句中用到了「SQL_BIG_RESULT」這個hint,正是由於這個hint致使了咱們沒有使用臨時表,先說說SQL_BIG_RESULT和SQL_SMALL_RESULT的含義。
SQL_SMALL_RESULT:顯示指定用內存表(memory引擎)
SQL_BIG_RESULT:顯示指定用磁盤臨時表(myisam引擎或innodb引擎)
二者區別在於,使用磁盤臨時表能夠藉助主鍵作去重排序,適合大數據量;使用內存表寫入更快,而後在內存中排序,適合小數據量。下面是從MySQL手冊中摘錄的說明。
SQL_BIG_RESULT or SQL_SMALL_RESULT can be used with GROUP BY or DISTINCT to tell the optimizer that the result set has many rows or is small, respectively. For SQL_BIG_RESULT, MySQL directly uses disk-based temporary tables if needed, and prefers sorting to using a temporary table with a key on the GROUP BY elements. For SQL_SMALL_RESULT, MySQL uses fast temporary tables to store the resulting table instead of using sorting. This should not normally be needed.
回到問題自己,這裏MySQL優化器根據hint知道須要使用磁盤臨時表,而最終直接選擇了數組存儲+文件排序這種更輕量的方式。
一般的SQL優化方式是讓group by 的列創建索引,那麼執行group by時,直接按索引掃描該列,並統計便可,也就不須要temporary和filesort了。
mysql> alter table t1_normal add index idx_c1(c1); Query OK, 0 rows affected (1 min 23.82 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> explain select c1,count(*) as count from t1_normal group by c1 order by null; +----+-------------+-----------+------------+-------+---------------+--------+---------+------+--------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+---------------+--------+---------+------+--------+----------+-------------+
| 1 | SIMPLE | t1_normal | NULL | index | idx_c1 | idx_c1 | 5 | NULL | 523848 | 100.00 | Using index |
+----+-------------+-----------+------------+-------+---------------+--------+---------+------+--------+----------+-------------+
1).參數說明
max_heap_table_size
This variable sets the maximum size to which user-created MEMORY tables are permitted to grow,The value of the variable is used to calculate MEMORY table MAX_ROWS values.
這個參數主要針對用戶建立的MEMORY表,限制內存表最大空間大小,注意不是記錄數目,與單條記錄的長度有關。若是超出閥值,則報錯。ERROR 1114 (HY000): The table 'xxx' is full
tmp_table_size
The maximum size of internal in-memory temporary tables.
對於用戶手工建立的內存表,只有參數max_heap_table_size起做用;對於內部產生的內存表,則參數max_heap_table_size和tmp_table_size同時起做用。對於內部產生的內存表(好比union,group by等產生的臨時表),先是採用內存表(memory表),而後超過設置的閥值(max_heap_table_size,tmp_table_size)就會轉爲磁盤表,使用innodb引擎或者myisam引擎,經過參數internal_tmp_disk_storage_engine指定。
tmpdir
若是內存臨時表超出了限制,MySQL就會自動地把它轉化爲基於磁盤的MyISAM表,存儲在指定的tmpdir目錄下
2.狀態監控
Created_tmp_tables,內部臨時表數目
Created_tmp_disk_tables,磁盤臨時表數目
3.information_schema相關
mysql> create temporary table t1_tmp(id int primary key,c1 int); Query OK, 0 rows affected (0.02 sec) mysql> SELECT * FROM information_schema.INNODB_TEMP_TABLE_INFO; +----------+---------------+--------+-------+----------------------+---------------+ | TABLE_ID | NAME | N_COLS | SPACE | PER_TABLE_TABLESPACE | IS_COMPRESSED | +----------+---------------+--------+-------+----------------------+---------------+ | 10063 | #sql693d_29_0 | 5 | 45 | FALSE | FALSE | +----------+---------------+--------+-------+----------------------+---------------+