23Nov/110
死于Order By Rand()
我的某个项目里有需求从N个项目里随机抽取M个,这里我用到了Order By Rand()。
开始运作的挺好,工作表的记录数在几千左右,响应时间都马马虎虎过得去。
后来我们添加了一个功能,建了一个新表,这个表的记录会持续增加。累积了一段时间灾难爆发了,应用连接不上数据库,用mysql console,show processlist看到这种查询竟然要几秒钟。Order By Rand()用在大量记录的表上真是个灾难,而且会越来越慢。
网上搜索了一下方法,在min(id) max(id)之间生成一个随机数,然后选择大于等于这个id的M个记录即可。
SELECT * FROM `centences` AS t1 JOIN (SELECT ROUND(RAND() * ((SELECT MAX(id) FROM `centences`)-(SELECT MIN(id) FROM `centences`))+(SELECT MIN(id) FROM `centences`)) AS id) AS t2 WHERE t1.id >= t2.id ORDER BY t1.id LIMIT 1;
经过修改,问题解决。不过这个解决方案有个缺陷,假设不是LIMIT 1,会有几率导致结果比预想的少一点,这种时候就需要靠程序判断去弥补了。
另外一个问题,就是取到的是连续的记录,这个不满足我们的应用。这里依然采用循环M次的方法,每次取1。虽然执行的语句多了,但是总体执行时间还是比Order By Rand要节省很多。