ORDER BY RAND() 是 MySQL 隨機查詢的「效率殺手」。當數據量超 10 萬行,全表掃描+臨時表+文件排序會導致查詢時間呈指數級增長。本文介紹基於 JOIN 的經典優化方案,實測性能提升 100 倍以上。
ORDER BY RAND() 這麼慢?MySQL 必須為每行生成隨機數、寫入臨時表、排序、再取前 N 行。時間複雜度 O(N log N),且無法利用索引。
假設表有自增主鍵 id,且分布相對連續。隨機取一個 ID 值,向後查找滿足條件的行:
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
WHERE t1.id >= t2.id
ORDER BY t1.id LIMIT 10;
t2 計算一個落在 [MIN(id), MAX(id)] 範圍內的隨機 ID。t1.id >= 隨機ID 的第一行。LIMIT 10 行,避免全表排序。時間複雜度降為 O(log N + Limit),極大縮短響應時間。
WHERE EXISTS (SELECT 1 FROM table WHERE id = t2.id) 修正,或改用「隨機偏移量」方案。UNION 多個隨機點。MySQL 8.0 引入 TABLESAMPLE(Bernoulli/System 採樣),但目前僅適用於 InnoDB 且為近似隨機,精確隨機仍推薦 JOIN 方案。
| 方案 | 耗時 | 說明 |
|---|---|---|
ORDER BY RAND() LIMIT 10 | ~2.3 秒 | 全表掃描+臨時表+filesort |
| JOIN 隨機 ID 方案 | ~0.02 秒 | 索引範圍掃描,提速 100 倍+ |
告別 RAND(),用索引換速度,是大數據量隨機查詢的正解。