ORDER BY RAND() LIMIT N 是最直觀的隨機查詢寫法,但在大表上性能極差。本文通過 15 萬行實測,對比多種優化方案,給出最佳實踐。
id 自增,無業務空洞| 方案 | SQL 核心邏輯 | 耗時 | 評價 |
|---|---|---|---|
| 1. 原生 RAND | SELECT * FROM t ORDER BY RAND() LIMIT 5 | 8 秒+ | 全表掃描+臨時表+filesort,極慢 |
| 2. 子查詢 MAX*RAND | WHERE id >= (SELECT FLOOR(MAX(id)*RAND()) FROM t) ORDER BY id LIMIT 1 | 0.5 秒 | 仍需掃描部分索引 |
| 3. 完整範圍 MIN/MAX | WHERE id >= (SELECT FLOOR(RAND()*(MAX(id)-MIN(id))+MIN(id)) FROM t) ORDER BY id LIMIT 1 | 0.01 秒 | 單次索引查找,極快 |
| 4. JOIN 衍生表(推薦) | JOIN (SELECT ROUND(RAND()*(MAX(id)-MIN(id))+MIN(id)) AS id FROM t) t2 ON t1.id >= t2.id ORDER BY t1.id LIMIT 1 | 0.0015 秒 | 衍生表物化一次,JOIN 消除相關子查詢開銷 |
// 方案 3(WHERE 子查詢)
總耗時: 0.147 秒 -- 平均 0.0147 秒/次
// 方案 4(JOIN 衍生表)
總耗時: 0.015 秒 -- 平均 0.0015 秒/次
結論:JOIN 衍生表方案性能碾壓 WHERE 子查詢,穩定在毫秒級。
SELECT t1.*
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 ASC
LIMIT 1;
LIMIT N 但需接受連續 ID 可能性(業務容忍度評估)。MAX*RAND 方案命中率驟降,必須用 MIN/MAX 範圍修正。ORDER BY RAND() 或建立冗餘自增列。TABLESAMPLE SYSTEM (1) 伯努利採樣(近似隨機)。「拒絕 ORDER BY RAND(),擁抱 JOIN 衍生表」,是大表隨機查詢的鐵律。