排序sql升級5.6變慢原因分析
背景:
某業務DB從5.5升級5.6後,同一sql執行時間由毫秒級飆升到了20秒,sql文字如下
摸索過程:
表上索引分佈
PRIMARY KEY (`id`),
KEY `idx_xx_id` (`xx_id`,`plan_type`,`user_id`),
……
KEY `idx_gmt_create` (`limit_type`,`xx_id`,`gmt_create`)
該sql選擇索引idx_gmt_create,因其符合最左字首策略,故排序沒有使用到filesort,其訪問路徑大致如下:
1 反向掃描idx_gmt_create葉子結點,搜尋(limit_type,xx_id)=(10,25773208367)的元組;
2 回表,驗證該元組對應的主鍵記錄是否滿足約束(plan_type,user_id)=(1 or 3, 133174222100),滿足則計數加1否則丟棄,繼續掃描下一個元組;
3 當計數達到10時,停止掃描,將對應的10條記錄返回給客戶端;
作為複合索引的引導列,limit_type欄位的選擇性驚人的低,這是查詢變慢的主要原因之一。
之所以要強調”之一”,是因為針對本例,只要其他欄位足夠給力,即便limit_type=10也能很快執行完畢,
檢視xx_id的分佈情況,也是比較畸形,該sql又很不幸的選擇了候選行最多的那個
如果xx_id=1,該sql最多隻需要比較2100條記錄即可返回,會很快執行完畢;
即便xx= 25773208367,如果能快速找出滿足非索引欄位約束的主鍵記錄,sql也會很快執行完畢,mysql是在驗證了海量的(limit_type,xx_id)=(10,25773208367)元組後,才湊齊10條同時滿足(plan_type,user_id)約束的主鍵記錄,據此我們可以反推出最早滿足所有約束條件的user_id,其查詢邏輯如下
隨便挑一個替換25773208367,比如1851362558,執行計劃沒有變,原本需要執行20多秒的sql卻在200毫秒內執行完畢。
而5.5版本的執行計劃為
雖然採用了filesort,但是經索引idx_xx_id過濾後的候選行非常少,故執行時間很短。
解決方案
1 修改sql,新增force index (idx_xx_id),此方案不夠靈活;
2 修改sql,將排序欄位從gmt_create改為gmt_modified,因無法採用索引排序5.6會選擇idx_xx_id,此方案可能造成返回資料有誤;
3 修改sql,將t.limit_TYPE in (10)改為t.limit_TYPE >9 and t.limit_TYPE <11,最佳化器會認為sql沒有滿足索引最左字首便不再使用idx_gmt_create,這招似乎有點賤,同時說明mysql最佳化器還不夠智慧。
結束語
5.6最佳化器做了大量改進,以本sql為例,讓其選擇了idx_gmt_create從而省去了filesort,之所以執行變慢了是因為表欄位資料分佈太不均勻,而本sql又湊巧滿足了各種坑,這算是一個意外吧。
某業務DB從5.5升級5.6後,同一sql執行時間由毫秒級飆升到了20秒,sql文字如下
點選(此處)摺疊或開啟
-
select * from big_table as t
-
where ( t.plan_TYPE = 1 or t.plan_TYPE=3 )
-
and t.limit_TYPE in (10)
-
and t.xx_ID = 25773208367
-
and t.USER_ID in (133174222100)
- plan by t.gmt_create desc , t.ID desc limit 1,10
摸索過程:
點選(此處)摺疊或開啟
-
檢視當前執行計劃
-
*************************** 1. row ***************************
-
id: 1
-
select_type: SIMPLE
-
table: t
-
type: range
-
possible_keys: idx_xx_id,idx_gmt_create
-
key: idx_gmt_create
-
key_len: 17
-
ref: NULL
-
rows: 6816016
-
Extra: Using index condition; Using where
- 1 row in set (0.00 sec)
表上索引分佈
PRIMARY KEY (`id`),
KEY `idx_xx_id` (`xx_id`,`plan_type`,`user_id`),
……
KEY `idx_gmt_create` (`limit_type`,`xx_id`,`gmt_create`)
該sql選擇索引idx_gmt_create,因其符合最左字首策略,故排序沒有使用到filesort,其訪問路徑大致如下:
1 反向掃描idx_gmt_create葉子結點,搜尋(limit_type,xx_id)=(10,25773208367)的元組;
2 回表,驗證該元組對應的主鍵記錄是否滿足約束(plan_type,user_id)=(1 or 3, 133174222100),滿足則計數加1否則丟棄,繼續掃描下一個元組;
3 當計數達到10時,停止掃描,將對應的10條記錄返回給客戶端;
點選(此處)摺疊或開啟
-
root@ 03:20:56>select limit_type,count(*) from big_table group by limit_type;
-
+-------------+----------+
-
| limit_type | count(*) |
-
+-------------+----------+
-
| NULL | 226865 |
-
| 9 | 463346 |
-
| 10 | 13353116 |
-
+-------------+----------+
- 3 rows in set (3.13 sec)
作為複合索引的引導列,limit_type欄位的選擇性驚人的低,這是查詢變慢的主要原因之一。
之所以要強調”之一”,是因為針對本例,只要其他欄位足夠給力,即便limit_type=10也能很快執行完畢,
檢視xx_id的分佈情況,也是比較畸形,該sql又很不幸的選擇了候選行最多的那個
點選(此處)摺疊或開啟
-
root@ 04:01:12>select xx_id,count(*) from big_table where limit_type =10 group by xx_id order by xx_id desc;
-
+-------------+----------+
-
| xx_id | count(*) |
-
+-------------+----------+
-
| 25773208367 | 13352433 |
-
| 25770261347 | 2 |
-
| 258809681 | 148 |
-
| 1 | 2100 |
-
+-------------+----------+
- 4 rows in set (5.79 sec)
如果xx_id=1,該sql最多隻需要比較2100條記錄即可返回,會很快執行完畢;
即便xx= 25773208367,如果能快速找出滿足非索引欄位約束的主鍵記錄,sql也會很快執行完畢,mysql是在驗證了海量的(limit_type,xx_id)=(10,25773208367)元組後,才湊齊10條同時滿足(plan_type,user_id)約束的主鍵記錄,據此我們可以反推出最早滿足所有約束條件的user_id,其查詢邏輯如下
點選(此處)摺疊或開啟
-
select user_id,count(*) from big_table t
-
where limit_type =10 and xx_id =25773208367
-
and ( t.plan_TYPE = 1 or t.plan_TYPE=3 )
-
group by user_id having count(*)>=10 plan by gmt_create desc limit 1,5;
-
執行結果
-
+------------+----------+
-
| user_id | count(*) |
-
+------------+----------+
-
| 1851362558 | 15 |
-
| 2118141658 | 11 |
-
| 2641244918 | 14 |
-
| 2448823838 | 17 |
-
| 16375410 | 32 |
-
+------------+----------+
- 5 rows in set (1 min 12.42 sec)
隨便挑一個替換25773208367,比如1851362558,執行計劃沒有變,原本需要執行20多秒的sql卻在200毫秒內執行完畢。
而5.5版本的執行計劃為
點選(此處)摺疊或開啟
-
*************************** 1. row ***************************
-
id: 1
-
select_type: SIMPLE
-
table: t
-
type: range
-
possible_keys: idx_xx_id
-
key: idx_xx_id
-
key_len: 18
-
ref: NULL
-
rows: 116
- Extra: Using where; Using filesort
雖然採用了filesort,但是經索引idx_xx_id過濾後的候選行非常少,故執行時間很短。
解決方案
1 修改sql,新增force index (idx_xx_id),此方案不夠靈活;
2 修改sql,將排序欄位從gmt_create改為gmt_modified,因無法採用索引排序5.6會選擇idx_xx_id,此方案可能造成返回資料有誤;
3 修改sql,將t.limit_TYPE in (10)改為t.limit_TYPE >9 and t.limit_TYPE <11,最佳化器會認為sql沒有滿足索引最左字首便不再使用idx_gmt_create,這招似乎有點賤,同時說明mysql最佳化器還不夠智慧。
結束語
5.6最佳化器做了大量改進,以本sql為例,讓其選擇了idx_gmt_create從而省去了filesort,之所以執行變慢了是因為表欄位資料分佈太不均勻,而本sql又湊巧滿足了各種坑,這算是一個意外吧。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/15480802/viewspace-1815984/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- MySQL升級會變慢?MySql
- MySQL:5.6 升級 5.7MySql
- 搬瓦工香港VPS主機變慢的原因分析
- 資料庫——慢sql的原因資料庫SQL
- 如何使用效能分析工具定位SQL執行慢的原因?SQL
- php5.6 升級至7.2.7PHP
- POSTGRESQL 小版本升級失敗後的原因分析SQL
- Weblogic 補丁升級慢Web
- centos7 mysql5.6升級5.7CentOSMySql
- Mysql慢SQL分析及優化MySql優化
- 升級win10 1903系統開機速度變慢如何解決Win10
- sql語句執行緩慢分析SQL
- 升級check SQL01SQL
- 【資料庫】mysql5.6升級至5.7(物理方式)資料庫MySql
- mysql 5.6 升級 到 5.7 的二進位制升級方法 另一個簡便思路.MySql
- MySQL information_schema.columns表查詢慢原因分析MySqlORM
- 網站速度慢,網站速度慢,網站速度慢的幾種原因分析網站
- 給國產資料庫廠商提個建議:把慢SQL監控升級為關鍵SQL管理資料庫SQL
- [20181119]sql語句執行緩慢分析.txtSQL
- 【SQL】Oracle 歸檔日誌暴增原因分析SQLOracle
- 搜尋EE場景排序鏈路升級排序
- 記一次,因表變數導致SQL執行效率變慢變數SQL
- 13 使用SQL Apply 升級資料庫SQLAPP資料庫
- 代理IP速度變慢的原因是什麼,要如何解決?
- 記一次開啟資料庫慢原因分析過程資料庫
- 生產系統 SQL 執行異常原因分析SQL
- 遠端升級頻頻失敗?原因竟然是…
- node版本升級npm命令警告原因及解決方法NPM
- latitude 7280升級win10鍵盤反應慢怎麼辦_latitude 7280升級win10鍵盤反應慢的解決方法Win10
- mysql5.6生成排序欄位MySql排序
- java 自動升級sql指令碼 flyway 工具JavaSQL指令碼
- Mac 下 Docker 執行較慢的原因分析及個人見解MacDocker
- mysql伺服器查詢慢原因分析與解決方法小結MySql伺服器
- 技術分享 | 如何優雅地在 Windows 上從 MySQL 5.6 升級到 5.7WindowsMySql
- HighgoDB查詢慢SQL和阻塞SQLGoSQL
- 慢Sql優化思路SQL優化
- 生產內網ssh登陸變慢問題原因及解決辦法內網
- flyway實現java 自動升級SQL指令碼JavaSQL指令碼
- MySQL5.6升級5.7時,出現主從延遲問題排查過程MySql