SQL 性能避坑:为什么阿里强制禁用 ORDER BY RAND()?
当前位置:点晴教程→知识管理交流
→『 技术文档交流 』
如果你翻阅过《阿里巴巴 Java 开发手册》,在 MySQL 数据库规约中,一定见过这条醒目的“红线”: 【强制】不得在 database 中使用 ORDER BY RAND() 进行随机排序。 很多人第一反应是:“不就随机查几条数据吗?MySQL 既然提供了这个内置函数,为什么不让用?” 事实上,这可能是 MySQL 里最“坑爹”的内置函数之一。在数据量只有几百条时,它是省时省力的小甜甜;一旦数据量突破十万级,它立马变身吸干 CPU 的“牛夫人”,分分钟让你的数据库报警。 今天我们就来扒一扒,为什么这个函数是性能杀手,以及在海量数据下,我们该如何优雅且高性能地实现“随机推荐”功能。 案发现场:一条 SQL 引发的血案那个让 DBA 暴跳如雷的 SQL 长这样:
为什么它这么慢?我在测试环境重现了一下,顺手敲了个 Using temporary; Using filesort 这简直是 MySQL 性能杀手界的“卧龙凤雏”!
这不崩谁崩? 深入剖析:五种高性能替代方案既然
适用场景:数据量不大(例如 < 10万),内存不值钱。 核心思想:既然数据库随机排序慢,那我把 ID 全拿出来,在 Java 代码里洗牌行不行? 代码实现优缺点点评
方案二:Limit 偏移法(Limit Offset) 适用场景:数据量大(百万级以上),对随机性要求没那么严苛。 核心思想:给所有数据编个号,随机生成一个“偏移量”,直接跳到那里去拿。 代码实现优缺点点评
核心思想:既然方案二取出的数据是连续的,那我多随机几次,每次取 1 条,拼凑出 3 条不就行了? 代码实现代码高亮: // 1. 获取总数 int total = productMapper.count(); // 2. 生成3个不重复的随机下标(Java 8 Stream 写法) List<Integer> randomOffsets = new Random() .ints(0, total) // 生成无限流 .distinct() // 去重 .limit(3) // 截取前3个 .boxed() .collect(Collectors.toList()); // 3. 循环查询(或者拼接 SQL 用 UNION ALL) List<Product> result = new ArrayList<>(); for (Integer offset : randomOffsets) { // SQL: SELECT * FROM product LIMIT #{offset}, 1 result.add(productMapper.selectByLimit(offset, 1)); } 其实这就是 MySQL 45讲 里推荐的优化思路。相比于方案二,它打散了连续性。 优缺点点评
方案四:主键范围法(Index Random)适用场景:ID 必须这是连续的(或空洞很少),追求极致性能。 核心思想:既然
Java 逻辑处理:
SQL 实现:
优缺点点评
方案五:Redis 预处理法(Redis Set)适用场景:高并发、高性能、大数据量,标准的互联网大厂打法。 核心思想:既然 MySQL 不擅长做随机,那就别难为它了,交给最擅长的 Redis。 代码实现
优缺点点评
最终总结:选型指南那这几种方案怎么选?
最后多嘴一句: 如果你的业务可以接受“伪随机”(比如每个人看到的随机列表在 1 小时内是一样的),强烈建议把算好的随机结果丢进 Redis。毕竟,最好的 SQL 优化就是不执行 SQL。 别让你写的代码,成为深夜报警的罪魁祸首。 参考文章:原文链接 该文章在 2026/1/7 17:00:46 编辑过 |
关键字查询
相关文章
正在查询... |