Memcached,锁定和竞争条件

时间:2009-10-22 17:54:04

标签: memcached

我们在写入数据库时​​尝试更新memcached对象,以避免在插入/更新后从数据库中读取它们。

对于我们的论坛帖子对象,我们有一个ViewCount字段,其中包含查看帖子的次数。

我们担心通过更新memcached对象来引入竞争条件,因为可以在服务器场中的另一台服务器上同时查看同一帖子。

任何想法如何处理这些问题 - 似乎需要某种锁定,但如何在服务器场中的服务器之间可靠地进行锁定?

4 个答案:

答案 0 :(得分:15)

如果您正在处理不一定需要实时更新的数据,并且对我来说视图计数就是其中之一,那么您可以在对象中添加一个到期字段存储在memcache中。

一旦到期,它将返回数据库并读取新值,但在此之前它将不管它。

当然,对于新帖子,您可能希望更频繁地更新,但您可以为此编码。

Memcache只在你的一个实例中存储你的对象的一个​​副本,而不是在很多实例中,所以我不担心对象锁定或任何东西。这是数据库要处理的,而不是你的缓存。

编辑:

Memcache不保证当您从各种服务器获取和设置时,您的数据不会被破坏。

来自memcache docs:

  • 一系列命令不是原子的。如果对项目发出'get',对数据进行操作,然后希望将其“设置”回memcached,则不能保证您是唯一处理该值的进程。同时,您最终可能会覆盖由其他内容设置的值。

竞争条件和陈旧数据

在设计应用程序以缓存数据时要记住的一件事是如何处理竞争条件和偶尔的陈旧数据。

假设您缓存最新的五条评论,以便在应用程序的侧栏上显示。您决定只需每分钟刷新一次数据。但是,你忽略了这个侧边栏显示每秒渲染50次!因此,一旦60秒滚动并且缓存过期,突然有10多个进程正在运行相同的SQL查询以重新填充该缓存。每次缓存过期时,都会突然出现SQL流量突发。

更糟糕的是,您有多个进程更新相同的数据,错误的进程最终会对缓存进行约会。然后你就会有过时的过时数据。

应该注意填充或重新填充缓存的可能问题。请记住,检查memcached,获取SQL以及存储到memcached中的过程根本不是原子的!

答案 1 :(得分:3)

我在想 - 可以解决方案是从Post对象中单独存储viewcount,然后对其进行INCR。当然,这需要在显示信息时从memcached中读取2个单独的值。

答案 2 :(得分:2)

我们在系统中遇到过这种情况。我们修改得到了

  • 如果未设置该值,则使用标志(' g')和[8]第二个TTL设置它,并返回false,以便调用函数生成它。
  • 如果该值未被标记(!==' g')则会反序列化并将其返回。
  • 如果该值已被标记(===' g'),请等待1秒钟再试一次,直至其未标记为止。它最终将由其他进程设置,或由TTL过期。

我们实施此操作时,我们的数据库负载下降了100倍。

function get($key) {
  $value=$m->get($key);
  if ($value===false) $m->set($key, 'g', $ttl=8);
  else while ($value==='g') {
    sleep(1);
    $value=$m->get($key);
  }
  return $value;
}

答案 3 :(得分:1)

memcached操作是原子的。服务器进程会对请求进行排队,并在进入下一个请求之前完全为每个请求提供服务,因此无需锁定。

编辑:memcached有一个增量命令, 原子。您只需将计数器作为单独的值存储在缓存中。