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

MySQL 大表 UPDATE 優化實戰:索引失效導致效能崩潰的根因分析與解決

     編輯:MySQL 資料庫相關知識

案例背景

  • 表結構: fuckSpeed,32 萬筆資料,唯一聯合索引 uin_id (uin, id)
  • 任務: 依 url 欄位匹配規則,更新新增欄位 type、typeid
  • 初始腳本: Shell 迴圈逐筆 SELECT ... WHERE id BETWEEN ... → 處理 → UPDATE ... WHERE id = ?
  • 災難結果: 平均 3-5 rows/sec,預估需 24+ 小時完成

根因定位:聯合索引最左前綴原則

-- 初始查詢
SELECT id, url FROM fuckSpeed WHERE id >= 101 AND id <= 200;
EXPLAIN 顯示:type=ALL, key=NULL, rows=324746 -- 全表掃描!

原因: 聯合索引 (uin, id) 僅在 WHERE 條件包含 uin(最左列)或 uin + id 時生效。單獨查 id 導致索引失效,退化為全表掃描。

-- 加上 uin 後
SELECT uin, id, url FROM fuckSpeed WHERE uin = 10023 AND id = 162;
EXPLAIN 顯示:type=ref, key=uin_id, rows=4, Extra=Using index -- 秒級!

解決方案:帶上最左列

  1. SELECT 時一併撈取 uin:SELECT uin, id, url ...
  2. UPDATE 時帶上 uin:UPDATE ... SET type=?, typeid=? WHERE uin=? AND id=?

優化後效能: 30+ rows/sec,總耗時從 24h 降至 3 小時內。

MySQL 語句級優化 11 條鐵律

#原則說明
1統計行數用獨立統計表InnoDB COUNT(*) 全表掃描,改用 MyISAM 統計表定期同步
2業務邏輯勿在 DB 做運算應用層計算完成再寫入,減少 CPU 壓力
3避免負向查詢與前導模糊NOT IN、!=、%keyword% 皆導致索引失效
4索引列上禁用函數/運算WHERE YEAR(dt) = 2023 → WHERE dt >= '2023-01-01' AND dt < '2024-01-01'
5禁用 SELECT *只取需要欄位,利於覆蓋索引
6善用 LIMIT 限制回傳行數減少網路傳輸與記憶體
7WHERE 列勿用函數同第 4 點,函數導致索引失效
8避免隱式類型轉換字串加引號、數字不加,否則索引失效
9SQL 關鍵字大寫減少解析快取分裂(大小寫不同視為不同 SQL)
10小結果集驅動大結果集JOIN 時小表在前,或用子查詢先縮小駕駛表
11開啟慢查詢日誌定期 EXPLAIN建立持續優化閉環

延伸思考:為何不直接在 id 上建單列索引?

  • 若 id 非唯一且選擇度低,單列索引效果有限
  • 若業務查詢模式固定為 uin + id,聯合索引已覆蓋,額外單列索引增加寫入開銷
  • 若確有單獨查 id 的高頻場景,可評估新增 idx_id (id),但必須經負載測試驗證

核心教訓: 索引設計必須對齊查詢模式。EXPLAIN 是驗證索引生效的唯一標準——沒有 EXPLAIN 的優化都是耍流氓。大批量 DML 前,務必先跑樣本驗證執行計畫,避免上線後發現「全表掃描」的災難。

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