一、背景
自己因为有一个答题插件逻辑,需要从数据库随机选择一定数目的题目,这个业务已经被同事实现,但我觉得他实现不对或者不够好,于是思考这个问题。他用rand() 产生一个随机数,然后大于这个随机数 加上limit 得到 题库。
二、问题
- 题库是连续的,用户体验不好
- 随机概率增大,如果用这个思路写抽奖那么绝对是不对的。
三、解决思路
假设我们选择uid 用户的ID 是整数,主键。。
select * from xxx where uid >= (rand(max(uid) - min(uid) ) + min(uid)) limit 1
上面手写,不代表语法正确, rand 只是找一个随机数据, 这个可以程序传过来,不一定要数据库随机数。 后面+min(uid) 只是为了防止随机到0而已,保证uid最小值而已。
如果要随机多条,应该调用多次,而不是limit 10 (假设多条是10),因为这里有一个隐藏前提,随机条数和数据必须差别比较大,不然很可能出现连续,而不是看起来随机的。
四、补充
select * from xxx where uid >= (rand(max(uid) - min(uid) ) + min(uid)) limit 1
select * 加了条件,如果数据库有10条数据,uid(0~9),如果我随机5条数据话,如果用上面的 select * from xxx where uid >= (rand(max(uid) - min(uid) ) + min(uid)) limit 5 。很容易出现连续的,而且数据缺失。select * 拿数据,会一行行拿,条件对比,而不是扑通一下拿取所有数据,然后才进行对比,所以用rand() limit 个数,会出现不连续,而你自己用 select * from xxx where uid >= 10 limit 5 却是 连续的,这里10我只是随便举一个数字而已。 如果数据少,加上UID自增,随机到大于5是50% ,如果我选择5条数据,那么很容易不够,并且连续,这里连续满足where条件太少了,所以导致连续,如果数据足够多就没有这个问题。
我以前全表扫描比较慢,测试200W数据,个人电脑也就0.85秒消耗。如果加上条件那么速度快很多,更不要说有索引的情况下。。。
这个问题我记录下来,只是跟后端开发探讨出来,自己不喜欢写数据库东西。只是记录技术而已。