用于在MySQL中获取随机行的无间隙辅助ID

用于在MySQL中获取随机行的无间隙辅助ID,mysql,design-patterns,orm,random,query-optimization,Mysql,Design Patterns,Orm,Random,Query Optimization,在我一直从事的几个项目中,我遇到了从大于1M行的大型表中获取随机行的需要。对于这么大的表,ORDER BY rand LIMIT 1是不可选择的,因为它会很快使数据库崩溃 通常的解决方案是在MINid和MAXid之间生成一个随机数,然后直接选择该行。但是,如果id序列中存在较大的间隔,则需要多次重新滚动,或者使用WHERE id>=:myrandomnumber,这将导致成功获得较大间隔的行的命中率显著高于平均值 我一直在考虑通过创建一个新的索引列来解决这个问题,只用于随机目的,比如id2。此列

在我一直从事的几个项目中,我遇到了从大于1M行的大型表中获取随机行的需要。对于这么大的表,ORDER BY rand LIMIT 1是不可选择的,因为它会很快使数据库崩溃

通常的解决方案是在MINid和MAXid之间生成一个随机数,然后直接选择该行。但是,如果id序列中存在较大的间隔,则需要多次重新滚动,或者使用WHERE id>=:myrandomnumber,这将导致成功获得较大间隔的行的命中率显著高于平均值

我一直在考虑通过创建一个新的索引列来解决这个问题,只用于随机目的,比如id2。此列始终是1和表中行数之间的无间隙序列

问:什么是保持该序列无间隙的最佳方法

想到的第一个解决方案是创建一个包含tablename和id2列的helper表id。每当从tablename中删除一行时,该行的id2就会插入到ID中。插入新行时,id2要么从ID中选择,要么如果没有可用的,则创建一个新行。有没有更简单的方法

附加问题:是否有已经这样做的orm或框架,或者有其他有效的随机行选择?这是一个现有的模式,它有名字吗

更新:我为此编写了一个快速基准测试,并针对一个125000行的表运行它,其中有30000行之间的间隙。结果非常有希望:

Fetch a random row 100 times using id2: 0.0234689712524 seconds
Fetch a random row 100 times using ORDER BY rand() LIMIT 1: 54.992347002 seconds
在插入测试数据时,我每插入五行就删除一个随机行。序列始终保持无间隙

for($i=1; $i<=$amount; $i++) {
    insert_row();
    if($i % 5 == 0)
        delete_random_row();
}
在我的低端vserver上,再次运行$amount=10000的循环需要9秒。这是每行0.009秒,包括每五次迭代删除一个随机行。它确实会随着表的增长而变慢,但获取随机行却不会


我最初的问题仍然适用。

以下是我的做法-

从表中选择MAXid 在PHP或任何您正在使用的语言中,生成一个介于1和MAXid之间的随机整数 从id>=$random ORDER BY id ASC LIMIT 1的表格中选择* 如果3不返回任何内容,请从id<$random ORDER BY id DESC LIMIT 1的表中选择*
避免运行速度非常慢的任何查询。它还避免了额外的列,保持无间隙,这将是一个讨厌的工作真的

我要说的是,这是救援行动

SET @rank:= 1;  

SELECT * FROM
  (
  SELECT @rank:= @rank + 1 as rank, * FROM table1  
  ) s
WHERE s.rank = $random;

谢谢你的回答,但如果有差距,我希望随机性分布均匀,那就不行了。想象一个夸张的例子,一个表有三行,ID为1、2、1000000。前两行的概率为百万分之一。你可以根据范围和随机生成的值,通过操纵限制来进一步随机化。你指的是哪种操纵?除了ORDER BY rand或我所描述的额外列之外,我想不出任何其他方法来获得更接近33%的赔率。如果我错了,请纠正我,但这看起来像是对一个有一百万行的表的绝对杀手级查询。@Kaivosukeltaja,你是对的:-\。如果你有什么好主意,如何使这一速度达到100万行,我当然很乐意听到。当然,您可以使用表中的一个额外列作为更新运行排名,而不必只杀死服务器一次。我想我必须对它进行基准测试,看看它是如何工作的。