当前位置:首页 > 问答 > 正文

Redis集群一致性老问题,这次有了新办法,聊聊怎么解决的

(引用来源:阿里云开发者社区,Redis中国用户组CRUG)

今天咱们来聊一个Redis集群的老大难问题:一致性,说白了,就是当你往Redis集群里写一个数据,怎么能保证你立刻能读到这个最新的数据?听起来好像很简单,但这事儿在分布式系统里,尤其是在Redis这种追求高性能的架构下,还真没那么容易。

老问题的核心:主从延迟的“锅”

Redis集群为了高可用和扩展性,通常采用主从模式,一个主节点负责写,多个从节点负责读,这样可以分摊压力,问题就出在这里,当你向主节点写入一个键值对,set user:123 "张三",主节点会立刻返回成功,但主节点需要时间把这个写操作同步给它的所有从节点,这个同步过程是异步的,需要一点点时间,可能就几毫秒。

就是这几毫秒,成了“魔鬼时间窗口”,如果你的应用在写入成功后,立马去一个还没来得及同步新数据的从节点上读取 user:123,那么你读到的可能就是旧的空值或者旧的名字,而不是刚刚写入的“张三”,这就出现了数据不一致的情况,明明提示修改成功了,一刷新页面还是老样子,体验非常糟糕。

Redis集群一致性老问题,这次有了新办法,聊聊怎么解决的

过去的“土办法”和它们的局限

为了解决这个问题,大家想了很多办法,但都有点“伤敌一千,自损八百”的感觉。

  1. 强制读主节点:最简单的办法就是,所有写操作和可能涉及刚写入数据的读操作,都强制发往主节点,这样肯定能读到最新的数据,但这就完全丧失了读写分离的意义,主节点的压力会非常大,集群扩展读能力的好处也没了。
  2. 等待同步:写操作之后,让应用层等待一小段时间,比如100毫秒,估计数据同步完了再去读,这个方法很“玄学”,等短了可能还没同步完,等长了又严重影响性能,而且网络抖动一下,等待时间就更难估摸了。
  3. 半同步:有些数据库支持半同步复制,要求主节点必须成功同步数据到至少一个从节点后,才向客户端返回成功,这确实能保证强一致性,但代价是写入延迟显著增加,因为要等网络往返,Redis本身的设计哲学是极致性能,官方版本很长时间内没有原生支持这种模式。

新办法:Redis的“因果一致性”解决方案

既然老办法都不够优雅,那有没有更好的新办法呢?有的,这就是近年来被广泛讨论和实践的“因果一致性”模型,它不强求所有节点在任何时刻都完全一致,而是保证:如果操作B因果依赖于操作A(比如先修改再查询),那么系统必须保证B能看到A的结果。

Redis集群一致性老问题,这次有了新办法,聊聊怎么解决的

在Redis的语境下,新办法的核心思想是:让客户端“自己最后的写操作是在哪个主节点上完成的。

(引用来源:Redis Labs官方博客,关于Redis Edge的讨论)

具体怎么实现呢?一种越来越流行的做法是借助Redis的“客户端缓存”功能(Client-side Caching),并结合一种叫“因果令牌”(Causal Token)的机制,流程大概是这样的:

  1. 写入与令牌下发:当客户端向主节点成功写入一个键时,一种更优雅的解决方案出现了,它被称为因果一致性(Causal Consistency),并在一些云服务商提供的Redis版本或代理层中得以实现。

这个新办法的核心思想是:不让应用去“猜”数据什么时候同步完,而是让系统来“告诉”应用什么时候可以安全地读。

Redis集群一致性老问题,这次有了新办法,聊聊怎么解决的

具体是怎么做的呢?

  1. 给命令打标签:当客户端向主节点发起一个写操作时,主节点不仅处理命令,还会给这个命令分配一个全局递增的序列号(或时间戳),这个序列号代表了操作的先后顺序。
  2. 客户端记住标签:客户端在收到写入成功的响应时,也会收到这个最新的序列号,并把它缓存起来。
  3. 带着“凭证”去读:这个客户端发起读请求时,会将这个最新的序列号一并发送给任何一个从节点
  4. 从节点“担保”一致性:从节点收到带有序号的读请求后,它不会立刻返回数据,它会检查自己当前的同步状态,只有当自己的数据已经同步到了这个序列号所对应的写操作之后,才会执行读操作并返回结果,如果还没同步到,它会等待,直到同步完成再响应。

这样一来,就完美地解决了问题,对于执行了写入操作的客户端来说,它发起的后续读请求,一定能读到刚才写入的数据,保证了会话内的因果一致性,其他没有进行写入操作的客户端,依然可以从从节点读取数据(可能读到稍旧的数据,但这是可接受的最终一致性),整个集群的读性能依然得到保障。

总结一下

这个新办法妙就妙在,它没有粗暴地牺牲性能来换一致性,而是通过在客户端和服务器之间建立一个轻量的“约定”机制,聪明地规避了主从延迟带来的不一致问题,它承认延迟的客观存在,但通过技术手段确保了在需要的时候,延迟不会影响到用户体验。

Redis集群这个老问题,现在有了更智能、更优雅的新答案,技术的发展,正是在解决这些看似微小却至关重要的细节中,不断前进的。