ORDER BY RAND() 為何極慢?MySQL 會為每行產生隨機數、排序、再取 LIMIT。15 萬行表查 5 行耗時 8 秒+。官方文檔明確警告:RAND() 在 ORDER BY 中會被多次評估。
假設表有自增主鍵 id,無大量刪除導致空洞:
MIN(id)、MAX(id)FLOOR(RAND() * (MAX - MIN + 1)) + MINWHERE id >= 隨機點 ORDER BY id LIMIT 1| 版本 | SQL 片段 | 耗時 | 備註 |
|---|---|---|---|
| 原始 | ORDER BY RAND() LIMIT 5 | 8 秒+ | 全表排序 |
| JOIN 子查詢 | JOIN (SELECT ROUND(RAND()*(MAX-MIN)+MIN) AS id) t2 ON t1.id>=t2.id LIMIT 5 | 連續 5 行 | 需拆分 5 次查詢 |
| WHERE 子查詢 | WHERE id >= (SELECT FLOOR(MAX*RAND())) ORDER BY id LIMIT 1 | 0.5 秒 | 未含 MIN 修正 |
| 完整修正 | WHERE id >= (SELECT FLOOR(RAND()*(MAX-MIN+1)+MIN)) ORDER BY id LIMIT 1 | 0.01 秒 | 含 MIN 邊界修正 |
| JOIN 完整版 | JOIN (SELECT ROUND(RAND()*(MAX-MIN+1)+MIN) AS id) t2 ON t1.id>=t2.id LIMIT 1 | 0.0015 秒 | JOIN 效能最佳 |
SELECT t1.*
FROM `table` AS t1
JOIN (
SELECT ROUND(
RAND() * (
(SELECT MAX(id) FROM `table`) - (SELECT MIN(id) FROM `table`) + 1
) + (SELECT MIN(id) FROM `table`)
) AS id
) AS t2 ON t1.id >= t2.id
ORDER BY t1.id LIMIT 1;
| 方案 | 10 次查詢總耗時 |
|---|---|
| WHERE 子查詢版 | 0.147 秒 |
| JOIN 版 | 0.015 秒 |
JOIN 版快近 10 倍,建議生產採用。
ORDER BY RAND() 明顯慢OFFSET 方案-- 離線計算隨機權重
ALTER TABLE `table` ADD COLUMN `rand_weight` DOUBLE;
UPDATE `table` SET `rand_weight` = RAND();
CREATE INDEX idx_rand ON `table` (`rand_weight`);
-- 線上查詢極快
SELECT * FROM `table` ORDER BY `rand_weight` LIMIT 10;
適合「高頻隨機、資料相對靜態」場景(如推薦牆、廣告輪播)。
ORDER BY RAND() 是效能殺手。主鍵範圍 + JOIN 子查詢 可將百萬行表隨機查詢從秒級降至毫秒級。若業務允許,預計算隨機權重更是終極解法。