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

存储在内存中的PHP Stats对象并发更新=数据丢失?,php,apc,Php,Apc,我想把系统中对象的统计数据抽象到一个地方。此时,每个对象在MySQL表中的行上增加一个计数器,即updatesometable SET views=views+1,其中id=? 为了获得显著的性能提升,而不是每次使用对象时都将此更新写入DB,我想使用apc将singleton analytics对象存储在此对象内的内存增量计数器中,并定期将此对象保存回DB 然而,我的理解是对的,如果两个人访问同一个页面并增加stats对象,那么如果他们都$obj->views=$obj->views+1,其中一

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

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

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


我该如何解决这个问题?

如果您分三步行动:

  • 从APC获取条目
  • 正在努力
  • 将其推回APC
那么,是的,您可能有并发问题


更好的解决方案是为每个计数器设置一个APC条目;并使用函数将其递增

这可能意味着有多个条目(每个计数器一个)——但这也意味着,在大多数情况下(当增加计数器时,这是最容易做到的),您不必关心并发性:APC将为您处理这一问题


唯一需要解决的问题是您何时需要:

  • 获取计数器的值
  • 将该值存储到数据库
  • 然后重置计数器
在这里,您可能会再次遇到并发问题

两种解决方案:

  • 考虑到这种情况不会再次发生(与增量相比,此操作应该非常罕见),并接受释放一点数据
  • 自己处理一个锁定机制:
    • 创建一个显示“此计数器已锁定”的APC条目
    • 执行抓取+数据库操作+重置
    • 拆下锁
    • 同时,如果其他脚本想要增加计数器,它应该检查锁条目是否存在;如果是这样,等待几毫秒,直到锁被移除

作为两个旁注,现在:

  • 在需要几个web服务器的那一天,您可以使用memcached做同样的事情——请参阅
  • 在这两种情况下,APC和memcached都是缓存机制,而不是持久数据存储!这意味着他们可以在需要时逐出您的数据(当他们没有足够的内存用于新条目时,或者当您的条目变得太旧时,就会发生这种情况)
对于第二点,您可能希望切换到另一个解决方案,即:

  • 比MySQL更快
  • 保存到磁盘

事实上,问题中的示例将覆盖先前的更改。除此之外,这是一个非常好的解决方案(对于数量较少的纯计数器也可以)。Memcached中的另一个解决方案(与APC相反)是使用的能力,如果对更复杂的变量/对象进行了更改,可以防止覆盖。