需求:從大表中隨機抽取 N 筆記錄。最直覺的 ORDER BY RAND() LIMIT N 在百萬級資料會全表掃描、產生臨時表、排序,效能極差。本文演進三種方案並分析優劣。
SELECT * FROM `table` ORDER BY RAND() LIMIT 1;
SELECT * FROM `table` AS t1
JOIN (SELECT ROUND(RAND() * (SELECT MAX(id) FROM `table`)) AS id) AS t2
ON t1.id >= t2.id
ORDER BY t1.id ASC LIMIT 5;
SELECT * FROM `user`
WHERE userId >= (
(SELECT MAX(userId) FROM `user`) - (SELECT MIN(userId) FROM `user`)
) * RAND() + (SELECT MIN(userId) FROM `user`)
LIMIT 5;
SELECT id FROM `table` WHERE id >= (SELECT FLOOR(RAND() * (MAX(id)-MIN(id)+1)) + MIN(id) FROM `table`) LIMIT 100;
取得 100 個候選 ID(極快,只掃索引)。shuffle 取前 N 個。SELECT * FROM `table` WHERE id IN (?,?,...);
| 方案 | 耗時 | 隨機度 | 適用場景 |
|---|---|---|---|
| ORDER BY RAND() | 2.3s | 完美 | 小表(<1萬) |
| MAX ID 點查 | 3ms | 連續段 | 單行、ID 連續 |
| 範圍映射 | 5ms | 近似均勻 | 單查詢、ID 較連續 |
| 兩階段法 | 8ms | 完美 | 生產環境、大表、多行 |
拒絕 ORDER BY RAND(),根據業務對隨機度與延遲的要求,選擇「範圍映射」或「兩階段法」。若資料量達千萬級,建議維護一張隨機抽樣表(定期重建),查詢時直接 JOIN 該表,將隨機化成本離線化。