存储在内存中的PHP统计对象并发更新=数据丢失​​?

Gco*_*oop 2 php apc

我想抽象统计我的系统中的对象到一个地方.此时,每个对象在MySQL表中的行上增加一个计数器,即UPDATE sometable SET views = views + 1 WHERE id = ?

为了获得显着的性能提升,而不是每次使用对象时都将此更新写入数据库,我想使用apc将单个分析对象存储在此对象的内存增量计数器中,并定期将此对象保存回DB.

但是我正确地理解如果两个人访问同一页面并增加stats对象,那么如果他们都这样做可能会丢失其中一个视图,$obj->views = $obj->views + 1因为从apc获得的stats对象具有相同的总视图数,它们都是增加一个然后相互覆盖.

我该如何解决这个问题?

Pas*_*TIN 5

如果您采取三个步骤:

  • 从APC获取条目
  • 正在努力
  • 把它推回APC

然后,是的,您可能会遇到并发问题.


一个更好的解决方案是为每个计数器提供一个APC条目; 并使用该apc_inc()函数来递增它.

它可能意味着有许多条目(每个计数器一个) - 但它也意味着,在大多数注意事项中(当增加计数器时,这将是最多的工作),你将不必关心并发:APC将为你处理.


唯一应该保留的问题是你想要:

  • 获取计数器的值,
  • 将该值存储到数据库中,
  • 并重置计数器.

在那里,您可能会再次遇到并发问题.

两种解决方案

  • 考虑到这种情况不会再发生(与增量相比,此操作应该非常少见),并接受松散一点数据
  • 自己处理锁定机制:
    • 创建一个说"此计数器已锁定"的APC条目
    • 提取+ DB事物+重置
    • 取下锁
    • 与此同时,如果其他一些脚本想要递增计数器,它应该检查锁定条目是否存在; 如果是这样,请等待几毫秒,直到锁定被移除.


作为几个旁注,现在:

  • 你可以用memcached做同样的事情,那天你需要几个web服务器 - 见 Memcache::increment
  • 在这两种情况下,APC和memcached都是缓存机制,而不是持久的数据存储!这意味着他们可以在需要时驱逐您的数据(当他们没有足够的内存留给新条目,或者当您的条目变得太旧时会发生这种情况)

对于第二点,您可能希望切换到另一种解决方案,即:

  • 比MySQL快
  • 坚持到磁盘