ORDER BY RAND() 極其低效?MySQL 會為每行生成隨機數、建立臨時表、排序、再取前 N 行。15 萬行查 5 行耗時 8 秒+,複雜度 O(N log N),大表不可用。
SELECT * FROM `table` AS t1
JOIN (SELECT ROUND(RAND() * (SELECT MAX(id) FROM `table`)) AS id) AS t2
WHERE t1.id >= t2.id
ORDER BY t1.id LIMIT 5;
⚠️ 產生連續 5 行,非真正隨機。需執行 5 次 LIMIT 1 並去重。
SELECT * FROM `table`
WHERE id >= (SELECT FLOOR(MAX(id) * RAND()) FROM `table`)
ORDER BY id LIMIT 1;
SELECT * FROM `table`
WHERE id >= (SELECT FLOOR(RAND() * (SELECT MAX(id) FROM `table`)))
ORDER BY id LIMIT 1;
將 RAND() * MAX(id) 移出聚合函數,避免重複掃描。
SELECT * FROM `table`
WHERE id >= (
SELECT FLOOR(
RAND() * ((SELECT MAX(id) FROM `table`) - (SELECT MIN(id) FROM `table`))
+ (SELECT MIN(id) FROM `table`)
)
)
ORDER BY id LIMIT 1;
解決 ID 從 10000 起始導致前段資料被過度查詢的問題。
SELECT * FROM `table` AS t1
JOIN (
SELECT ROUND(
RAND() * ((SELECT MAX(id) FROM `table`) - (SELECT MIN(id) FROM `table`))
+ (SELECT MIN(id) FROM `table`)
) AS id
) AS t2 ON t1.id >= t2.id
ORDER BY t1.id LIMIT 1;
JOIN 版比 WHERE 版快近 10 倍,因衍生表僅掃描一次索引,且優化器能更好利用索引範圍掃描。
ORDER BY RAND() LIMIT N 可接受。UNION)。SELECT * FROM t WHERE id >= (隨機起點) ORDER BY id LIMIT N 取連續段,效能極高。CRC32() 或 MD5() 轉數值,或維護對應自增影子欄位。| 方案 | 單次耗時 | 10 次總耗時 | 備註 |
|---|---|---|---|
| ORDER BY RAND() | ~8 秒 | ~80 秒 | 全表掃描+排序 |
| MAX(id) JOIN 連續 | 0.01 秒 | 0.05 秒 | 連續資料,需去重 |
| WHERE 子查詢版 | 0.5 秒 | 5 秒 | 未優化執行計畫 |
| 優化 WHERE 版 | 0.01 秒 | 0.147 秒 | 含 MIN/MAX 修正 |
| JOIN 最終版 | 0.0015 秒 | 0.015 秒 | 生產環境首選 |
id 有索引(主鍵自動)。SRANDMEMBER 替代。