Sphinx 從 MySQL 拉取源數據建索引,記錄數僅幾萬,但查詢耗時 4-5 分鐘。EXPLAIN 顯示索引用得不錯,無全表掃描。
mysql> SHOW PROCESSLIST; +----+------+-----------+------+---------+------+--------------+------------------+ | Id | User | Host | db | Command | Time | State | Info | +----+------+-----------+------+---------+------+--------------+------------------+ | 12 | sphinx | 10.x.x.x | db | Query | 216 | Sending data | SELECT ... | +----+------+-----------+------+---------+------+--------------+------------------+
長時間停留在 Sending data。
非單純「發送數據」,而是「收集數據 + 發送數據」。
當查詢使用索引定位行後,若 SELECT 列包含**非索引列**,MySQL 需回表(回主鍵聚簇索引)逐行讀取完整行數據,再組裝發送。大量隨機 IO 在此發生。
SET profiling=1; -- 執行慢查詢 SHOW PROFILES; -- 獲取 Query_ID SHOW PROFILE FOR QUERY 1; +----------------------+----------+ | Status | Duration | +----------------------+----------+ | Sending data | 216.0000 | -- 核心瓶頸 | ... | ... | +----------------------+----------+
逐個排除 SELECT 列,發現 description VARCHAR(8000) 是元兇:
ROW_FORMAT=COMPACT/REDUNDANT 時,列長度 > 768 字節,前 768 字節存主頁,**餘下存溢出頁**SHOW TABLE STATUS 顯示 Avg_row_length ≈ 1.5KB,頁大小 16KB → 每頁約 8 行,**大量行觸發溢出頁**| 方案 | 實施成本 | 效果 | 適用場景 |
|---|---|---|---|
| 查詢去掉大字段 | 低(改應用 SQL) | 立竿見影 | 業務允許不返回大字段 |
| 垂直拆表:大字段移獨立表 | 中(改表結構+應用) | 徹底解決 | 業務仍需大字段,但查詢頻率低 |
| ROW_FORMAT=DYNAMIC/COMPRESSED | 低(ALTER TABLE) | 部分緩解 | MySQL 5.7+,配合 innodb_file_per_table |
| 覆蓋索引包含大字段 | 高(索引巨大) | 理論最快 | 極少見,不推薦 |
ROW_FORMAT=DYNAMIC(5.7+ 默認)「Sending data 慢 = 大量回表讀非索引列 + 可能涉及溢出頁隨機 IO」。**垂直拆表隔離大字段** 是治本之策;**業務端禁 SELECT *、按需查詢** 是防患未然的鐵律。