首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

一條慢 SQL 導致全站宕機:從 70 秒大查詢到服務雪崩的根因分析與處置

     編輯:MySQL 資料庫相關知識

慢 SQL 全貌

# Query_time: 70.472013  Lock_time: 0.000078  Rows_sent: 7915203  Rows_examined: 15984089
SELECT ss_id, ss_sa_id, ss_si_id, ss_av_zid, ss_av_fid, ss_artno,
       ss_av_zvalue, ss_av_fvalue, ss_av_zpic, ss_av_fpic, ss_number,
       ss_sales, ss_cprice, ss_price, ss_stock, ss_orderid, ss_status,
       ss_add_time, ss_lastmodify
FROM js_sgoods_sku
WHERE ss_si_id = 0 AND ss_status > 0
ORDER BY ss_orderid DESC, ss_av_fid ASC;

單次查詢 70 秒,掃描 1600 萬行,返回 790 萬行,傳輸 1.2GB 數據。

執行計劃分析

EXPLAIN 结果:
type=ref, possible_keys=ss_si_id, key=ss_si_id, key_len=4, ref=const, rows=9516091, Extra=Using where; Using filesort

雖用到索引 ss_si_id (ss_si_id, ss_av_zid, ss_av_fid),但掃描行數極大,且需 filesort。

深入鑽取:統計信息與數據分布

索引統計信息

SHOW INDEX FROM js_sgoods_sku:
Key_name=ss_si_id, Column_name=ss_si_id, Cardinality=1,551,779
Key_name=PRIMARY, Column_name=ss_id, Cardinality=18,621,349

ss_si_id 理應近似主鍵基數(關聯 goods_info 主鍵),實際僅 155 萬,差異巨大。

ANALYZE TABLE 後無改善

重新收集統計信息,Cardinality 反而下降(採樣頁差異導致波動),確認統計信息非根因。

數據分布驚人真相

SELECT COUNT(*) FROM js_sgoods_sku WHERE ss_si_id = 0;
结果:7,994,788 / 19,048,617 = 41.97%

表中 42% 數據的 ss_si_id = 0(默認值),導致索引選擇性極低,掃描近半表數據。

為什麼導致全站宕機?

應用服務器症狀

  • CPU 長期 100%+
  • jstack 無法正常 dump,需 -F 強制
  • 所有線程處於 BLOCKED 狀態
  • FGC 極其頻繁(~10 秒一次)
  • 堆內存超過分配上限

鏈路推演

  1. 慢查詢返回 790 萬行 → 應用層嘗試載入 1.2GB 結果集到 JVM 堆
  2. 內存暴漲觸發頻繁 Full GC → CPU 飆升、線程阻塞
  3. 服務無響應 → 上游調用超時重試 → 連接池耗盡 → 雪崩
  4. DB 端並發連接數飆升、Load 從 0.x 飆至 50+ → DB 亦面臨崩潰風險

緊急處置與長期治理

即時止血

  1. 運維配合研發修改 SQL:WHERE ... AND ss_si_id <> 0,執行計劃層面過濾默認值
  2. DBA 使用 pt-kill 持續殺掉該慢查詢,保護 DB 存活

經驗總結

  1. 源頭治理:嚴禁查詢默認值字段(如 id=0, status=-1),業務層校驗攔截
  2. 結果集限制:強制 LIMIT、分頁查詢、禁止全量拉取,防止應用層 OOM
  3. 索引設計:低選擇性字段(如狀態、默認值佔比高)不宜建單列索引,考慮複合索引或分區
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved