1.情景展示
在實際開發過程中,當現有手段無法滿足我們想要的數據時,我們就可以通過創建臨時表,保存一些臨時數據的方式,來用作數據的過渡。
2.具體分析
臨時表只在當前連接可見,當關閉連接時,MySQL會自動刪除表並釋放所有空間;
使用其他MySQL客戶端程序連接MySQL數據庫服務器來創建臨時表,那么只有在關閉客戶端程序時才會銷毀臨時表(當然也可以手動刪除)。
在mysql中,臨時表可分為:外表臨時表和內部臨時表
3.外部臨時表(推薦使用)
通過CREATE TEMPORARY TABLE 創建的臨時表,這種臨時表稱為外部臨時表。
這種臨時表的命名與非臨時表可以同名(同名后非臨時表將對當前會話不可見,直到臨時表被刪除)。
引擎類型:memory(heap)、myisam、merge、innodb ,不支持mysql cluster(簇);
注意:
自己所用的數據庫賬號要有建立臨時表的權限;
在同一條sql中,不能關聯2次相同的臨時表;
臨時表在建立連接時可見(准確地來說是可用),關閉時會清除空間,刪除臨時表(也可以通過DROP TABLE手動刪表);
show tables 不會列出臨時表(對外不可見);
不能使用rename重命名臨時表。但是,你可以alter table代替:只能使用alter table old_tp_table_name rename new_tp_table_name;
影響使用replication功能。
簡單來說,就是:臨時表的用法和普通表一樣。
舉例:
通常情況下,我喜歡在存儲過程當中,使用臨時表。
4.內部臨時表(盡量規避)
內部臨時表是一種特殊輕量級的臨時表,用來進行性能優化。
這種臨時表會被MySQL自動創建並用來存儲某些操作的中間結果,這些操作可能包括在優化階段或者執行階段。
這種內部表對用戶來說是不可見的,但是通過EXPLAIN或者SHOW STATUS可以查看MYSQL是否使用了內部臨時表用來幫助完成某個操作。
內部臨時表在SQL語句的優化過程中扮演着非常重要的角色, MySQL中的很多操作都要依賴於內部臨時表來進行優化。
但是使用內部臨時表需要創建表以及中間數據的存取代價,所以用戶在寫SQL語句的時候應該盡量的去避免使用臨時表。
(如果用戶在書寫SQL語句的時候能夠盡量少的使用內部臨時表進行查詢優化,將有效的提高查詢執行的效率。)
內部臨時表有兩種類型:
一種是HEAP臨時表,這種臨時表的所有數據都會存在內存中,對於這種表的操作不需要IO操作;
另一種是OnDisk臨時表,顧名思義,這種臨時表會將數據存儲在磁盤上。
OnDisk臨時表用來處理中間結果比較大的操作:如果HEAP臨時表存儲的數據大於MAX_HEAP_TABLE_SIZE(詳情請參考MySQL手冊中系統變量部分),HEAP臨時表將會被自動轉換成OnDisk臨時表。
OnDisk臨時表在5.7中可以通過INTERNAL_TMP_DISK_STORAGE_ENGINE系統變量選擇使用MyISAM引擎或者InnoDB引擎。
如何知道SQL查詢語句到底有沒有使用內部臨時表?
需要通過expalin來完成,具體表現形式有兩種:見文末推薦。
mysql會使用內部臨時表的情況匯總:
情形1:在SQL語句中使用SQL_BUFFER_RESULT
SQL_BUFFER_RESULT主要用來讓MySQL盡早的釋放表上的鎖。
因為如果數據量很大的話,需要較長時間將數據發送到客戶端,通過將數據緩沖到臨時表中可以有效的減少讀鎖對表的占用時間。
情形2:使用派生表(DERIVED_TABLE)
類似這種,但是,這個沒有用內部臨時表,等有合適案例了,再補上。
可通過SELECT @@optimizer_switch;語句來查看derived_merge當前的狀態(on/off);
可使用:set optimizer_switch='derived_merge=off'來禁止derived table合並到外層的Query中。
mysql 5.1中開始引入optimizer_switch, 控制mysql優化器行為。他有一些結果集,通過on和off控制開啟和關閉優化器行為。使用有效期全局和會話兩個級別;
在mysql優化語句過程中,可通過設置optimizer_switch控制優化行為。
情形3:查詢系統表
如果我們查詢系統表的話,系統表的數據將被存儲到內部臨時表中。
當我們使用EXPLAIN來查看是否讀取系統表數據需要利用到內部臨時表時,得到的結果為:否;
但是,這並不意味着查詢系統表沒有用到內部臨時表;
查詢系統表前,臨時表數量為10;查詢后,臨時表數量為18。
情形4:使用distinct或者group by且字段沒有設置索引
town_village_mapping表沒有任何索引
我們可以看到,無論是:group by 還是distinct,都是用了內部臨時表;
town_village_mapping_old表的county_code字段有普通索引
我們可以看到:此時走的就是索引,並且沒有使用內部臨時表。
說明:這里,沒有考慮索引失效的情況,具體問題具體分析。
情形5:關聯查詢+order by
MySQL如何執行關聯查詢?
MySQL對任何關聯都執行嵌套循環(Block Nested Loop)關聯操作,即MySQL先在一個表中循環取出單條數據,然后再嵌套循環到一個表中尋找匹配的行,依次下去直到找到的有匹配的行為止。然后根據各個表匹配的行,返回查詢中需要的各個列。
我們可以看到:兩表關聯,在沒有排序的情況下,是不會使用內部臨時表的;
但是,當我們加上排序字段后,將會使用內部臨時表。
在關聯查詢的時候如果需要排序(有ORDER BY子句),MySQL都會將關聯的結果存放在一個臨時表中,然后在所有的關聯都結束后,再進行文件排序。
這種情況下Extra字段可以看到Using temporary;Using filesort;
如果查詢中有LIMIT的話,LIMIT也會在排序之后應用,所以即使需要返回較少的數據,臨時表和需要排序的數據量仍然會非常大。
MySQL的執行計划是一棵左側深度優先的樹。
不過,如果有超過n個表的關聯,那么需要檢查n的階乘種關聯順序。我們稱之為所有可能的執行計划的“搜索空間”。實際上,當需要關聯的表超過optimizer_search_depth的限制的時候,就會選擇“貪婪”搜索模式。
無論如何排序都是一個成本很高的操作,所以從性能角度考慮,應盡可能避免排序或者盡可能避免對大量數據進行排序。如果需要排序的數據量小於排序緩沖區,MySQL使用內存進行“快速排序”操作。如果內存不夠排序,那么MySQL會先將數據分塊,對每個獨立的塊使用“快速排序”進行排序,並將各個塊的排序結果存放在磁盤上,然后將各個排序的塊進行合並,最后返回排序結果。
情形6:表連接+group by
沒加group by之前
加group by之后
情形6:group by 與 order by 不同列
我們可以看到,當group by與order by使用相同列時,不會使用內部臨時表;
但是,當group by與order by使用的列名不同時,就會觸發內部臨時表。
情形7:distinct 與 order by 不同列
由情形4,我們知道:
當distinct后面的列使用索引的話,將不會使用內部臨時表;
我們可以再次看到,當distinct走索引,且排序字段與distinct相同時,mysql是不會用內部臨時表的;
但是,當order by后面的字段與distinct 字段不相同時,將會使用內部臨時表。
情形8:使用union去重
先看union all(不去重)
不去重的情況下,不會使用內部臨時表;
但是,如果去重的話,將會使用內部臨時表。
.....(未完待續)