O(N log N)。SELECT * FROM t ORDER BY RAND() LIMIT 5 耗時 8 秒以上。ORDER BY RAND() 會掃描全表並多次評估隨機值。假設表有自增主鍵 id,且刪除較少(ID 較密集)。
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 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 LIMIT 1;
range,極快。上述方案單次只能取 1 行(或連續 N 行)。若需 5 筆不重複:
LIMIT 20),應用層 shuffle 取 5 筆。-- 先取總行數
SELECT COUNT(*) INTO @total FROM `table`;
-- 隨機 OFFSET(需 MySQL 8.0+ 支援 CTE/變數)
SET @offset = FLOOR(RAND() * @total);
PREPARE stmt FROM 'SELECT * FROM `table` LIMIT ?, 1';
EXECUTE stmt USING @offset;
DEALLOCATE PREPARE stmt;
缺點:LIMIT offset, 1 深分頁同樣慢(需掃描 offset 行)。適合中小表(< 10 萬)。
| 方案 | 適用場景 | 耗時 (15萬行) | 均勻度 | 備註 |
|---|---|---|---|---|
| ORDER BY RAND() | 極小表 (<1000) | 8 秒+ | 完美 | 生產禁用 |
| WHERE id >= 隨機起點 | ID 密集、單行 | 0.01 秒 | 一般 | 簡單、無 JOIN |
| JOIN 衍生表 | ID 密集、單行/高頻 | 0.0015 秒 | 一般 | 效能最佳 |
| COUNT + LIMIT offset | 中小表、需均勻 | 0.1~1 秒 | 完美 | 深分頁慢 |
| TABLESAMPLE (8.0.23+) | 大表統計採樣 | 極快 | 近似 | 不保證精確行數 |
SELECT * FROM `table` TABLESAMPLE SYSTEM (10) LIMIT 5;
-- SYSTEM: 頁面層級採樣,適合大表近似隨機
SELECT * FROM `table` TABLESAMPLE BERNOULLI (10) LIMIT 5;
-- BERNOULLI: 行層級獨立採樣,較均勻但較慢
適用於分析型查詢,不適合業務精確隨機抽獎。
生產環境嚴禁 ORDER BY RAND()。
主鍵 ID 較密集時:JOIN 衍生表 + 隨機 ID 起點 是效能與簡潔度的最佳平衡(毫秒級)。
需嚴格均勻分布:應用層 COUNT(*) + 多次 LIMIT offset, 1 或 TABLESAMPLE BERNOULLI。
永遠記得:EXPLAIN 驗證 type=ref/range,而非 ALL。