表 table 約 15 萬行,需隨機抽取 5 條記錄。最直觀的寫法:
SELECT * FROM `table` ORDER BY RAND() LIMIT 5;
實測耗時:8 秒+ —— 完全不可接受。
官方文檔解釋:RAND() 在 ORDER BY 中會被多次計算,導致全表掃描 + 臨時表排序,複雜度 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 ASC LIMIT 5;
LIMIT 1 查 5 次,單次 0.01sSELECT * 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`) - (SELECT MIN(id) FROM `table`)) + (SELECT MIN(id) FROM `table`) )
)
ORDER BY id LIMIT 1;
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 1;
| 方案 | 單次耗時 | 10次總耗時 | 隨機性 | 適用場景 |
|---|---|---|---|---|
| ORDER BY RAND() | 8s+ | - | 完美 | 極小表(<1000行) |
| MAX*RAND + JOIN | 0.01s | 0.1s | 連續段 | 需連續樣本 |
| WHERE + 子查詢 (修正版) | 0.01s | 0.147s | 良好 | 通用 |
| JOIN + 子查詢 (推薦) | 0.0015s | 0.015s | 良好 | 高性能通用 |
id 有序特性,將隨機轉化為「隨機起點 + 範圍掃描」JOIN 的優化器支持更好,衍生表物化後可直接參與索引查找,而 WHERE 子查詢可能重複執行MAX*RAND 會導致前段數據概率加倍,引入 MIN 實現均勻分布-- 通用模板(單條隨機)
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;
-- PHP 批量獲取 N 條不重複隨機記錄
$ids = [];
$results = [];
for ($i = 0; $i < $N; $i++) {
$sql = "上述模板";
$row = $db->query($sql)->fetch();
if ($row && !in_array($row['id'], $ids)) {
$ids[] = $row['id'];
$results[] = $row;
}
}
// 15萬表,N=20 總耗時約 0.03s
OFFSET FLOOR(RAND() * COUNT(*)) 方案(需覆蓋索引支持)TABLESAMPLE(伯努利/系統採樣),但僅支持 InnoDB 且為近似隨機MySQL 隨機查詢的本質是「如何用索引替代全表排序」。JOIN + 衍生表 + 主鍵範圍查找 是經過實測驗證的最高性能方案,將 15 萬表隨機查詢從 8 秒降至 1.5 毫秒,性能提升 5000 倍。記住:ORDER BY RAND() 只屬於教科書和極小表。