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/

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