首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> MySQL 資料庫 >> MySQL 綜合教學

一條慢 SQL 導致全站宕機的實戰復盤:從 Slow Log 到 pt-kill 緊急止血

     編輯:MySQL 綜合教學

前言

「慢查詢只是響應變慢」是危險的幻覺。本文復盤一起真實故障:一條 70s 慢查詢掃描 790 萬行,引發應用服務 OOM、Full GC 風暴、線程阻塞、數據庫負載飆升至 50+,最終導致全站不可用。完整記錄定位、止血、根因分析、長效治理全過程。

一、現場還原:Slow Log 第一現場

# Query_time: 70.472013  Lock_time: 0.000078
# Rows_sent: 7915203  Rows_examined: 15984089  Rows_affected: 0
# Bytes_sent: 1258414478
USE js_sku;
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;

二、執行計劃欺騙:EXPLAIN 顯示用了索引?

+----+-------------+---------------+------+---------------+----------+---------+-------+---------+-----------------------------+
| id | select_type | table         | type | possible_keys | key      | key_len | ref   | rows    | Extra                       |
+----+-------------+---------------+------+---------------+----------+---------+-------+---------+-----------------------------+
| 1  | SIMPLE      | js_sgoods_sku | ref  | ss_si_id      | ss_si_id | 4       | const | 9516091 | Using where; Using filesort |
+----+-------------+---------------+------+---------------+----------+---------+-------+---------+-----------------------------+

type=ref、key=ss_si_id 看似走索引,但 rows=951 萬、Using filesort 揭示真相:索引區分度極低,導致幾乎全表掃描 + 文件排序。

三、深挖根因:統計信息 vs 真實分佈

-- 索引統計信息
SHOW INDEX FROM js_sgoods_sku;
+---------------+------------+----------+--------------+-------------+-----------+-------------+
| Table         | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality |
+---------------+------------+----------+--------------+-------------+-----------+-------------+
| js_sgoods_sku | 1          | ss_si_id | 1            | ss_si_id    | A         | 1551779     |
+---------------+------------+----------+--------------+-------------+-----------+-------------+

-- 實際數據分佈
SELECT ss_si_id, COUNT(*) FROM js_sgoods_sku GROUP BY ss_si_id;
+----------+-----------+
| ss_si_id | COUNT(*)  |
+----------+-----------+
| 0        | 7994788   |  -- 佔 41.9%!
| ...      | ...       |
+----------+-----------+

結論:ss_si_id=0 是業務默認值,佔比近 42%,Cardinality 統計信息(155 萬)嚴重高估區分度,優化器誤判索引優於全表掃描。

四、連鎖反應:為什麼會「掛站」?

  1. 數據傳輸洪峰:單次查詢返回 790 萬行 × 1.2GB 網絡包,佔滿帶緩衝
  2. 應用層 OOM:Java 服務堆內存暴漲,FGC 每 10s 一次、線程全部 BLOCKED、CPU 100%
  3. 連接池耗盡:慢查詢佔用連接不釋放,新請求排隊超時
  4. DB 負載雪崩:並發慢查詢堆疊,Load 0.x → 50+,險些拖垮數據庫

五、緊急止血:雙管齊下

5.1 應用層邏輯攔截(治本)

// 伪代碼:Service 層攔截
if (param.siId == 0) {
    log.warn("Forbidden query: siId=0");
    return Collections.emptyList();  // 直接返回空,不打 DB
}

5.2 數據庫層 pt-kill 熔斷(治標)

# 幹掉運行超過 30s、查 js_sgoods_sku 表的查詢
pt-kill --host=localhost --user=root --password=xxx \
  --busy-time=30 --match-command=Query \
  --match-info="js_sgoods_sku" --kill --print --victims=all

效果:秒級清理殘留慢查詢,DB 負載從 50+ 降回 0.x,爭取修復窗口。

六、長效治理:從戰術到戰略

維度措施驗收標準
SQL 規範禁止查詢默認值/全表掃描;Code Review 強制 EXPLAIN慢查詢日誌零增長
結果集限制強制 LIMIT N;中間件攔截超大結果集單查詢返回 < 1000 行
索引重構針對高頻查詢建聯合索引 (ss_status, ss_si_id, ss_orderid, ss_av_fid) 覆蓋排序EXPLAIN rows < 1000
統計信息定時 ANALYZE TABLE;大表啟用 innodb_stats_persistent_sample_pages 增大採樣Cardinality 偏差 < 10%
熔斷機制pt-kill 守護進程 + ProxySQL 查詢規則限流故障影響時間 < 30s
壓測預案全鏈路壓測驗證「默認值查詢」抗壓能力QPS 2倍峰值無宕機

七、總結

慢查詢≠無害。大結果集 + 低區分度索引 + 無 LIMIT + 高併發 = 核彈。建立「慢查詢日審計 → 週度索引健康檢查 → 月度架構演進評審」機制,讓優化成為持續交付流程的一環,而非救火行動。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved