1. 程式人生 > 其它 >生產慢查詢記錄:MySQL統計問題導致的慢查詢一則

生產慢查詢記錄:MySQL統計問題導致的慢查詢一則

技術標籤:資料庫

先說結論,變更頻繁的大表,應該定期使用 ANALYZE TABLE 命令分析優化表統計資訊。

正文:
收到一起bug反饋,生產環境突然查詢變慢,查詢消費資料需要近20秒才能有響應,
開發兄弟在測試環境使用同樣的程式碼版本,並把生產資料匯入測試環境,均無法重現。
v*n訪問生產環境的swagger介面驗證,也是秒級響應,無法重現。

第一反應,看有沒有慢查詢,發現沒有
注:這一步,獲取資訊有誤,開發給了錯誤的連線資訊,導致判斷沒有慢查詢。

幸好線上問題是可以復現的,先把生產的一臺伺服器下線,然後把請求打到下線的伺服器,
用jstack命令, 分別分析檢視無請求的堆疊、有慢查詢的堆疊:

jstack -l 程序id
經過對比,定位到是某個資料庫請求,但是jstack無法打印出具體的請求引數,
只能增加日誌,釋出,再請求,最終得到有問題的語句是:

SELECT * FROM Payment WHERE ParentPayId IN (
52969268,52752270,52132196,52132169,51784361,51784177,51782944,51781323,51781030,51266841
)

該sql扔到測試環境執行,都是秒回,
扔到生產資料庫執行,果然,全表掃描,執行了17秒才返回,
查詢計劃如下:在這裡插入圖片描述

會不會是沒有索引呢?或者 ParentPayId是VARCHAR,導致型別轉換,從而忽略索引呢?

看錶結構,定義是數值,並且有索引:

 TABLE `payment` (
  `Id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'Id',
...
  `ParentPayId` bigint(20) NOT NULL DEFAULT '0' COMMENT 'xxx',
...
  KEY `IdxParentPayId` (`ParentPayId`),

把id改少試了一下,發現in後面的id少於10個,就正常,多於10個就是全表掃描:
在這裡插入圖片描述

到這裡初步判斷是統計資訊問題了,用下面的sql,檢視統計資訊:
SHOW INDEX FROM payment WHERE key_name='IdxParentPayId'


在這裡插入圖片描述
上圖有個欄位 Cardinality,就是該索引的區分度,該數值越接近表記錄總數,表示區分度越好。
該值並不是實時更新的,詳細瞭解它請參考官方文件:
https://dev.mysql.com/doc/refman/8.0/en/analyze-table.html
我這個截圖是已經優化過的結果。

修復就簡單了,執行下述語句後,恢復正常:
ANALYZE TABLE payment
注:該語句在千萬級的表上,是秒級完成,但是,該命令會生成只讀表鎖,高峰期請謹慎執行。

到這裡,覺得有點奇怪,重新確認了下資料庫連線,發現慢查詢列表裡,明擺著有上面的問題語句,
溝通失誤,導致我還要去生產環境用jstack抓包……

補充一下:跟開發和運維瞭解了一下,該表確實更新頻繁,而且每天晚上還會大量刪除舊資料,所以統計資訊變動也是很頻繁的。

因此,也給運維加了2個任務:
1、定期對變動頻繁的表,進行索引優化,避免統計資訊錯誤。
2、引申問題,定期檢查表碎片,避免碎片太大,導致效能損耗,查看錶碎片的SQL如下:

-- 檢視MySql的每個表:記錄行數,資料大小,索引大小,自增欄位當前值,碎片(data_free)
SELECT t.table_schema, t.table_name, t.table_rows, t.avg_row_length, t.data_length , t.index_length, t.auto_increment 下一自增值, t.create_time, t.table_comment,t.data_free
FROM information_schema.tables t
WHERE t.table_type = 'base table' AND t.table_schema NOT IN ('performance_schema', 'mysql', 'information_schema', 'sys')
ORDER BY t.table_schema, t.table_name

注:碎片太大時,不建議進行碎片整理,效率太低,影響也比較長,
一般建議在低峰期,新建一個表,把舊錶資料插入新表,再進行rename,但是在rename前可能有增量資料,需要確認並補錄到新表裡(大表加索引,一般也採用這種方式)


最後吐槽一下百度,一開始不記得怎麼優化索引, 用百度搜索,告訴我要重建索引,千萬級的表,重建索引起碼是分鐘級操作,甚至更久。 用谷哥哥搜尋 mysql refresh index 得到了上面的 `ANALYZE TABLE `