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

红色的Redis跟普通数据库不太一样,数据一致性那些事儿聊聊

基于对Redis官方文档的常见解读、技术社区如Stack Overflow的相关讨论、以及《设计数据密集型应用》一书中关于内存与持久化数据库的对比章节的综合理解)

红色的Redis,那个标志性的logo让人一眼就能记住,但它确实和我们平时用的那些关系型数据库,比如MySQL、PostgreSQL不太一样,最根本的不同,就像是你用一张瞬时的便签纸和一本正经的记账本的区别,普通数据库是那个记账本,每一笔开销都工工整整地记下来,确保不会出错;而Redis更像是你手边那张便签纸,写东西飞快,读取也飞快,但可能一阵风过来就给吹跑了,咱们今天就不聊那些复杂的“主从复制”、“CAP定理”了,就说说这种不同带来的数据一致性上那些事儿。

Redis为了追求那种闪电般的速度,选择把数据主要放在内存里,内存的读写速度是硬盘的几百上千倍,这也就是为什么Redis能轻松处理每秒几十万次的请求,但内存有个天大的问题:一断电,数据就没了,你写在便签纸上的东西,电脑一关机,可能就没了,普通数据库则不同,它们的设计哲学是“持久化第一”,任何数据写入,都必须先安全地落到硬盘上才算成功,就像你把账记在本子上,本子不掉,账就一直在。

那Redis怎么解决“怕断电”这个问题呢?它提供了两种主要的“把便签纸内容抄到本子上”的机制,但这两种机制恰恰是很多一致性问题的源头,一种叫RDB,你可以理解为“拍快照”,Redis隔一段时间,比如5分钟,就把内存里所有的数据完整地复制一份存到硬盘上,这种方式效率高,文件小,但问题是你可能会丢失最近5分钟内写入的所有数据,就像你每隔5分钟才抄一次便签纸,万一在第4分59秒断电,这5分钟的工作就白干了。

红色的Redis跟普通数据库不太一样,数据一致性那些事儿聊聊

另一种叫AOF,相当于“记流水账”,Redis会把每一个写命令都记录在一个文件里,这样即使断电,重启后重新执行一遍这个流水账,理论上能恢复到最新的状态,听起来很完美对吧?但这里又有权衡,为了保证少丢数据,你可以设置让Redis每秒钟把流水账刷一次到硬盘,但这依然可能丢失最后一秒的数据,如果你设置成每次写命令都立刻刷盘,那安全性是最高了,但速度就会急剧下降,因为写硬盘太慢了,这就有点背离Redis追求速度的初衷了。

你看,从最基础的持久化层面,Redis就存在数据丢失的风险,这在普通数据库里是很少见的,它们通常保证一旦告诉你“写入成功”,数据就一定在硬盘上了,这是第一种“不一致”:你的应用可能以为数据已经存好了,但实际上服务器突然宕机,数据就没了。

红色的Redis跟普通数据库不太一样,数据一致性那些事儿聊聊

再来聊聊更复杂点的情况:多个客户端同时操作Redis,在普通数据库里,你可以用“事务”(Transaction)来保证一系列操作要么全做,要么全不做,中间不会被别人打断,这提供了很强的一致性,Redis也有个叫“事务”的东西,但它和数据库的事务根本不是一回事,Redis的事务更像是一个“命令打包”,它只是确保这一批命令会一个接一个地顺序执行,中间不会被其他客户端的命令插队,但它不具备“回滚”能力,也就是说,如果这批命令里第三个命令出错了,Redis不会自动取消前两个已经执行成功的命令,它还会继续执行后面的命令,这可能会导致数据处于一种奇怪的不一致状态。

在分布式环境下,为了应对高并发和容灾,我们常常会用多个Redis实例组成一个集群,这时候,数据会被分片存储在不同的实例上,当你写入数据时,它可能只被写到了其中一个实例上,然后需要一点点时间才能同步到其他的副本实例上去,在这段极短的同步窗口期内,如果你去读另一个副本,就可能读到旧的数据,普通数据库的集群虽然也有类似问题,但它们通常提供了更严格的读一致性级别让你选择,而Redis在这方面更倾向于保证可用性和分区容错性,在一致性上做了妥协。

红色的Redis就像是一个天赋异禀的短跑冠军,速度无人能及,非常适合用来做缓存、存会话、搞实时排行榜这些对速度要求极高、但允许偶尔丢一点数据或者读到一个旧数据的场景,而普通数据库则像是一位严谨的马拉松选手,速度不占优,但耐力好,每一步都踩得扎实,保证数据的万无一失,适合存储像订单、账户余额这样的核心数据。

聊Redis的数据一致性,核心就是理解它在速度、持久化和分布式能力上所做的权衡,它不是不关心一致性,而是在这个“鱼与熊掌不可兼得”的世界里,它优先选择了速度和简单性,用对了场景,它就是神兵利器;用错了场景,比如你非要用它来存银行的交易记录,那可能就会带来大麻烦,关键就在于,你得清楚你手里的便签纸和记账本,分别该在什么时候用。