萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 資料庫 >> MySQL 教學

MySQL 高效隨機查詢多條記錄:避開 ORDER BY RAND() 陷阱

為什麼不用 ORDER BY RAND()?

SELECT * FROM news ORDER BY RAND() LIMIT 10 在幾十萬資料時會全表排序,耗時秒級甚至分鐘級。

方案一:子查詢取隨機起點(適合 ID 連續或較密集)

SELECT * FROM `news`
WHERE id >= (
    SELECT FLOOR(RAND() * (SELECT MAX(id) FROM `news`))
)
LIMIT 10;

原理:在 [1, MAX(id)] 取隨機數,查第一筆 ≥ 該值的記錄。
**缺點**:ID 有空洞時,分布偏向空洞後的記錄。

方案二:JOIN 子查詢(兼容 MIN/MAX,分布更均勻)

SELECT * FROM `news` AS t1
JOIN (
    SELECT ROUND(
        RAND() * ((SELECT MAX(id) FROM `news`) - (SELECT MIN(id) FROM `news`))
        + (SELECT MIN(id) FROM `news`)
    ) AS id
) AS t2 ON t1.id >= t2.id
LIMIT 10;

原理:在 [MIN(id), MAX(id)] 區間均勻取隨機起點,再向後取 10 筆。

效能對比(十萬級測試)

寫法耗時說明
ORDER BY RAND() LIMIT 10~2.3 秒全表排序
方案一(子查詢)~0.003 秒索引範圍掃描
方案二(JOIN)~0.004 秒兩次標量子查詢 + 索引掃描

進階:真正均勻隨機抽樣(Bernoulli 抽樣)

SELECT * FROM `news`
WHERE RAND() < 10 / (SELECT COUNT(*) FROM `news`)
LIMIT 10;

每行獨立以概率 n/總行數 被選中,**統計學完全均勻**,但需全表掃描計算 RAND(),適合中等表(< 10 萬行)。

生產環境推薦組合

  • 極大表(> 100 萬)+ 允許輕微不均勻:方案二(JOIN)
  • 中等表 + 要求嚴格均勻:Bernoulli 抽樣
  • 頻繁隨機讀、極高效能要求:預建 rand_sort 欄位定期更新,查詢時 ORDER BY rand_sort LIMIT n

**核心口訣:善用索引範圍掃描替代全表排序,隨機只在「起點」產生。**

copyright © 萬盛學電腦網 all rights reserved