确保(分布式)缓存只存储分布式系统中的最新值

时间:2021-06-21 06:38:49

标签: redis memcached distributed-system

假设我想使用诸如 Redis 或 Memcached 之类的内置解决方案来缓存数据库行(例如),以避免经常性地访问数据库,代价高昂。

为了论证起见,我们假设我有一个 TABLE(id, x, y) 并且我想缓存所有行,这样我就不必直接从数据库中读取数据。

问题:

  1. 考虑以下情况:NodeA 尝试更新给定行的字段 x,而 NodeB 尝试更新 y,然后两者同时尝试更新缓存行。如果他们尝试“手动”更新他们刚刚更改为缓存中的行的字段,如果我们遵循典型的最后写入获胜,那么其中一个字段将被丢弃,这是灾难性的。这让我觉得我需要总是用从数据库读取的完整行来填充缓存的行。
  2. 但这本身并不一定对我有帮助。如果 NodeA 写入 x 并将整行加载到内存中,然后 NodeB 写入 y 并读取内存中的整行,如果 NodeB 在 NodeA 之前写入缓存,则 NodeB 的更改将被覆盖!这让我相信我需要始终以某种方式对数据库和缓存中的行进行版本控制。是这种情况吗? Memcached 似乎有一个比较和设置原语,但我在 Redis 中没有看到这样的东西。
  3. 即使 1. 和 2. 不是问题,我仍然需要保证我的写/读具有 read-after-write 一致性,否则可能会发生我正在阅读并打算放入缓存中的内容不一定是最新版本。如果是这种情况,我如何确保这一点?通过要求 w + r > n?

这似乎是一个非常常见的用例,我猜这几乎是一个已解决的问题。我可以尝试什么来解决这个问题?

2 个答案:

答案 0 :(得分:1)

作为 redis 的键值存储支持高级数据结构,例如 HASH。

如果您对缓存实体进行部分更新(仅更新一组字段作为超集的一部分),并且您的目标是避免耗时的数据库读取,只需将表条目保存为 HASH K/V 对(使用 HSET)和使用 HGETALL 进行读取。

Redis OPS 本质上是原子的,所以应该可以解决你的问题,如果我做对了。

附带说明一下,如果您缓存整个实体但进行部分更新,则应考虑使用更简单的缓存方法,例如通读(使缓存有效性成为只读问题)。

与数据库访问相反。除非以某种方式序列化,否则来自不同位置的 Redis 缓存访问在分布式系统中总是有可能出现乱序,因为执行环境(网络、线程)总是会引入可能的延迟。

进行通读缓存将确保在最近一次写入后始终更新数据,而无需同步任何其他内容。

答案 1 :(得分:0)

Facebook 是这样用 Memcached 解决这个问题的:http://nil.csail.mit.edu/6.824/2020/papers/memcache-faq.txt

这个想法是使用租约的概念:当收到对缓存值的请求并且没有此类密钥的数据时,返回租用令牌(64 位 id)。

当网络服务器从数据库中获取数据时,它可以使用该令牌将数据存储在缓存中。每次对密钥调用失效请求时,都会创建一个新的租用令牌,因此,如果尝试对旧令牌进行放置,则放置最终会被拒绝。

据我所知,如果不借助 LUA 脚本,就不可能(轻松)使用 Redis 复制这种行为。