需求:從千萬級表中高效隨機抽取 N 行。本文對比三種方案性能,揭示 ORDER BY RAND() 的全表掃描本質,推薦基於主鍵範圍的 JOIN 方案,並給出生產環境參數化模板。
| 方案 | SQL | 耗時 | 原理 | 適用場景 |
|---|---|---|---|---|
| 1. 簡單 RAND | SELECT * FROM t ORDER BY RAND() LIMIT 5; | 0.08 s | 全表掃描 + 臨時表 + filesort | 小表(<1 萬行)、低頻 |
| 2. RAND() 種子 | SELECT * FROM t WHERE RAND() < 0.01 LIMIT 5; | 0.06 s | 全表掃描,概率過濾 | 近似採樣、統計估算 |
| 3. 主鍵範圍 JOIN(推薦) | 見下文 | <0.001 s | 索引範圍掃描,無 filesort | 大表、高頻、生產環境 |
SELECT t1.*
FROM `table_name` AS t1
JOIN (
SELECT ROUND(RAND() * (SELECT MAX(id) FROM `table_name`)) AS id
) AS t2
WHERE t1.id >= t2.id
ORDER BY t1.id ASC
LIMIT 5;
SELECT MAX(id) 獲取主鍵上界(極快,走索引)。RAND() * MAX(id) 生成隨機起始點。JOIN 關聯主表,WHERE t1.id >= t2.id 定位到該點及其後的行。ORDER BY t1.id LIMIT N 取連續 N 行(主鍵聚簇索引順序讀取,零開銷)。ref / range。SELECT id FROM t ORDER BY id LIMIT 1 OFFSET FLOOR(RAND() * (SELECT COUNT(*) FROM t)) 獲取真隨機主鍵,再 JOIN 取數據(兩次索引查找,仍極快)。UNION ALL 合併多個隨機點。ROW_NUMBER() 物化視圖)。WITH rand_start AS (
SELECT FLOOR(RAND() * (SELECT COUNT(*) FROM `big_table`)) AS offset_val
),
rand_id AS (
SELECT id FROM `big_table` ORDER BY id LIMIT 1 OFFSET (SELECT offset_val FROM rand_start)
)
SELECT b.* FROM `big_table` b
JOIN rand_id r ON b.id = r.id;
ORDER BY RAND() LIMIT N 簡單直觀。OFFSET RAND()*COUNT(*) 取真隨機主鍵。Handler_read_rnd_next 指標,驗證是否退化為全表掃描。隨機抽樣看似簡單,實則考驗對索引、執行計劃、存儲引擎特性的綜合理解。選對方案,千萬行表也能毫秒級返回。