SELECT * FROM t ORDER BY RAND() LIMIT 10 —— MySQL 必須為每行產生隨機數、全表排序、再取前 10 行。百萬行表單次查詢耗時秒級,高併發下直接拖垮資料庫。
SELECT * FROM t ORDER BY RAND() LIMIT 5;
SELECT * FROM t WHERE RAND() < 0.001 LIMIT 10;
SELECT * FROM t AS r1
JOIN (
SELECT ROUND(RAND() * (SELECT MAX(id) FROM t)) AS id
) AS r2 ON r1.id >= r2.id
ORDER BY r1.id ASC LIMIT 5;
-- 步驟 1:取得總行數(可快取)
SELECT COUNT(*) FROM t; -- 假設 1,000,000
-- 步驟 2:應用層產生 5 個隨機 OFFSET
-- PHP 範例
$offsets = array_map(fn() => mt_rand(0, 999999), range(1, 5));
-- 步驟 3:每個 OFFSET 執行單點查詢
foreach ($offsets as $offset) {
$sql = "SELECT * FROM t LIMIT $offset, 1";
// 執行並收集結果
}
LIMIT offset, 1 走索引定位,極快。SELECT * FROM t TABLESAMPLE SYSTEM (10) LIMIT 10;
| 方案 | 耗時 | 分布均勻性 | 版本需求 | 適用場景 |
|---|---|---|---|---|
| ORDER BY RAND() | ~2.5 秒 | 完美 | 所有 | 小表、一次性腳本 |
| WHERE RAND() < p | ~0.3 秒 | 近似 | 所有 | 統計抽樣、不固定行數 |
| 主鍵範圍 JOIN | ~0.05 秒 | 依賴 ID 連續性 | 4.1+ | ID 連續、固定行數 |
| 兩階段 OFFSET | ~0.01 秒 | 完美 | 所有 | 通用高效首選 |
| TABLESAMPLE | ~0.02 秒 | 近似 | 8.0.27+ | 大表近似抽樣 |
WHERE RAND() < p 或 TABLESAMPLE。隨機抽樣看似簡單,實則考驗對 MySQL 執行引擎的理解。ORDER BY RAND() 是反模式,請將其列入 Code Review 禁止清單。掌握「應用層產生隨機數 + 資料庫端索引點查」的兩階段模式,能以最小代價獲得完美分布的固定行數隨機樣本,是高併發系統的標準解法。