摘要
与一些同事交谈时,我们遇到了“从大数据库表中提取随机行”的问题。这是一个经典的方法,我们知道天真的方法(also on SO)通常是这样的:
从mytable order by rand()限制1中选择*
问题
我们还知道这样的查询是完全低效的,实际上只能在很少的行中使用。有一些方法可以用来获得更好的效率,比如these ones仍然是这样,但是它们不会对任意主键起作用,一旦你的数字主键上有洞,随机性就会被扭曲。最后一个被引用的问题的答案链接到this article,它有一个很好的解释和一些很好的解决方案,涉及一个附加的“等分布”表,每当“主数据”表发生变化时必须维护该表。但是,如果您经常删除一个大表,您可能会被不断更新添加的表搞砸。还要注意,许多解决方案都依赖于COUNT(*),这在myisam上是非常快的,但在innodb上是“非常快”的(我不知道它在其他平台上是如何运行的,但我怀疑innodb案例可能代表了其他事务性数据库系统)。
除此之外,即使是我能找到的最好的解决方案也很快,但不是可笑的速度快。
这个想法
单独的服务可以负责生成、缓冲和分发随机行ID,甚至是整个随机行:
它可以根据原始pks的结构选择最佳的随机行id提取方法。服务可以在ram中维护一个有序的密钥列表(除了pk的实际大小之外,每行不应该占用太多字节,对于标准pc,最多100~1000m行是可以的,对于健壮的服务器,最多10~100亿行是可以的)
一旦密钥在内存中,每个密钥都有一个隐式的“行号”,并且没有任何漏洞,因此只需选择一个随机数并直接获取相应的密钥
可以维护一个随时可用的随机密钥缓冲区,以快速响应传入请求中的峰值
服务的使用者将连接并从缓冲区请求n个随机行
行作为简单键返回,或者服务可以维护一个(池)数据库连接来获取整行
如果缓冲区是空的,则请求可以阻塞或返回类似于
如果将数据添加到主表中,则必须通知服务也将相同的数据添加到其副本中,刷新随机选取的缓冲区并从中继续
如果从主表中删除了数据,则必须通知服务也从“所有键”列表和“随机选取”缓冲区中删除该数据
如果主表中的数据已更新,则必须通知服务更新密钥列表和随机选取中的相应行
为什么我们觉得很酷
在启动时或发出信号时,不要触摸除初始加载密钥以外的磁盘
与任何类型的主键一起工作,无论是否为数字键
如果你知道你要更新一大批数据,你只需在完成时发出信号(即不是每次在原始数据上插入/更新/删除),这基本上就像拥有一个细粒度的锁,它只阻塞对随机行的请求
对原始数据中的任何类型的更新都非常快
将一些工作从关系数据库转移到另一个仅限内存的进程:有助于可伸缩性
从缓冲区快速响应,无需等待任何查询、扫描、排序
可以很容易地扩展到sql之外的类似用例
为什么我们认为这是个愚蠢的主意
因为我们没有第三方的帮助
因为没人(我们听说过)费心做类似的事
因为它增加了混合的复杂性,以便在原始数据更改时保持更新
问题是…
类似的东西已经存在了吗?如果没有,是否可行?如果不是,为什么?
最佳答案
“合格主键缓存”概念的最大风险是在源数据不断更改时保持缓存最新。保持缓存同步的成本可能与对原始数据运行随机查询的成本一样高。
您希望如何向缓存发出已添加/删除/更新值的信号?如果使用触发器执行此操作,请记住,即使生成触发器的事务回滚,触发器也会触发。这是从触发器通知外部系统的一般问题。
如果在数据库中提交更改后从应用程序通知缓存,则必须担心其他应用程序在没有安装信令代码的情况下进行更改。或特别查询。或来自无法更改代码的应用程序或工具的查询。
一般来说,增加的复杂性可能不值得。大多数应用程序可以容忍一些妥协,它们不需要一直随机选择。
例如,对于某些需要,不等式查找可能是可以接受的,即使存在已知的弱点,即在间隔之后选择数字的频率更高。
或者可以预先选择少量随机值(例如30)并缓存它们。让应用程序请求从中选择。每隔60秒左右,用另一组随机选择的值刷新缓存。
或者选择一个平均分布在min(id)和max(id)之间的随机值。尝试按相等而不是不相等查找。如果该值对应于主键中的间隙,只需循环并使用其他随机值重试。如果尝试几次后循环不成功,则可以终止循环。然后改用另一种方法。平均来说,平等查找的改进的简单性和速度可以弥补偶尔的重试。
关于mysql - 从给定的SQL表中提供随机元素的服务是否可行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13262068/
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru
我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..
这个问题在这里已经有了答案:Checktoseeifanarrayisalreadysorted?(8个答案)关闭9年前。我只是想知道是否有办法检查数组是否在增加?这是我的解决方案,但我正在寻找更漂亮的方法:n=-1@arr.flatten.each{|e|returnfalseife
最近,当我启动我的Rails服务器时,我收到了一长串警告。虽然它不影响我的应用程序,但我想知道如何解决这些警告。我的估计是imagemagick以某种方式被调用了两次?当我在警告前后检查我的git日志时。我想知道如何解决这个问题。-bcrypt-ruby(3.1.2)-better_errors(1.0.1)+bcrypt(3.1.7)+bcrypt-ruby(3.1.5)-bcrypt(>=3.1.3)+better_errors(1.1.0)bcrypt和imagemagick有关系吗?/Users/rbchris/.rbenv/versions/2.0.0-p247/lib/ru
在Rails4.0.2中,我使用s3_direct_upload和aws-sdkgems直接为s3存储桶上传文件。在开发环境中它工作正常,但在生产环境中它会抛出如下错误,ActionView::Template::Error(noimplicitconversionofnilintoString)在View中,create_cv_url,:id=>"s3_uploader",:key=>"cv_uploads/{unique_id}/${filename}",:key_starts_with=>"cv_uploads/",:callback_param=>"cv[direct_uplo
我有一个包含多个键的散列和一个字符串,该字符串不包含散列中的任何键或包含一个键。h={"k1"=>"v1","k2"=>"v2","k3"=>"v3"}s="thisisanexamplestringthatmightoccurwithakeysomewhereinthestringk1(withspecialcharacterslike(^&*$#@!^&&*))"检查s是否包含h中的任何键的最佳方法是什么,如果包含,则返回它包含的键的值?例如,对于上面的h和s的例子,输出应该是v1。编辑:只有字符串是用户定义的。哈希将始终相同。 最佳答案
我需要检查DateTime是否采用有效的ISO8601格式。喜欢:#iso8601?我检查了ruby是否有特定方法,但没有找到。目前我正在使用date.iso8601==date来检查这个。有什么好的方法吗?编辑解释我的环境,并改变问题的范围。因此,我的项目将使用jsapiFullCalendar,这就是我需要iso8601字符串格式的原因。我想知道更好或正确的方法是什么,以正确的格式将日期保存在数据库中,或者让ActiveRecord完成它们的工作并在我需要时间信息时对其进行操作。 最佳答案 我不太明白你的问题。我假设您想检查
我的日期格式如下:"%d-%m-%Y"(例如,今天的日期为07-09-2015),我想看看是不是在过去的七天内。谁能推荐一种方法? 最佳答案 你可以这样做:require"date"Date.today-7 关于ruby-检查日期是否在过去7天内,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/32438063/
这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下