实现动态更新upvote / downvote

时间:2014-02-06 10:05:24

标签: mysql architecture memcached voting-system

如何实现类似于quora的动态更新投票计数: - 每当用户提交答案时,每个人都会自动反映每个正在查看该页面的人。

我正在寻找一个解决以下问题的答案:

  • 我们是否必须为每个答案保持民意调查的计数,如果是的话 那么如何管理由于这么多用户而产生的服务器负载 投票赞成票。
  • 或者使用websockits /推送通知,这些可扩展性如何?
  • 如何在数据库/内存中存储upvote / downvote计数以支持此功能。它们如何控制读/写次数。我的后端数据库是mysql

我正在寻找的答案可能并不完全是quora如何做到这一点,但可能是如何使用可用的开源技术来完成。

3 个答案:

答案 0 :(得分:5)

Websockets,服务器发送事件(我认为这就是'推送通知'的含义)和AJAX长轮询具有相同的缺点 - 它们使基础TCP连接长时间保持打开状态。
所以问题是服务器可以处理多少个开放的TCP连接。 基本上,它取决于其操作系统,文件描述符的数量(配置参数)和可用内存(每个打开的连接保留读/写缓冲区)。 Here's more on that.

我们曾经测试过在一台服务器上打开100万个websocket连接的可能性(带有16Gb RAM的Windows 7 x64,带有8Gb堆的JVM 1.7,使用Undertow beta来提供Web请求)。 令人惊讶的是,最困难的部分是在服务器上产生负载 它成功地保住了1M。但是,服务器再次没有做一些有用的事情,只是收到请求,通过协议升级并保持这些连接打开。 无论出于何种原因,也有一些失去的联系。我们没有调查。但在生产中,您还必须ping服务器并处理重新连接。

除此之外,Websockets似乎有点矫枉过正,SSE still aren't widely adopted. 所以我会选择旧的AJAX轮询,但要尽可能地优化它 无处不在,易于实现和调整,不依赖于外部系统(我有几次不好的经验),优化的可能性。 例如,您可以在一个浏览器中为所有打开的文章分组更新,或根据文章的受欢迎程度调整更新间隔 毕竟,你似乎不需要这里的实时通知。

答案 1 :(得分:4)

这不是您需要担心的后端系统细节,而是前端。连接始终打开在任何实际规模上都是不切实际的。相反,你想要相反 - 能够尽可能快地从后端服务和关闭连接。

Websockets是一种性感技术,但在现实世界中还有issues with proxies,如果你正在开发适用于各种屏幕(桌面,平板电脑,移动设备)的东西,它可能会成为你关心的问题。即使是古老的长期民意调查也可能无法通过防火墙和代理工作。

这是一个好消息:我想

  

“保持对每个答案的upvote计数进行投票”

在这种情况下,

是一个非常好的解决方案。请考虑以下事项:

  • 您的用例不需要任何真实实时更新。稍后更新计数器几乎没有什么害处
  • 对于非常受欢迎的主题,您希望将多个投票/下选票压缩成一个
  • 大多数主题将在几天/几周内看不到向上/向下投票的流量,因此保持连接打开,等待从未发生的事件是浪费
  • 大多数用户永远不会投票/投票,只是来阅读一个主题,所以你的读/写主题统计数据将大大倾向于阅读
  • 网络延迟在客户端之间变化很大,你会看到100B http响应的可怕传输速率,而这个缓慢的客户端逐字节获取他的宝贵服务器连接的响应,更重要的是 - 后端的线程服务器正忙着

以下是我的开始:

  • 在主页加载后,浏览器会定期轮询新的主题统计信息
  • 保留你的MySQL,保留计数器。每次有上/下投票更新数据库
  • 将Memcached作为直写缓存放在DB前面,即每次有上/下投票更新缓存,然后更新DB。将计数器的显式到期时间设置为10-15分钟。每次更新计数器时,到期时间会自动延长。
  • 设计这些轮询http调用可以通过http代理缓存,设置expire和ttl http标头为60秒
  • 在您的前端服务器前放置一个反向代理(Varnishnginx),让此代理执行所述轮询调用的缓存。这些可以处理二级缓存并帮助更快地释放后端服务器线程,请参阅上面的网络延迟
  • 将您的反向代理组件设置为直接与memcached服务器通信,而无需调用后端服务器,是的,如果您可以同时使用Varnish和nginx。
  • 没有用于存储此类数据的花哨模式,这是memcached中的简单inc()/dec()操作,请注意,从竞争条件的角度来看,它是安全的。它也是MySQL UPDATE table SET field = field + 1 WHERE [...]
  • 中安全的原子操作

积极的多级缓存涵盖了read路径:在Memcached中以及沿途的所有http缓存中,请注意这些http轮询请求也将缓存在edges上。

要照顾不受欢迎的主题的长尾 - 使这些响应的http ttl与流行度成反比。

当http缓存过期且memcached也没有读取请求时,读取请求很少会到达前端服务器。如果仍然存在问题,请添加memecached服务器并在memcached中全面增加过期时间。

完成后,您已完成所有reads处理。根据比例,您可能仍然存在的唯一问题是writes的高速率,即上/下投票的流量。这是您的单个MySQL实例可能开始显示一些滞后的地方。不要害怕 - 按照分割实例的旧方法进行,或者只为计数器添加NoSQL存储。

除非绝对必要,否则不要使用任何邮件系统,或者您想借口使用它。

答案 2 :(得分:2)

听起来您可以使用像Kafka,RabbitMQ或ActiveMQ这样的消息传递系统。您的前端会将投票发送到消息通道并通过侦听器接收它们,并且您可以让服务器端部件定期将数据持久保存到数据库。

您还可以通过轮询数据库来完成您的任务,并通过存储过程增加/减少与帖子相关的数字...这里有很多选项,这取决于您可能有多少并发性面对。