SELECT * FROM articles WHERE status = 1 ORDER BY RAND() LIMIT 10;
原理: MySQL 為每行生成隨機數,按隨機數排序,取前 N 行。缺點: 全表掃描 + 臨時表 + 文件排序,數據量 > 1萬時極慢。
// 1. 獲取最大 ID
$maxId = $pdo->query("SELECT MAX(id) FROM articles WHERE status = 1")->fetchColumn();
// 2. 生成 10 個隨機 ID (去重)
$ids = [];
while (count($ids) < 10) {
$ids[] = random_int(1, $maxId);
}
$ids = array_unique($ids);
// 3. 查詢 (可能不足 10 條,需補查)
$placeholders = implode(',', array_fill(0, count($ids), '?'));
$stmt = $pdo->prepare("SELECT * FROM articles WHERE id IN ($placeholders) AND status = 1");
$stmt->execute($ids);
$rows = $stmt->fetchAll();
// 4. 若不足,補充順序查詢
if (count($rows) < 10) {
$stmt = $pdo->prepare("SELECT * FROM articles WHERE status = 1 AND id NOT IN ($placeholders) ORDER BY id LIMIT ?");
$params = array_merge($ids, [10 - count($rows)]);
$stmt->execute($params);
$rows = array_merge($rows, $stmt->fetchAll());
}
// 1. 統計總數 (可緩存)
$total = $pdo->query("SELECT COUNT(*) FROM articles WHERE status = 1")->fetchColumn();
// 2. 生成隨機偏移
$offset = random_int(0, max(0, $total - 10));
// 3. LIMIT offset, 10
$stmt = $pdo->prepare("SELECT * FROM articles WHERE status = 1 LIMIT ?, 10");
$stmt->execute([$offset]);
$rows = $stmt->fetchAll();
// 注意:offset 越大越慢,但比 ORDER BY RAND() 快得多
// 表結構增加
ALTER TABLE articles ADD COLUMN rand_val DOUBLE DEFAULT 0;
CREATE INDEX idx_rand_status ON articles (status, rand_val);
// 定期任務 (每天/每小時) 更新隨機值
UPDATE articles SET rand_val = RAND() WHERE status = 1;
// 查詢變為極快的索引掃描
SELECT * FROM articles WHERE status = 1 ORDER BY rand_val LIMIT 10;
| 方案 | 適用場景 | 性能 | 隨機度 | 維護成本 |
|---|---|---|---|---|
| ORDER BY RAND() | 小表 (<5000行) | 極慢 | 完美 | 無 |
| ID 範圍隨機 | ID 連續、中小表 | 快 | 良好 | 低 |
| 隨機偏移 | 大表、ID 不連續 | 中等 (offset 大時慢) | 良好 | 低 |
| 預計算隨機列 | 大表、高頻隨機讀 | 極快 (索引) | 完美 | 需定時任務 |
ORDER BY RAND() LIMIT 10 可接受。