2012-03-28 60 views
5

我正在寻找一种从MySQL表中随机选择100行满足特定条件的有效方法,这些行可能有数百万行。如何从MySQL表中随机选择满足特定条件的多行?

几乎所有我发现的建议避免使用ORDER BY RAND(),因为性能和可伸缩性较差。

但是,this article建议ORDER BY RAND()仍然可以用作获取randow数据的“快捷方式”。

基于这篇文章,下面是一些示例代码,显示了我试图完成的任务。我的问题是:

  1. 这是从一个表,可能有数百万行随机选择100(或多达几百个)行的有效途径?

  2. 什么时候成绩会成为问题?

 
    SELECT user.* 
    FROM (
      SELECT id 
      FROM user 
      WHERE is_active = 1 
      AND  deleted = 0 
      AND  expiretime > '.time().' 
      AND  id NOT IN (10, 13, 15) 
      AND  id NOT IN (20, 30, 50) 
      AND  id NOT IN (103, 140, 250) 
     ORDER BY RAND() 
      LIMIT 100 
      ) 
      AS  random_users 
    STRAIGHT JOIN user 
    ON  user.id = random_users.id 
+0

在索引字段上选择随机值是有意义的。 – Kayser 2012-03-28 16:52:34

+0

@Kayser,我担心我们仍然需要扫描WHERE条件的所有行。这是否会影响性能与巨大的表(可能数百万行)? – user1298692 2012-03-28 17:05:04

+0

使用pk-subselect的方法很可能只会稍微减少执行时间。这是因为无论使用或不使用这种技术,都会为所有匹配的行调用rand(),并且要排序的行数相同。假设“用户”有很多列或者大的列,并且mysql在LIMIT发生后没有足够的智能来实现用户*(这应该被测试),那么这很有趣。 – 2012-03-28 19:49:33

回答

0

恐怕没有人会能够作出准确回答你的问题。如果你真的想知道你需要针对你的系统运行一些基准测试(而不是理想的实况测试,但确切的副本)。基于不同解决方案(例如,使用PHP获取随机行),并将这些数据与您/您的客户认为的“良好性能”进行比较,然后提高您的数据,尽量保持列值的分布接近真实你可以看到性能开始下降,说实话,如果它现在对你有一定的提升空间,那么我会努力去做,当(如果!)它成为瓶颈,那么你可以看看它再次 - 或者只是扔掉多余的铁在你的数据库...

1

强烈敦促您阅读本article的最后一段将是覆盖多个随机行的选择,你应该能够注意到SELECT声明。将在那里描述的PROCEDURE这将是你添加特定的WHERE con的地方扬长避短。

ORDER BY RAND()的问题在于此操作的复杂度为n*log2(n),而我链接的文章中描述的方法具有几乎不变的复杂度。

让我们假设,在选择从表,其中包含10个条目,使用ORDER BY RAND()随机行占用1 time unit

entries | time units 
------------------------- 
     10 |   1  /* if this takes 0.001s */ 
     100 |  20 
    1'000 |  300 
    10'000 |  4'000 
    100'000 | 50'000 
1'000'000 | 600'000  /* then this will need 10 minutes */ 

而且你写的,你是对的数以百万计的规模处理表。

0

预处理尽可能 尝试像(VB类为例)

Dim sRND = New StringBuilder : Dim iRandom As New Random() 
Dim iMaxID As Integer = **put you maxId here** 
Dim Cnt as Integer=0 
While Cnt < 100 
     Dim RndVal As Integer = iRandom.Next(1, iMaxID) 
     If Not ("10,13,15,20,30,50,103,140,250").Contains(RndVal) Then 
      Cnt += 1 
      sRND.Append("," & RndVal) 
     end if 
End While 
String.Format("SELECT * FROM (Select ID FROM(User) WHERE(is_active = 1) AND deleted = 0 AND expiretime > {0} AND id IN ({1}) .blahblablah.... LIMIT 100",time(), Mid(sRND.ToString, 2)) 

我没有检查语法,但你会明白我的意思,我希望。 这将使MySql读取符合'IN'的记录,并在达到100时停止,而无需先预处理所有记录。

请让我知道经过时间的差异,如果你尝试它。 (我是qurious)

相关问题