mysql 臨時表


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(不去重)

不去重的情況下,不會使用內部臨時表;

但是,如果去重的話,將會使用內部臨時表。

 

.....(未完待續)

寫在最后

  哪位大佬如若發現文章存在紕漏之處或需要補充更多內容,歡迎留言!!!

 相關推薦:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM