【问题标题】:Fast mysql random weighted choice on big database大数据库上的快速mysql随机加权选择
【发布时间】:2014-03-06 13:08:28
【问题描述】:

我建立了一个需要选择随机加权记录的网站 来自数据库。

SQL : select one row randomly, but taking into account a weight中有一段代码

SELECT t.*, RAND() * t.weight AS w 
FROM table t 
ORDER BY w DESC
LIMIT 1

它适用于小样本记录。

当尝试接近 100 万条记录时,它会变慢(1.3 - 1.8 秒) 在我的本地机器上,我想我会在更大的集合上花费更长的时间。

如何优化? 有没有更好的方法来随机选择加权记录?

我的尝试是定期计算权重,将它们存储在单独的表中,以编程方式选择随机数并搜索最接近该数字的记录。

【问题讨论】:

标签: mysql sql random


【解决方案1】:

您可以根据权重对数据进行分区,然后随机选择一个分区。

确定要使用的分区:O(n)

SELECT Weight, FLOOR(RAND()*COUNT(*)) as Target 
FROM test 
GROUP BY Weight
ORDER BY RAND()*(Weight)*count(Weight)/100 DESC
LIMIT 1;

使用之前查询的权重和目标得到结果:O(Log(n))

SELECT test.*
FROM test
WHERE Weight = $Weight
LIMIT $Target, 1

测试一下:

CREATE TABLE `test` (
  `Id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `Weight` int(11) NOT NULL,
  PRIMARY KEY (`Id`),
  KEY `Weight` (`Weight`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;


insert into test (Weight) ( select FLOOR(RAND()*1000) );

运行 20 次以创建 100 万个测试行:

insert into test (Weight) select FLOOR(rand()*1000) as Weight from test;

由于 GROUP BY,第一个查询在 O(n) 中运行。如果您维护另一个表来跟踪每个重量的计数,则可以将其降低到 log(n) 运行时间。

在测试表中有 800 万行的我的数据库中,第一个查询在 (6.089 s) 中运行,第二个在 (0.001 s) 中运行

【讨论】:

    【解决方案2】:

    首先获取所有权重的总和,以便您可以计算每行被动态选择的概率。

    SELECT SUM(weight) FROM t;
    

    我假设总金额可以通过名为 @TOTAL_WEIGHT 的 mysql 变量访问

    SELECT t.* 
    FROM t
    WHERE RAND() <= (weight / @TOTAL_WEIGHT)
    ORDER BY RAND()
    LIMIT 1;
    

    这有可能会遍历整个表并且仍然找不到匹配项,在这种情况下,您可能只是运行另一个查询来获取一个随机行。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-05-25
      • 2010-09-17
      • 2016-03-15
      • 1970-01-01
      • 2015-07-05
      • 2010-09-08
      相关资源
      最近更新 更多