Users on my site为说唱歌词创建注释(example)。我想创建一个排行榜来奖励那些创建了最多注释的人。
排行榜应该跟踪每个用户总共创建了多少注释,以及他在过去的一周、一天等创建了多少注释。
我在实现整体排行榜方面没有问题:
@users = User.all
<table>
<tr>
<th>Contributor</th>
<th>Annotations</th>
</tr>
<% @users.sort_by{|u| u.annotations.size }.reverse.each do |u| %>
<tr>
<td><%= u %></td>
<td><%= u.annotations.size %></td>
</tr>
<% end %>
</table>但是当我尝试实现(比方说)每日记分板时,我在重复代码,并且操作非常慢(因为它必须迭代内存中的每个注释,而不是依赖于数据库排序/计数):
<table>
<tr>
<th>Contributor</th>
<th>Annotations</th>
</tr>
<% @users.sort_by{|u| u.annotations.select{|a| a.created_at > 1.day.ago }.size }.reverse.each do |u| %>
<tr>
<td><%= u %></td>
<td><%= u.annotations.select{|a| a.created_at > 1.day.ago }.size %></td>
</tr>
<% end %>
</table>实现每日/每周记分板的最佳方式是什么?
发布于 2010-01-03 04:15:38
排行榜作为一个整体的实现是一件痛苦的事情。根据我的经验,实际的实现是相当简单的,只是它们很难扩展。通常,你会发现自己不得不运行许多数据库查询,这对数据库来说是非常密集的。要处理每日/每周报告,您可能会查询日期时间列,但这意味着您在该列上有一个索引。该索引实际上只对排行榜查询有用,它会使该表上的所有其他写操作付出代价,因为必须重新计算索引。
另一种方法是按计划的时间间隔生成统计数据,然后将该数据写入一个单独的表,该表供排行榜查询使用。例如,你有一个后台作业,每晚你运行一个查询(这可能是一个昂贵的,因为它不使用datetime索引,但因为它只运行一次,通过一个后台作业的费用是"ok"),该查询反过来写到一个统计表,确实有一个索引的datetime列,然后你重写您的排行榜页面,以达到您的预先计算的统计数据。根据您的需要,您可能还会让cron脚本执行其他数据转换和预计算,因此排行榜页面必须尽可能少地进行计算。
在这一点上,您的排行榜页面正在工作,虽然它命中一个带有索引的表,但它仍然必须读取大量的行。这是假设你有不错的流量。让索引查询命中每页上的大量行仍然是很昂贵的。因此,现在您可以考虑实现页面缓存,也许将数据存储在memcached中。也就是说,由于每天排行榜数据至少每天都会发生变化,根据定义,在每个页面视图上重新运行这些DB查询的代价很高。在memcached中缓存日常数据更有意义,每个页面视图只访问memcached。
所以你可以看到这是一个进化的过程。如果您的流量低于,那么您可以不使用单独的表,只在datetime列上建立索引。运行和,计数和平均值可能是可以的。但它不能扩展。因此,您必须考虑将其分解为一个更优化的结构。然后,您会发现,在底层数据在24小时内保持不变的情况下,每天反复运行相同的查询是非常昂贵的,因此您转向了缓存设置。有许多运动部件,它可能会变得复杂,嗯,真的很乏味的快速。
当涉及到排行榜时,我是一个久经沙场的愤世嫉俗者,虽然它们对游戏机制和激励人很有帮助(每个人都喜欢看到分数!)做大规模的工作是件很痛苦的事情。
发布于 2010-01-03 04:16:32
您是否考虑过将这些统计信息保存在由观察者更新的单独的表/模型中?您在这里的视图中做了大量繁重的工作,这通常不是一个好的实践。
发布于 2011-06-17 14:53:52
我建议使用Redis。您可以运行cron类型的任务,从数据库中提取数据,然后将其放入Redis排序集中。排序集功能可能是存储排行榜的最好工具。http://redis.io/topics/data-types
https://stackoverflow.com/questions/1992608
复制相似问题