1. 程式人生 > >mysql(二) 慢查詢分析(一)

mysql(二) 慢查詢分析(一)

engine big date 商品 key col 多表 pre 查詢

如下表結構:

CREATE TABLE `trade_order` (
  `order_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 訂單編號,
  `total_price` bigint(20) DEFAULT NULL COMMENT 訂單總價,
  `item_name` varchar(128) DEFAULT NULL COMMENT 商品名稱,
  `mobile` varchar(16) DEFAULT NULL COMMENT 下單電話,
  `gmt_create` timestamp NOT
NULL DEFAULT CURRENT_TIMESTAMP, `gmt_modify` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`order_id`) ) ENGINE=InnoDB AUTO_INCREMENT=239 DEFAULT CHARSET=utf8mb4;
CREATE TABLE `trade_sub_order` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `order_id` bigint(20) DEFAULT
NULL COMMENT 訂單號, `item_price` bigint(20) DEFAULT NULL COMMENT 商品單價, `item_nums` int(11) DEFAULT NULL COMMENT 商品數量, `gmt_create` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `gmt_modify` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `order_index` (`order_id`) ) ENGINE
=InnoDB AUTO_INCREMENT=1227 DEFAULT CHARSET=utf8mb4;

在執行聯合查詢時,如下2種寫法的執行計劃有明顯差別。

第一種:

select * from trade_order left join `trade_sub_order` using(`order_id`) order by `trade_order`.`order_id` desc;

第二種:

select * from trade_order left join `trade_sub_order` using(`order_id`) order by `trade_sub_order`.`order_id` desc;

兩者的差別在於order by的表字段不同。

看執行計劃:

第一種的執行計劃:

技術分享圖片

第二種的執行計劃:

技術分享圖片

rows的差別忽略,因為數據在不停的新增過程中。

第一種方式有主鍵索引,不需要使用臨時表。第二種全表掃描,使用臨時表,使用文件排序。

這裏面的關鍵問題在於,MySQL 表關聯的算法是“Nest Loop Join”,是通過驅動表的結果集作為循環基礎數據,然後一條一條地通過該結果集中的數據作為過濾條件到下一個表中查詢數據,然後再合並結果(臨時表)。

在explain中,第一行出現的表就是驅動表,對於驅動表字段的排序可以直接進行,對於非驅動表字段的排序需要對循環查詢的合並結果(臨時表)進行排序。所以在第二種寫法下就會出現Using temporary。而且由於對非驅動表字段排序,導致將驅動表的全表數據作為驅動表的結果集,產生全表掃描,無法使用到主鍵索引。

所以,在explain中,有Using temporary時,需要關註是否使用非驅動表字段做排序處理。

驅動表的定義:

在進行多表連接查詢時,

  • 指定了連接條件,滿足查詢查詢條件的記錄數少的表為驅動表;
  • 未指定連接條件,行數少的表為驅動表;

mysql(二) 慢查詢分析(一)