MySQL臨時表

来源:https://www.cnblogs.com/cchust/archive/2019/05/20/10891520.html
-Advertisement-
Play Games

概述 MySQL中臨時表主要有兩類,包括外部臨時表和內部臨時表。外部臨時表是通過語句create temporary table...創建的臨時表,臨時表只在本會話有效,會話斷開後,臨時表數據會自動清理。內部臨時表主要有兩類,一類是information_schema中臨時表,另一類是會話執行查詢時 ...


概述

MySQL中臨時表主要有兩類,包括外部臨時表和內部臨時表。外部臨時表是通過語句create temporary table...創建的臨時表,臨時表只在本會話有效,會話斷開後,臨時表數據會自動清理。內部臨時表主要有兩類,一類是information_schema中臨時表,另一類是會話執行查詢時,如果執行計劃中包含有“Using temporary”時,會產生臨時表。內部臨時表與外部臨時表的一個區別在於,我們看不到內部臨時表的表結構定義文件frm。而外部臨時表的表定義文件frm,一般是以#sql{進程id}_{線程id}_序列號組成,因此不同會話可以創建同名的臨時表。

臨時表

臨時表與普通表的主要區別在於是否在實例,會話,或語句結束後,自動清理數據。比如,內部臨時表,我們在一個查詢中,如果要存儲中間結果集,而查詢結束後,臨時表就會自動回收,不會影響用戶表結構和數據。另外就是,不同會話的臨時表可以重名,所有多個會話執行查詢時,如果要使用臨時表,不會有重名的擔憂。5.7引入了臨時表空間後,所有臨時表都存儲在臨時表空間(非壓縮)中,臨時表空間的數據可以復用。臨時表並非只支持Innodb引擎,還支持myisam引擎,memory引擎等。因此,臨時表我們看不到實體(idb文件),但其實不一定是記憶體表,也可能存儲在臨時表空間中。

臨時表 VS 記憶體表

臨時表既可以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
場景1:union

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 by

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).消除filesort

實際上,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 |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-----------------+
可以看到,語句中加上“order by null”後,執行計劃中,不再出現“Using filesort”。

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 | +----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+----------------+

可以看到執行計劃中已經沒有了“Using temporary”,所以group by並非一定依賴臨時表,臨時表在group by中的作用主要是“去重”。所以,實際上有另外一種方式,不使用臨時表,直接利用sort_buffer排序(sort_buffer不夠時,進行文件排序,具體而言是每一個有序數組作為一個單獨文件,然後進行外排歸併),然後再掃描得到聚合後的結果集。

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 | +----------+---------------+--------+-------+----------------------+---------------+

總結

本文詳細介紹了MySQL中臨時表的核心特征,按需創建並且自動銷毀,對於純記憶體的數據特別適合,但為了避免記憶體不可控,實際上不僅僅有記憶體臨時表,還有磁碟臨時表。臨時表和記憶體表本沒有直接關聯,因為臨時表既可以是memory引擎,又可以innodb引擎將兩者聯繫到了一起,實際上不同類別的臨時表也是用到了不同引擎的優勢。臨時表使用的典型場景是union和group by。為了消除臨時表,我們需要對group by列添加索引,或者對於大結果集,使用SQL_BIG_RESULT等。最後本文介紹了臨時表相關的參數和狀態變數,以及information_schema中的臨時表信息。
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.基礎知識點 (1)Rewirte規則也稱為 規則重寫,主要功能是實現瀏覽器訪問HTTP URL的跳轉,其正則表達式是基於Perl語言。 (2)對收縮引擎優化(SEO),利於收索引擎抓取網站頁面。 (3)隱藏網站URL真實地址。 (4)網站變更升級,可以基於Rewrite臨時重定向到其他頁面。 ( ...
  • 回到目錄 除了上面的伏安特性曲線以外,對於二極體,你還需要知道兩個特性:二極體電容和反向恢復時間。這兩個特性掌握了之後,那對於通常的二極體來說,你該知道的基本上就算都知道了。 1. 二極體電容 如果你一輩子只做低頻領域,那可以不管二極體電容。但那幾乎是不可能的,隨著現在電子電路和MCU晶元的主頻越來 ...
  • 本文講述今天發生的一起黑客入侵事件,網路紅客與黑客攻防對戰。作者帶您一步步揭開黑客攻擊計算系統的內幕,也講述網路紅客如何絕地反擊。 ...
  • 大家好,我是痞子衡,是正經搞技術的痞子。 從2019年6月份開始我將為大家帶來《痞子衡嵌入式半月刊》,搜羅與整理對嵌入式開發有用的信息: "《痞子衡嵌入式半月刊》 開刊序言" "《痞子衡嵌入式半月刊》 第001期" ...
  • 1.在騰訊雲上註冊功能變數名稱 然後,功能變數名稱實名認證 2.Vultr主機購買(看我之前寫的:手把手教你如何自己搭梯子) 然後會獲取到一個伺服器IP地址 3.綁定功能變數名稱與IP 點解,解析後會彈出讓你輸入IP地址,此時輸入2中獲取的IP就可以綁定 綁定後: 4.然後你就可以正常訪問這個網址了 ...
  • 如下麵的代碼所示: 更性的unix密碼就是root的密碼,然後我們可以切換到root用戶進行登陸,輸入su root來到root的用戶界面: 登陸成功,得解。命令行之後的$變成了#這樣就切換回root目錄啦! ...
  • 首先我們來瞭解下Linux下用戶管理的概念: 如上圖所示,左邊的一列表示用戶名,中間的一列表示用戶組,最右邊的一列表示的是家目錄。用戶名我們這裡處於簡單就,添加了root,xm,xh三個用戶。用戶組和家目錄則是Linux設計者為了方便大家使用從而創建的概念,每一個用戶都必須歸屬於一個組,一個用戶可以 ...
  • 數字輔助表: DECLARE @start_digital INT = 0, @end_digital INT = 9 ;WITH Digital_Rangs(Digital) AS ( SELECT @start_digital UNION ALL SELECT [Digital] = [Digi ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...