上週幫同事排查生產環境訂單系統的卡頓問題,後臺慢查詢日誌裡飄著幾十條耗時超10秒的SQL,奇怪的是相關欄位早就加了索引,怎麼還會慢?查了半天才發現,複合索引建反了順序,再加一個隱式型別轉換,直接把索引的作用消耗殆盡,調整完之後查詢耗時直接降到0.01秒。這就是索引最佳化的魔力:用不對的索引不如不加,用對了才能真正把效能拉滿。
## 踩過最多的坑:那些自以為是的”正確索引”
很多新手開發者對索引的認知還停留在”給查詢欄位加索引就會快”,實際上亂加索引不僅不會提升效能,反而會拖慢整個資料庫的寫入效率——因為每次增刪改都要同步維護所有索引,索引越多,寫入開銷越大。
最常見的錯誤就是複合索引的順序搞反,違背了最左字首匹配原則。舉個例子,一張100萬條資料的使用者表,你經常要執行`select * from user where city=’杭州’ and age=28`的查詢,如果把age放在複合索引的第一位,建`index(age,city)`,這個索引的效率會大打折扣。原因很簡單:索引的排序要把選擇性高的欄位放前面,city有幾百種不同取值,age只有1-100的區間,把city放前面能快速過濾掉絕大多數不符合條件的資料,反過來的話,索引只能用到age的部分,後續的city條件無法利用索引過濾,本質上還是做了大範圍掃描。
還有人喜歡給所有欄位都加獨立索引,覺得總能用到,實際上MySQL一次查詢只能用到一個索引,多個獨立索引不僅會讓最佳化器選錯索引,還會佔用大量磁碟空間,一張核心表的索引最好不要超過5個,過度索引的危害遠大於少加一個索引。
## 最容易忽略的隱形殺手:那些會毀掉索引的操作
很多時候你明明建對了索引,查詢還是慢,大機率是踩了索引失效的坑,這幾個場景是線上問題的重災區:
第一是隱式型別轉換,比如varchar型別的手機號欄位,查詢時寫`where phone=13800001111`,把字串型別的手機號傳成了數字,MySQL會自動把表中所有phone欄位轉成數字做比較,直接導致全表掃描,索引完全沒用。
第二是在索引列上用函式或運算,比如`where year(create_time)=2024`,哪怕create_time加了索引,只要加了函式就會失效,正確的寫法是`create_time between ‘2024-01-01’ and ‘2024-12-31’`。
第三是前導模糊查詢,`like ‘%北京%’`這種寫法不會用到索引,只有字尾模糊`like ‘北京%’`才能利用索引的排序特性。
## 拿來就能用的索引最佳化落地技巧
說了這麼多坑,給大家分享幾個能直接落地的最佳化方法,親測能解決90%的線上索引問題:
第一是善用EXPLAIN工具分析SQL,執行SQL前加EXPLAIN,重點看type列,如果是ALL就是全表掃描,ref、range才是正常用到索引的狀態;再看Extra列,如果出現`Using filesort`或者`Using temporary`,說明你的索引沒有支撐排序或分組,需要調整。
第二是用覆蓋索引減少回表開銷,如果你經常要查`select nickname,phone from user where id=1001`,可以建`index(id,nickname,phone)`的複合索引,查詢需要的所有資料都能從索引裡拿到,不需要回聚簇索引裡查,速度能提升好幾倍。
第三是定期清理無效索引,MySQL5.7之後可以透過performance_schema統計索引的使用率,把從來沒被呼叫過的無效索引刪掉,既省空間又提升寫入效能,很多迭代了好幾年的老專案,都能清出一半以上的無用索引。
## 總結與行動號召
索引不是提升效能的萬能藥,卻是MySQL效能最佳化的核心抓手,核心原則記住:索引不在多而在精,建對順序、避開失效坑、定期維護,才能讓索引真正發揮作用。
今晚抽10分鐘,登入你負責的測試環境資料庫,拉3條最近的慢查詢,用EXPLAIN跑一遍,看看你的索引是不是真的生效了,再清掉兩三個沒人用的舊索引,你的資料庫效能就能悄悄往上跳一個檔。






