这个问题涉及计数器的两种实现,它们旨在在不进行分片的情况下进行扩展(在某些情况下它们可能会低估计数):
-
http://appengine-cookbook.appspot.com/recipe/high-concurrency-counters-without-sharding/(代码在评论里)
- http://blog.notdot.net/2010/04/High-concurrency-counters-without-sharding
我的问题:
- 关于#1:跑步
memcache.decr()
在延迟的事务性任务中似乎有点矫枉过正。如果memcache.decr()
是在交易之外完成的,我认为最糟糕的情况是交易失败,并且我们错过了计算我们减少的任何内容。我是否忽略了这样做可能会出现的其他问题?
- 两种实现之间的重要权衡是什么?
以下是我看到的权衡:
-
2 不需要数据存储事务。
- 为了获取计数器的值,#2 需要获取数据存储,而 #1通常只需要执行
memcache.get()
and memcache.add()
.
- 当增加计数器时,两者都会调用
memcache.incr()
。 #2 定期将任务添加到任务队列,而 #1 以事务方式执行数据存储获取和放入。 #1也总是表现memcache.add()
(测试是否需要将计数器持久保存到数据存储中)。
结论
(没有实际运行任何性能测试):
-
1 通常应该更快地检索计数器(#1 内存缓存 vs #2 数据存储)。虽然 #1 必须执行额外的操作memcache.add()
too.
- 然而,#2 在更新计数器时应该更快(#1 数据存储 get+put 与 #2 排队任务)。
- 另一方面,对于 #1,您必须更加小心更新间隔,因为任务队列配额几乎比数据存储或 memcached API 小 100 倍。
访问数据存储可能比访问内存缓存更昂贵。否则,memcache 一开始就不会那么有用:-)
我推荐第一个选项。
如果你有一个合理的请求率,你实际上可以更简单地实现它:
1) update the value in memcache
2) if the returned updated value is evenly divisible by N
2.1) add N to the datastore counter
2.2) decrement memcache by N
这假设您可以在内存缓存上设置足够长的超时以在连续事件之间生存,但如果事件如此稀疏以至于您的内存缓存超时,那么您很可能不需要“高并发”计数器:-)
对于较大的站点,依靠单个内存缓存来执行诸如计算总页面点击次数之类的操作可能会给您带来麻烦;在这种情况下,您确实想要对内存缓存进行分片,并更新随机计数器实例;计数器的聚合将通过数据库更新进行。
不过,在使用 memcache 时,请注意某些客户端 API 会假设一秒超时意味着该值不存在。如果发送到 memcache 实例的 TCP SYN 数据包被丢弃,这意味着您的请求将错误地认为数据不存在。 (对于 memcache 的 UDP 可能会发生类似的问题)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)