31.Redis主从同步和故障切换的坑

今天我们说一下主从集群下可能出现的坑

分别是主从数据不一致,读到过期数据,配置项设置不合理导致服务挂掉

我们会分别说明出现的原因和对应的解决方案

首先是主从数据不一致

主从数据不一致,指的是客户端从从库中读取到的值和主库中最新的值不一致

比如说,主库上的一个用户年龄为19,这时候,主库收到了命令,将数据更新到了20,然后去读取从库,还是可能读取到值为19的数据,这就是主从不一致的坑

这个坑的出现情况,

1.是由于主从库间的命令复制是异步而导致的

主从库在命令传播过程中,主库收到新的命令后,会发给从库执行,但是并不会等待从库执行完成

如果从库收到读请求的时候,还没执行主库发来的新命令,就会出现读取数据不一致的问题了

那么导致这个问题最显著的地方,就是网络传输,因为网络传输过程中的延迟,所以从库并不能及时的收到主库发的命令,导致从库执行延后

第二个可能导致的地方,就是从库收到了主库的命令

但是主库的命令过于复杂,以致于执行过程中从库阻塞,从库不断的阻塞,以致于主从同步越来越大

对于这两点的问题,其一,我们可以保证主从间网络连接状况良好,避免主从库部署在不同的机房中,避免将网络通信密集的应用和Redis主从库部署在一起

其次是利用中间件,进行监控主从库之间的复制进度,利用INFO replication命令查到主从库的进度,用master_repl_offset减去slave_repl_offset,得到从库和主库间复制进度差值

应用Redis的时候,可以周期性的运行这个流程来监控主从库之间的不一致性

图片

监控程序可以一直监控着从库的复制进度,从库复制进度赶上主库的时候,就允许客户端再次使用从库连接

对于阻塞命令,只能考虑从上层应用解决

然后是读取到过期数据

对于Redis主从急群中,有时候会读到过期数据,比如数据X的过期时间是202010240900,但是在202010240910,仍然可以从从库读到X,一个数据过期后,应该被删除的,客户端不应该能够读到这个数据,为何Redis还能读到过期数据呢?

这是由于Redis的过期数据删除策略引起的,Redis在删除的时候,会使用我们定义的LRU LFU来定期的删除,除此外还有着惰性删除的操作,定期删除只会抽取一部分的数据检查是否过期

而惰性删除,是在数据到期时间达到后,有请求读取这个数据,对数据进行检查,如果发现数据已经过期了,就删除这个数据

这个策略的好处就是减少删除操作对CPU资源的使用,对于用不到的数据,及时检查不到也不会影响使用

而从库本身不会执行删除操作,客户端访问过期数据的话,对于Redis,3.2版本之前的不会检查数据是否过期了,3.2版本之后,过期的数据会返回空值

但是即使定了过期时间,但是由于过期命令本身的局限性,还是可能被读取到

过期数据分为了EXPIRE和PEXPIRE,数据设置的是从命令执行时开始计算的存活时间

EXPIREAT和PEXPIREAT是将数据的过期时间设置为一个具体的时间点

参数的含义如下

图片

第一个例子是EXPIRE,将一个key的过期时间设置为60s后

EXPIRE key 60

第二个例子是EXPIREAT命令,我们执行下面的命令,让testkey在2020年10月24日上午9点过期

EXPIREAT testkey 1603501200

那么问题就出在EXPIRE命令中,如果主库直接执行这条命令,这条命令在同步之后,发给从库执行,从库会在执行之后的时间,会比主库延迟

这就是从库可能读到过期数据的原因

所以建议在业务应用中使用EXPIREAT/PEXPIREAT命令,将数据过期时间设置为一个具体的时间点,避免读到过期数据

最后就是配置不合理导致的集群问题

常见的配置不合理如下

1,protected-mode,这个配置是限定哨兵实例能否被其他的服务器访问

如果设置为yes的时候,哨兵实例只能被部署的服务器本地访问,这就导致主库故障的时候,哨兵无法客观进行判断,导致无法主从切换

2.cluster-node-timeout配置项

设置了Redis Cluster中实例相应心跳请求的超时时间

我们配置了一主多从的时候,如果主库发生故障,发生了切换,但是切换时间过长,会导致实例的心跳超时,故Redis Cluster为异常状态,谷可以考虑将cluster-node-timeout调大一些

3.主从库的maxmemory不一致,如果设置不同,可能从库先到maxmemory,开始淘汰数据

4.主从同步的client-output-buffer-limit设置的小,导致主从同步过程中,出现延迟导致覆盖,以致于只能从库重新全量同步,如此反复,占用了master大量的内存和CPU资源

5.slave-read-only,设置从库是否能够处理写命令,slave-read-only设置为yes,从库只能处理读请求

最后一个小问题,slave-read-only设置为no,从库直接删除数据,避免读到过期数据

这个问题有些没看懂,是直接从从库上删除数据吗,这应该是不被允许的把,现在在云上用K8S管理,也是设置了多个service,一个用来写一个用来读,从库只用来读取数据,不应该负责删除数据

而且,即使设置了过期时间,从库在达到之后也不会删除数据,只是返回空,只有接到主库删除操作才会进行删除吧

发表评论

邮箱地址不会被公开。 必填项已用*标注