upay_csys_scquery_txn_log_his:150 個欄位,行長 ~2KB... WHERE status<>'00' AND settle_date=20151230 LIMIT 50000,10000Sending data 佔 99%MySQL 實現機制:**先掃描所有滿足 WHERE 的行,排序,再丟棄前 50000 行,取後 10000 行**。
掃描 6 萬行寬表 = 大量隨機回表讀取 150 個欄位 = IO 地獄。
核心原則: **只對主鍵分頁,再用主鍵回表取完整行**。
SELECT a1.`col1`...`col150`, a2.`trans_name` FROM `upay_csys_scquery_txn_log_his` a1 LEFT JOIN `upay_csys_trans_code` a2 ON a1.`int_trans_code`=a2.`trans_code` WHERE a1.`status`<>'00' AND a1.`settle_date`=20151230 LIMIT 50000,10000; -- 32 秒
SELECT a1.`col1`...`col150`, a2.`trans_name`
FROM `upay_csys_scquery_txn_log_his` a1
LEFT JOIN `upay_csys_trans_code` a2 ON a1.`int_trans_code`=a2.`trans_code`
WHERE a1.`seq_id` IN (
SELECT seq_id FROM (
SELECT `seq_id`
FROM `upay_csys_scquery_txn_log_his`
WHERE `status`<>'00' AND `settle_date`=20151230
ORDER BY 1 LIMIT 50000,10000
) AS t
); -- 3 秒,提速 10 倍!
INDEX(status, settle_date, seq_id)),**無回表**MySQL 不支援IN (SELECT ... LIMIT ...)直接嵌套,報錯ERROR 1235: This version doesn't support 'LIMIT & IN/ALL/ANY/SOME subquery'。
-- 錯誤寫法 WHERE seq_id IN (SELECT seq_id FROM tbl WHERE ... LIMIT 50000,10000); -- 正確寫法:多包一層派生表 WHERE seq_id IN (SELECT seq_id FROM (SELECT seq_id FROM tbl WHERE ... LIMIT 50000,10000) AS t);
EXPLAIN EXTENDED SELECT ...优化后SQL...; SHOW WARNINGS; -- 輸出的 Note 中可見優化器等價改寫後的 SQL,確認執行順序符合預期
| 指標 | 優化前 | 優化後 | 提升 |
|---|---|---|---|
| 執行時間 | 32 秒 | 3 秒 | 10.6x |
| 掃描行數 | ~60,000 行寬表 | ~60,000 行窄索引 + 10,000 行寬表 | IO 降序 90%+ |
| Sending data | 31.8 秒 | 2.9 秒 | 核心瓶頸消除 |
**延遲關聯** 是 MySQL 深分頁優化的經典模式:**主鍵分頁 → 回表取列**。配合覆蓋索引、雙層子查詢繞過限制,將寬表深分頁從「IO 地獄」拉回「毫秒級響應」。建議納入團隊《SQL 開發規範》:大表深分頁必用延遲關聯。