order by rand()在大表上必触发全表扫描,因mysql需为每一行调用rand()并全表排序,导致type: all、using temporary和using filesort,i/o与cpu双高。

ORDER BY RAND() 为什么在大表上必触发全表扫描
MySQL 执行 SELECT * FROM t ORDER BY RAND() LIMIT 10 时,根本不会“先取10条再随机”,而是:为表中**每一行都调用一次 RAND()**,生成一个随机值;再把这 N 行全部放进临时表,执行 filesort 排序;最后才取前 10 条。哪怕表有 100 万行,这个过程也必须完整走完——优化器无法跳过任何一行,因为每行的随机值不可预测、不可索引。
EXPLAIN 看到的全是危险信号
对任意带 ORDER BY RAND() 的语句执行 EXPLAIN,你一定会看到:
-
type: ALL(全表扫描,无视所有索引) -
Extra: Using temporary; Using filesort(强制建内存/磁盘临时表 + 外部排序) - 如果
sort_buffer_size不够,还会出现Copying to tmp table on disk或converting HEAP to MyISAM
这些不是警告,是已发生的资源消耗。10 万行就可能占满 sort buffer,溢出到磁盘;百万行会让 I/O 和 CPU 同时飙高,监控里 Sort_merge_passes 暴涨就是明证。
并发一上来,数据库就卡死
每个请求都重复上述流程,问题会指数级放大:
- 单个查询耗时从毫秒 → 秒级,10 QPS 就可能打满连接池
- JDBC 若没设
queryTimeout,线程会一直阻塞,拖垮整个应用 - 即使加了
WHERE status = 1,MySQL 仍需先全表扫描过滤,再对结果集逐行算RAND()—— 条件无法提前剪枝
这不是“慢一点”,是架构级风险:阿里曾因促销页未优化的一条 ORDER BY RAND(),导致核心库 CPU 长期 99%,引发下游服务雪崩。
主键连续 ≠ 安全,空洞才是隐形杀手
很多人试过 WHERE id >= FLOOR(RAND() * MAX(id)) ORDER BY id LIMIT 1,却发现抽样结果集中在 ID 尾部或频繁返回空。原因很直接:
- 删过大量数据后,
id出现大片空洞(比如最大是 100 万,但实际只有 20 万行) -
FLOOR(RAND() * 1000000)生成的随机数大概率落在空洞区间,查不到数据 - 只生成 N 个随机 ID 就去查,漏行率可能超 50%;补足数量必须重试或预生成冗余 ID
真正落地时,不能只靠一条 SQL 解决——应用层要控制重试、去重、缓存 MIN(id)/MAX(id),否则看似替换,实则埋下新坑。











