關鍵原因分析
-
索引選擇性問題
- 如果
order_id
沒有索引,即使insert_time
有索引,優化器可能認為先通過order_id
過濾數據更高效。但由于order_id
無索引,只能全表掃描后過濾。 - 即使
insert_time
有索引,如果滿足insert_time >= '2024-02-26'
的數據量很大(如超過表總行數的 20%),優化器會認為全表掃描比回表更高效。
- 如果
-
缺乏復合索引
單列索引insert_time
僅能加速時間范圍過濾,但無法同時優化order_id
的等值查詢。若order_id
的過濾性(選擇性)高,優化器更傾向于使用復合索引(order_id, insert_time)
。 -
統計信息不準確
表的統計信息(如索引基數)過期,導致優化器誤判索引效果。可通過ANALYZE TABLE tb_oc_order;
更新統計信息。 -
隱式類型轉換或函數調用
如果transaction_id
的字符集或排序規則與查詢值不匹配,可能導致隱式轉換,從而無法使用索引(需檢查表結構)。
解決方案
-
為
transaction_id
添加索引
如果transaction_id
選擇性高(如唯一或接近唯一),優先為其單獨創建索引:ALTER TABLE tb_oc_order ADD INDEX idx_transaction_id (transaction_id);
-
創建復合索引
聯合索引(transaction_id, insert_time)
可以同時優化兩個條件:ALTER TABLE tb_oc_order ADD INDEX idx_trans_insert (transaction_id, insert_time);
- 優勢:先通過
transaction_id
快速定位少量行,再按insert_time
過濾,效率更高。
- 優勢:先通過
-
強制使用索引(謹慎使用)
通過FORCE INDEX
提示強制使用insert_time
索引(需確保其確實更優):EXPLAIN SELECT * FROM tb_oc_order FORCE INDEX (insert_time_index_name) WHERE transaction_id = '202502260333525000251000008' AND insert_time >= '2024-02-26 09:23:15';
-
檢查數據分布
執行以下查詢,確認滿足條件的數據比例:-- 滿足 insert_time 條件的數據占比 SELECT COUNT(*) / (SELECT COUNT(*) FROM tb_oc_order) AS ratio FROM tb_oc_order WHERE insert_time >= '2024-02-26 09:23:15';-- 滿足 transaction_id 條件的數據行數 SELECT COUNT(*) FROM tb_oc_order WHERE transaction_id = '202502260333525000251000008';
- 如果
transaction_id
匹配行數極少(如 1 行),但insert_time
范圍覆蓋大部分表,優化器可能優先全表掃描。
- 如果
優化后的執行計劃
創建復合索引后,執行計劃應類似以下結果(使用索引范圍掃描):
+----+-------------+-------------+------+---------------------+---------------------+---------+-------+------+-----------------------+
| id | select_type | table | type | key | key_len | ref | rows | Extra |
+----+-------------+-------------+------+---------------------+---------------------+---------+-------+------+-----------------------+
| 1 | SIMPLE | tb_oc_order | ref | idx_trans_insert | 772 (transaction_id)| const | 1 | Using index condition |
+----+-------------+-------------+------+---------------------+---------------------+---------+-------+------+-----------------------+