3

抽象的

在与一些同事交谈时,我们遇到了“从大型数据库表中提取随机行”问题。这是一个经典的方法,我们知道天真的方法(也在 SO 上)通常是这样的:

SELECT * FROM mytable ORDER BY RAND() LIMIT 1

问题

我们也知道这样的查询是完全低效的,并且实际上只能用于很少的行。可以采取一些方法来获得更高的效率,例如这些仍然在 SO 上的方法,但它们不适用于任意主键,并且一旦数字主键中有孔,随机性就会出现偏差。最后一个引用问题的答案链接到这篇文章,该文章有一个很好的解释和一些聪明的解决方案,涉及一个额外的“平均分布”表,每当“主数据”表发生变化时必须维护该表。但话又说回来,如果你在一张大桌子上经常删除,你可能会被不断更新添加的桌子搞砸了。另请注意,许多解决方案依赖于COUNT(*)这在 MyISAM 上速度快得离谱,但在 InnoDB 上却“很快”(我不知道它在其他平台上的表现如何,但我怀疑 InnoDB 案例可能代表其他事务性数据库系统)。

除此之外,即使是我能找到的最好的解决方案也很快,但不是Ludicrous Speed快。

理念

一个单独的服务可能负责生成、缓冲和分发随机行 ID 甚至整个随机行:

  • 它可以根据原始 PK 的结构选择最佳方法来提取随机行 ID。服务可以在 ram 中维护有序的键列表(除了 PK 的实际大小之外,每行不应占用太多字节,标准 PC 可能最多 100~1000M 行,最多 1~ 100 亿行和强大的服务器)
  • 一旦密钥在内存中,每个密钥都有一个隐含的“行号”,并且其中没有漏洞,因此只需选择一个随机数并直接获取相应的密钥
  • 可以维护准备好使用的随机密钥缓冲区,以快速响应传入请求中的峰值
  • 服务的消费者将连接并从缓冲区请求 N 个随机行
  • 行作为简单键返回,或者服务可以维护一个(池)数据库连接以获取整行
  • 如果缓冲区为空,则请求可能会阻塞或返回类似 EOF
  • 如果将数据添加到主表中,则必须向服务发送信号以将相同的数据也添加到其副本中,刷新随机选择的缓冲区并从那里继续
  • 如果从主表中删除数据,则必须向服务发出信号以从“所有键”列表和“随机选择”缓冲区中删除该数据
  • 如果数据在主表中更新,则必须向服务发出信号以更新键列表和随机选择中的相应行

为什么我们认为它很酷

  • 不接触磁盘,除了在启动时或发出信号时初始加载键
  • 适用于任何类型的主键,无论是否为数字
  • 如果您知道要更新大量数据,您可以在完成时发出信号(即不是在原始数据的每一次插入/更新/删除时),这基本上就像拥有一个细粒度的锁仅阻止对随机行的请求
  • 对原始数据中任何类型的更新都非常快
  • 将一些工作从关系数据库卸载到另一个仅内存的进程:有助于可伸缩性
  • 从缓冲区快速响应,无需等待任何查询、扫描、排序
  • 可以很容易地扩展到 SQL 之外的类似用例

为什么我们认为这可能是一个愚蠢的想法

  • 因为我们在没有任何第三方帮助的情况下有了这个想法
  • 因为没有人(我们听说过)曾经费心做类似的事情
  • 因为它增加了混合的复杂性,以便在原始数据更改时保持更新

问题是……

是否已经存在类似的东西?如果没有,是否可行?如果不是,为什么?

4

2 回答 2

1

您的“合格主键缓存”概念的最大风险是在原始数据不断变化时使缓存保持最新。保持缓存同步的成本可能与对原始数据运行随机查询的成本一样高。

您希望如何向缓存发出已添加/删除/更新值的信号?如果您使用触发器执行此操作,请记住,即使生成它的事务被回滚,触发器也可以触发。这是从触发器通知外部系统的一般问题。

如果您在数据库中提交更改从应用程序通知缓存,那么您必须担心其他应用程序在没有安装信号代码的情况下进行更改。或临时查询。或者来自您无法更改代码的应用或工具的查询。

一般来说,增加的复杂性可能不值得。大多数应用程序可以容忍一些妥协,并且它们不需要一直绝对随机选择。

例如,对于某些需求,不等式查找可能是可以接受的,即使已知的弱点是更频繁地选择间隙后面的数字。

或者您可以预先选择少量随机值(例如 30)并缓存它们。让应用程序请求从中选择。每隔 60 秒左右,用另一组随机选择的值刷新缓存。

或者选择一个在 MIN(id) 和 MAX(id) 之间均匀分布的随机值。尝试通过平等而不是不平等进行查找。如果该值对应于主键中的间隙,则只需循环并使用不同的随机值重试。如果在几次尝试后不成功,您可以终止循环。然后尝试另一种方法。平均而言,相等查找的改进的简单性和速度可以弥补偶尔的重试。

于 2012-11-07T02:16:14.263 回答
1

看来您基本上是在这里解决性能问题。大多数数据库性能专家建议您拥有与数据库大小一样多的 RAM,这样磁盘就不再是瓶颈——您的数据库位于 RAM 中并根据需要刷新到磁盘。

您基本上是在提议一个定制开发的内存中 CDC 散列系统。

如果您的数据库支持,您可以将其构建为仅限标准数据库的应用程序并将映射表锁定在 RAM 中。

我想我是说你可以在不开发自定义应用程序的情况下解决性能问题,只需使用现有的性能调整方法。

于 2012-11-07T03:34:52.023 回答