首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> MySQL 資料庫 >> MySQL 綜合教學

MySQL 隨機查詢優化終極指南:從 5 秒到 0.0003 秒的完整演進

     編輯:MySQL 綜合教學

問題現場

5 萬行表,ORDER BY RAND() LIMIT 10 耗時 5 秒+。業務需高頻隨機推薦,完全不可接受。

優化演進時間軸

第一代:ORDER BY RAND()(基準)

SELECT * FROM `table` ORDER BY RAND() LIMIT 10;
-- 5 秒+,全表掃描 + 臨時表 + filesort

第二代:MAX(id) * RAND() JOIN(連續資料)


SELECT * FROM `table` AS t1
JOIN (SELECT ROUND(RAND() * (SELECT MAX(id) FROM `table`)) AS id) AS t2
WHERE t1.id >= t2.id
ORDER BY t1.id LIMIT 10;
-- 0.01 秒,但返回連續 10 行,非真隨機

第三代:WHERE 子查詢版(0.5 秒 → 0.01 秒)


-- 未優化
SELECT * FROM `table`
WHERE id >= (SELECT FLOOR(MAX(id) * RAND()) FROM `table`)
ORDER BY id LIMIT 1;
-- 0.5 秒

-- 優化:RAND() 移出聚合
SELECT * FROM `table`
WHERE id >= (SELECT FLOOR(RAND() * (SELECT MAX(id) FROM `table`)))
ORDER BY id LIMIT 1;
-- 0.01 秒

第四代:MIN/MAX 修正非零起始 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;
-- 解決 ID 從 10000 起導致前段資料被過度查詢

第五代:JOIN 最終版(單次 0.0015 秒,10 次 0.015 秒)


SELECT * 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;
-- 10 次總耗時 0.015 秒,單次 0.0015 秒

第六代:批量隨機 10 行(生產實戰版)


SELECT * 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 10;
-- 單次查詢取 10 行(連續段),業務可接受「近似隨機」
-- 耗時 0.0003 秒,比原始 5 秒快 16000 倍

效能對比總表

代數方案單次耗時10 次/10 行耗時隨機度備註
1ORDER BY RAND()~5000 ms~50000 ms真隨機不可用
2MAX JOIN 連續10 ms100 ms連續段需去重
3WHERE 子查詢500 ms → 10 ms5000 → 100 ms單行真隨機優化後可用
4MIN/MAX 修正10 ms100 ms單行真隨機通用修正
5JOIN 最終版1.5 ms15 ms單行真隨機單行首選
6JOIN 批量 LIMIT 100.3 ms0.3 ms近似隨機(連續段)批量首選

生產部署檢查清單

  1. 確認 id 為主鍵/唯一索引,且型別為整數(BIGINT/INT)。
  2. ID 分布極不均(大量洞)時,建立 seq_id 影子欄位(觸發器維護 1..N 連續),隨機查 seq_id 再 JOIN 回主表。
  3. 非整數主鍵(UUID/字串):CRC32() 或 CONV(SUBSTRING(MD5(),1,16),16,10) 映射為數值,建立函數索引(MySQL 8.0+)或影子欄位。
  4. 極高併發(QPS > 1000)隨機讀:考慮 Redis SRANDMEMBER / ZRANDMEMBER 緩存熱點 ID 集合,降級走 MySQL。
  5. 監控慢查詢日誌,確認無 ORDER BY RAND() 殘留。

MySQL 8.0 新選項:TABLESAMPLE


-- Bernoulli 採樣(每行獨立概率)
SELECT * FROM `table` TABLESAMPLE BERNOULLI (0.1) LIMIT 10;

-- SYSTEM 採樣(頁面級,更快但較粗糙)
SELECT * FROM `table` TABLESAMPLE SYSTEM (10) LIMIT 10;

無需索引、無需主鍵整數、真隨機、極快。但 8.0.25+ 才支援,且為近似行數。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved