【问题标题】:Memcached, Locking and Race ConditionsMemcached、锁定和竞争条件
【发布时间】:2009-10-22 17:54:04
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: memcached


    【解决方案1】:

    如果您处理的数据不一定需要实时更新,而对我来说,查看次数就是其中之一,那么您可以向对象添加 expires 字段存储在内存缓存中。

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

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

    Memcache 仅在其中一个实例中存储您的对象的一份副本,而不是在其中的许多实例中,因此我不会担心对象锁定或其他任何事情。那是由数据库来处理,而不是你的缓存。

    编辑:

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

    来自内存缓存文档:

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

    竞争条件和过时数据

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

    假设您缓存了最新的五个 cmets 以显示在应用程序的侧边栏上。您决定数据只需要每分钟刷新一次。但是,您忘记了,此侧边栏显示每秒渲染 50 次!因此,一旦 60 秒左右滚动并且缓存过期,突然 10 多个进程正在运行相同的 SQL 查询以重新填充该缓存。每次缓存过期时,都会导致 SQL 流量突然爆发。

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

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

    【讨论】:

    • 问题是我们希望实时更新观看次数(在这种情况下,但也有其他情况存在相同问题)——您点击帖子,观看次数就会增加。当然,出于性能原因,我们还希望缓存对象尽可能长地存在。
    • 你无法保证你正在尝试以你想要的方式去做。 memcache 为您提供的是可扩展性,而不是原始性能。
    【解决方案2】:

    我在想 - 解决方案是否可以将查看次数与 Post 对象分开存储,然后对其执行 INCR。当然,这需要在显示信息时从 memcached 中读取 2 个单独的值。

    【讨论】:

      【解决方案3】:

      memcached 操作是原子的。服务器进程将请求排队并在进入下一个之前完全处理每个请求,因此不需要锁定。

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

      【讨论】:

      • 是的,但在这种情况下,我们将获取项目,增加 ViewCount 并再次放置它,正如 Nathan 还指出的,这不是原子操作
      【解决方案4】:

      我们在系统中遇到了这个问题。我们修改了get so

      • 如果该值未设置,则使用标志 ('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;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-11-08
        • 2012-04-17
        • 2012-10-27
        • 1970-01-01
        • 1970-01-01
        • 2019-02-15
        • 1970-01-01
        • 2020-07-12
        相关资源
        最近更新 更多