隨機查詢在推薦系統、廣告輪播、測試抽樣等場景極常見,但 ORDER BY RAND() 在大表上效能極差。本文提供單表與多表多種優化方案。
| 方案 | 適用場景 | 優點 | 缺點 |
|---|---|---|---|
| 應用層隨機 (全量 ID 緩存) | 資料量 < 10萬、更新頻率低 | 查詢極快、邏輯簡單 | 記憶體佔用大、資料不即時 |
WHERE id > RAND() * MAX(id) | 單表、ID 連續、無大量刪除空洞 | 無需緩存、SQL 完成 | 空洞導致分布不均、非真隨機 |
| 隨機游標法 | 單表、需真隨機、可接受雙查詢 | 分布均勻、支援條件過濾 | 需兩次查詢、游標維護成本 |
| 多表 Join + 隨機 | 關聯查詢隨機抽樣 | 業務邏輯完整 | Join 放大隨機成本、需優化 Join 順序 |
-- 定期重建緩存 (如每小時)
SELECT id FROM articles WHERE status=1 ORDER BY id;
-- 應用層: random.choice(id_list) 或 random.sample(id_list, k)
-- 再: SELECT * FROM articles WHERE id IN (?, ?, ?)
SELECT * FROM articles
WHERE id >= (SELECT FLOOR(RAND() * (SELECT MAX(id) FROM articles)))
ORDER BY id LIMIT 1;
⚠️ ID 有刪除空洞時,分布不均;需配合 WHERE status=1 等條件。
-- 步驟 1: 取總數
SELECT COUNT(*) FROM articles WHERE status=1; -- 假設 1000000
-- 步驟 2: 應用層產生隨機 offset
offset = random.randint(0, total-1)
-- 步驟 3: LIMIT offset, 1
SELECT * FROM articles WHERE status=1 ORDER BY id LIMIT 1000000, 1;
優化:建立覆蓋索引 (status, id),避免回表。
STRAIGHT_JOIN 強制 Join 順序-- 需求: 從有庫存的商品中,隨機推薦 5 個含分類名稱的商品
-- 方案: 衍生表先隨機抽樣商品 ID,再 Join 分類表
SELECT p.id, p.name, p.price, c.name AS category_name
FROM (
SELECT id, name, price, category_id
FROM products
WHERE stock > 0
ORDER BY RAND() -- 僅在縮小後的結果集排序
LIMIT 5
) p
LEFT JOIN categories c ON p.category_id = c.id;
-- 1. 應用層取得符合條件的主表 ID 列表 (可緩存、定期更新)
-- 2. 應用層隨機抽樣 k 個 ID
-- 3. 批次查詢詳情
SELECT p.*, c.name AS cat_name
FROM products p
LEFT JOIN categories c ON p.category_id = c.id
WHERE p.id IN (?, ?, ?, ?, ?);
-- 強制 Join 順序
SELECT STRAIGHT_JOIN p.*, c.name
FROM products p
LEFT JOIN categories c ON p.category_id = c.id
WHERE p.stock > 0
ORDER BY RAND() LIMIT 5;
-- 指定/忽略索引
SELECT * FROM products USE INDEX (idx_stock_id) WHERE stock > 0 ORDER BY RAND() LIMIT 5;
SELECT * FROM products IGNORE INDEX (idx_name) WHERE stock > 0 ORDER BY RAND() LIMIT 5;
ORDER BY RAND() LIMIT N