Redis主从节点的弊端

Redis主从节点的弊端

在实际开发中Redis的节点部署大多数不是单机,基本采用集群的形式,而目前常见集群模式有三种主从模式、主从哨兵模式、分片集群模式,其中我们较为熟悉的应该是主从哨兵模式,采用一个主节点多个从节点,同时采用哨兵集群监控主从集群,从而使Redis高可用,实现高可用也带来了一些弊端如Redis在主从节点读取的数据不一致、从节点还能读取过期数据等等,下面分析一波。

主从数据不一致

主从数据怎么不一致呢?举例说明

当主节点的数据X=20,主节点接收到修改命令将主节点的数据X改为15,而从节点还是X=20这就造成了主从节点不一致的情况。

首先思考的是为什么主从数据会不一致呢?

我们知道在主从节点进行全量同步后,后续主从节点增量同步都是异步的方式,也就是说主节点接收到写命令后,会将命令记录到缓冲区replication buffer中,后续将缓冲区中的数据同步到从节点,同步示例图如下。

具体分析就是在主从库命令同步阶段,主节点收到新的写命令后,会发送给从节点,但是主节点并不会等待从节点执行完毕后再给客户端应答,而是主节点在自己的本地将命令执行完毕后,直接向客户端返回,如果从节点没有立即执行主节点同步过来的命令,那么这就造成了主从数据不一致的情况,那么什么原因会导致从节点没有马上执行主节点同步过来的命令呢?有如下原因

  • 主从节点的网络延迟,从库不能及时的收到主库发送的命令,自然导致从库执行命令的时间延后了。

  • 即使从节点收到了主节点同步过来的命令,但由于从节点在处理其它复杂的操作(如复杂的聚合操作,bigkey的读取,复杂的计算等等)而阻塞了主线程,这样也会导致从库执行命令延后。

如果出现了上述的两种情况我们应该如何避免呢?

  • 如果因为网络延迟,那么我们需要保证主从节点的网络良好,最好主从节点部署在一个机房里。

  • 第二种情况我们无法避免,那么只能说尽量规避,我们可以监控主从节点间的复制进度,当主从节点间的复制进度超过了我们的预期,我们应该尽量不使用该节点读取数据,我们可以在主节点上执行info replication命令得到主库写命令的进度master_repl_offset,以及从节点接收命令的进度slave_repl_offset,主从库之间的复制进度可以将master_repl_offset减去slave_repl_offset得到复制的进度差值,检测流程图如下

读取过期数据

这个思考点其实稍微有点奇怪,为什么说过期数据还能读取呢?首先我们思考我们设置一个有过期时间的键值,那么键值过期后是怎么删除的呢?目前是两种方式惰性删除和定期删除

  • 惰性删除:是当客户端请求键值时,对数据进行检查,如果发现这个数据过期那么再删除数据,这个方式对CPU友好,不会占据过多的CPU资源来删除数据,但是对内存不友好,因为已经过期的数据还占据内存显然是对资源的一种浪费。

  • 定期删除:也叫主动删除,Redis将每隔一段时间默认(100ms),随机选取一定数量的数据,检查它们是否过期,并把其中过期的数据删除,这样就能定期主动释放一些内存。

读取过期数据情况一

我们在了解两种键值删除方式后,对_客户端为什么读取过期数据_这个问题应该也就有思路了,首先我们注意到定期删除方式时,每次是选取一定数量的键值,那么也就是说如果过期的键值数据量巨大,那么Redis不可能一次性将所有过期的键值淘汰,必须分为多次,这时去访问已经过期的键值是有可能访问到已过期数据的。

读取过期数据情况二

其次惰性删除,必须要访问键值后才能检查是否过期,过期将被删除,但如果客户端访问的是从节点呢?从节点默认不具备删除功能,那么客户端在从节点访问过期数据是有可能访问到过期数据的,从节点只有接收到主节点的删除命令才会删除键值

那么从节点会返回过期数据吗?这是根据Redis版本来决定的,如果Redis的版本是3.2以下的,那么从节点在进行读请求时,是不会校验键值是否过期的,有可能返回过期数据,如果是3.2之后的版本那么读请求会检查过期时间,注意这里和主节点的处理方式不同如果读取的键值是过期的从节点会返回空值,但不会删除该键值,只有接收到主节点的删除命令才会删除

所以在使用主从时最好采用3.2之后的版本,那么3.2之后就不会读取到过期数据了吗?那必然不是的,还有一种场景

读取过期数据情况三

Redis给我们提供了两种设置过期时间的方式

  • EXPIREPEXPIRE:设置键值存活时间的秒、毫秒数。

  • EXPIREATPEXPIREAT:设置键值过期时间为具体的时间戳,单位秒、毫秒。

具体是什么意思呢?命令演示如下

### 设置2022-06-13 20:26:50过期
127.0.0.1:6379> EXPIREAT name 1655123210
(integer) 1

### 设置10s后过期
127.0.0.1:6379> EXPIRE name 10

当主从节点开始进行全量同步时,主节点接收到命令EXPIRE name 10给一个键值设置过期时间,这时主节点会等主从节点全量同步完毕后,才向从节点发送全量同步期间接收到的命令EXPIRE name 10

如果全量同步时间长,假设主节点执行命令时间为2022/06/13 23:16:00那么主节点name的过期时间应该为2022/06/13 23:16:10,而如果主从节点全量同步完毕需要两分钟那么从节点执行命令的时间为2022/06/13 23:18:00,从节点name过期时间就为2022/06/13 23:18:10,这时就造成了主从节点数据不一致的情况。

这种情况一般建议使用EXPIREAT命令,明确指出键值的具体过期时间点,但也要注意需要保证主从节点的时钟基本一致,不然时钟跳跃一样会导致数据不一致的情况。

主从节点配置注意点

slave-serve-stale-data

  • 设置为yes,那么在主从复制中,从节点可以响应客户端请求(包括读写请求)。

  • 设置为no,那么在主从复制中,从节点将阻塞所有请求,有客户端请求时返回SYNC with master in progress,从节点只能响应如INFOSLAVEOF等命令。

这个配置和slave-read-only很像一定要注意区分。

slave-read-only

  1. 如果Redis版本低于4.0版本,将slave-read-only设置为no,此时从节点允许写入数据,但如果key设置过期时间这时从节点将查询不到过期数据,但并不会从内存中删除,这些key将占用内存无法释放。

  2. Redis4.0版本就解决了第一个问题,当salve写入带有过期时间的key值时,Redis将记录这个key值,并且后台定时检测这些key是否过期,过期从内存中删除。

一定一定注意,上面两种情况,从节点都不会主动删除主节点同步过来带有过期时间的key值,也就是说主节点同步过来的key值由主节点自己维护,从节点并不会介入(除非主节点同步删除命令到从节点)。

==就算从节点可以修改键值,也并不建议从节点修改==,这样可能造成数据不一致的情况,假设主从节点有键值a:stock,这时客户端1想将键值a:stock改为13给主节点发送了set命令,客户端2想将键值a:stock改为15给从节点发送了set命令,这时就造成了主从节点数据不一致的情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值