標籤:
http://mysql.taobao.org/monthly/2016/06/07/#rdMySQL中的兩種暫存資料表外部暫存資料表
通過CREATE TEMPORARY TABLE 建立的暫存資料表,這種暫存資料表稱為外部暫存資料表。這種暫存資料表只對目前使用者可見,當前會話結束的時候,該暫存資料表會自動關閉。這種暫存資料表的命名與非暫存資料表可以同名(同名後非暫存資料表將對當前會話不可見,直到暫存資料表被刪除)。
內部暫存資料表
內部暫存資料表是一種特殊輕量級的暫存資料表,用來進行效能最佳化。這種暫存資料表會被MySQL自動建立並用來儲存某些操作的中間結果。這些操作可能包括在最佳化階段或者執行階段。這種內部表對使用者來說是不可見的,但是通過EXPLAIN或者SHOW STATUS可以查看MYSQL是否使用了內部暫存資料表用來協助完成某個操作。內部暫存資料表在SQL語句的最佳化過程中扮演著非常重要的角色, MySQL中的很多操作都要依賴於內部暫存資料表來進行最佳化。但是使用內部暫存資料表需要建立表以及中間資料的存取代價,所以使用者在寫SQL語句的時候應該盡量的去避免使用暫存資料表。
內部暫存資料表有兩種類型:一種是HEAP暫存資料表,這種暫存資料表的所有資料都會存在記憶體中,對於這種表的操作不需要IO操作。另一種是OnDisk暫存資料表,顧名思義,這種暫存資料表會將資料存放區在磁碟上。OnDisk暫存資料表用來處理中間結果比較大的操作。如果HEAP暫存資料表儲存的資料大於MAX_HEAP_TABLE_SIZE(詳情請參考MySQL手冊中系統變數部分),HEAP暫存資料表將會被自動轉換成OnDisk暫存資料表。OnDisk暫存資料表在5.7中可以通過INTERNAL_TMP_DISK_STORAGE_ENGINE系統變數選擇使用MyISAM引擎或者InnoDB引擎。
本篇文章主要介紹哪些操作可能會利用到內部暫存資料表。如果使用者在書寫SQL語句的時候能夠盡量少的使用內部暫存資料表進行查詢最佳化,將有效提高查詢執行的效率。
首先我們定義一個表t1,
CREATE TABLE t1( a int, b int); INSERT INTO t1 VALUES(1,2),(3,4);
下面所有的操作都是基於表t1進行舉例的。
- 在SQL語句中使用SQL_BUFFER_RESULT hint
SQL_BUFFER_RESULT主要用來讓MySQL儘早的釋放表上的鎖。因為如果資料量很大的話,需要較長時間將資料發送到用戶端,通過將資料緩衝到暫存資料表中可以有效減少讀鎖對錶的佔用時間。
例如:
mysql> explain format=json select SQL_BUFFER_RESULT * from t1;EXPLAIN{ "query_block": {"select_id": 1,"cost_info": { "query_cost": "2.00"},"buffer_result": { "using_temporary_table": true, "table": {"table_name": "t1","access_type": "ALL",...
- 如果SQL語句中包含了DERIVED_TABLE。
在5.7中,由於採用了新的最佳化方式,我們需要使用 set optimizer_switch=’derived_merge=off’來禁止derived table合并到外層的Query中。
例如:
mysql> explain format=json select * from (select * from t1) as tt;EXPLAIN{ "query_block": {"select_id": 1,"cost_info": { "query_cost": "2.40"},"table": { "table_name": "tt", "access_type": "ALL", ... "materialized_from_subquery": {"using_temporary_table": true,...
- 如果我們查詢系統資料表的話,系統資料表的資料將被儲存到內部暫存資料表中。
我們當前不能使用EXPLAIN來查看是否讀取系統資料表資料需要利用到內部暫存資料表,但是可以通過SHOW STATUS來查看是否利用到了內部暫存資料表。
例如:
mysql> select * from information_schema.character_sets;mysql> show status like ‘CREATE%‘;
- 如果DISTINCT語句沒有被最佳化掉,即DISTINCT語句被最佳化轉換為GROUP BY操作或者利用UNIQUE INDEX消除DISTINCT, 內部暫存資料表將會被使用。
mysql> explain format=json select distinct a from t1;EXPLAIN{{ "query_block": {"select_id": 1,"cost_info": { "query_cost": "1.60"},"duplicates_removal": { "using_temporary_table": true,...
- 如果查詢帶有ORDER BY語句,並且不能被最佳化掉。下面幾種情況會利用到內部暫存資料表緩衝中間資料,然後對中間資料進行排序。
1)如果串連表使用BNL(Batched Nestloop)/BKA(Batched Key Access)
例如:
1))BNL預設是開啟的
mysql> explain format=json select * from t1, t1 as t2 order by t1.a;EXPLAIN{ "query_block": { "select_id": 1, "cost_info": {"query_cost": "22.00" }, "ordering_operation": {"using_temporary_table": true, ...
2))關掉BNL後,ORDER BY將直接使用filesort。
mysql> set optimizer_switch=‘block_nested_loop=off‘;Query OK, 0 rows affected (0.00 sec)mysql> explain format=json select * from t1, t1 as t2 order by t1.a;EXPLAIN{ "query_block": {"select_id": 1,"cost_info": { "query_cost": "25.00"},"ordering_operation": { "using_filesort": true,...
2)ORDER BY的列不屬於執行計畫中第一個串連表的列。
例如:
mysql> explain format=json select * from t as t1, t as t2 order by t2.a;EXPLAIN{ "query_block": {"select_id": 1,"cost_info": { "query_cost": "25.00"},"ordering_operation": { "using_temporary_table": true,...
3)如果ORDER BY的運算式是個複雜運算式。
那麼什麼樣的ORDER BY運算式,MySQL認為是複雜運算式呢?
1))如果排序運算式是SP或者UDF。
例如:
drop function if exists func1;delimiter |create function func1(x int)returns int deterministicbegindeclare z1, z2 int;set z1 = x;set z2 = z1+2;return z2;end|delimiter ;explain format=json select * from t1 order by func1(a);{"query_block": {"select_id": 1,"cost_info": { "query_cost": "2.20"},"ordering_operation": { "using_temporary_table": true,...
2))ORDER BY的列包含聚集合函式
為了簡化執行計畫,我們利用INDEX來最佳化GROUP BY語句。
例如:
create index idx1 on t1(a); explain format=json SELECt a FROM t1 group by a order by sum(a); | { "query_block": {"select_id": 1,"cost_info": { "query_cost": "1.20"},"ordering_operation": { "using_temporary_table": true, "using_filesort": true, "grouping_operation": {"using_filesort": false,... drop index idx1 on t1;
3))ORDER BY的列中包含有SCALAR SUBQUERY,當然該SCALAR SUBQUERY沒有被最佳化掉。
例如:
explain format=json select (select rand() from t1 limit 1) as a from t1 order by a;| { "query_block": {"select_id": 1,"cost_info": { "query_cost": "1.20"},"ordering_operation": { "using_temporary_table": true, "using_filesort": true,...
4) 如果查詢既帶有ORDER BY同時也有GROUP BY語句,但是兩個語句使用的列不相同。
注意: 如果是5.7,我們需要將sql_mode設定為非only_full_group_by模式,否則會報錯。
同樣為了簡化執行計畫,我們利用INDEX來最佳化GROUP BY語句。
例如:
set sql_mode=‘‘;create index idx1 on t1(b);explain format=json select t1.a from t1 group by t1.b order by 1;| { "query_block": {"select_id": 1,"cost_info": { "query_cost": "1.40"},"ordering_operation": { "using_temporary_table": true, "using_filesort": true, "grouping_operation": {"using_filesort": false,...drop index idx1 on t1;
- 如果查詢帶有GROUP BY語句,並且不能被最佳化掉。下面幾種情況會利用到內部暫存資料表緩衝中間資料,然後對中間資料進行GROUP BY。
1)如果串連表使用BNL(Batched Nestloop)/BKA(Batched Key Access)。
例如:
explain format=json select t2.a from t1, t1 as t2 group by t1.a;| {"query_block": {"select_id": 1,"cost_info": { "query_cost": "8.20"},"grouping_operation": { "using_temporary_table": true, "using_filesort": true, "cost_info": {"sort_cost": "4.00"...
2) 如果GROUP BY的列不屬於執行計畫中的第一個串連表。
例如:
explain format=json select t2.a from t1, t1 as t2 group by t2.a;| {"query_block": {"select_id": 1,"cost_info": { "query_cost": "8.20"},"grouping_operation": { "using_temporary_table": true, "using_filesort": true, "nested_loop": [...
3) 如果GROUP BY語句使用的列與ORDER BY語句使用的列不同。
例如:
set sql_mode=‘‘;explain format=json select t1.a from t1 group by t1.b order by t1.a;| { "query_block": {"select_id": 1,"cost_info": { "query_cost": "1.40"},"ordering_operation": { "using_filesort": true, "grouping_operation": {"using_temporary_table": true,"using_filesort": false,...
4) 如果GROUP BY帶有ROLLUP並且是基於多表外串連。
例如:
explain format=json select sum(t1.a) from t1 left join t1 as t2 on true group by t1.a with rollup;| {"query_block": {"select_id": 1,"cost_info": { "query_cost": "7.20"},"grouping_operation": { "using_temporary_table": true, "using_filesort": true, "cost_info": {"sort_cost": "4.00" },...
5) 如果GROUP BY語句使用的列來自於SCALAR SUBQUERY,並且沒有被最佳化掉。
例如:
explain format=json select (select avg(a) from t1) as a from t1 group by a;| {"query_block": {"select_id": 1,"cost_info": { "query_cost": "3.40"},"grouping_operation": { "using_temporary_table": true, "using_filesort": true, "cost_info": {"sort_cost": "2.00" },...
- IN運算式轉換為semi-join進行最佳化
1) 如果semi-join執行方式為Materialization
例如:
set optimizer_switch=‘firstmatch=off,duplicateweedout=off‘;explain format=json select * from t1 where a in (select b from t1);| {"query_block": {"select_id": 1,"cost_info": { "query_cost": "5.60"},"nested_loop": [ { "rows_examined_per_scan": 1, "materialized_from_subquery": {"using_temporary_table": true,"query_block": { "table": {"table_name": "t1","access_type": "ALL",...
2) 如果semi-join執行方式為Duplicate Weedout
例如:
set optimizer_switch=‘firstmatch=off‘;explain format=json select * from t1 where a in (select b from t1); | {"query_block": {"select_id": 1,"cost_info": { "query_cost": "4.80"},"duplicates_removal": { "using_temporary_table": true, "nested_loop": [{...
- 如果查詢語句帶有UNION,MySQL將利用內部暫存資料表協助UNION操作消除重複。
例如:
explain format=json select * from t1 union select * from t1;| {"query_block": {"union_result": { "using_temporary_table": true, "table_name": "<union1,2>",...
- 如果查詢語句使用多表更新。
這裡Explain不能看到內部暫存資料表被利用,所以需要查看status。
例如:
update t1, t1 as t2 set t1.a=3;show status like ‘CREATE%‘;
- 如果聚集合函式中包含如下函數,內部暫存資料表也會被利用。
1) count(distinct *)例如:explain format=json select count(distinct a) from t1;2) group_concat例如:explain format=json select group_concat(b) from t1;
總之,上面列出了10種情況,MySQL將利用內部暫存資料表進行中間結果緩衝,如果資料量比較大的話,內部暫存資料表將會把資料存放區在磁碟上,這樣顯然會對效能有所影響。為了儘可能的減少效能損失,我們需要盡量避免上述情況的出現。
MySQL · 特性分析 · 內部暫存資料表