redis面试:由于主从延迟导致读取到过期数据怎么处理
共 1609字,需浏览 4分钟
·
2022-08-24 17:57
读写分离,当使用从节点响应读请求时,业务端可能会遇到如下问题:
复制数据延迟
读到过期数据
从节点故障
复制数据延迟
因为主从库间的命令复制是异步进行的,所以有可能客户端从从客户读取到的主和主库中最新值不一致。
具体来说,在主从库命令传播阶段,主库收到新的写命令后,会发生给从库。但是,主库并不会等到从库实际执行完命令后,再把结果返回给客户端。如果从库还没有执行主库同步过来的命令,主从库键的数据就不会一致了。
从库滞后执行同步命令主要有两种情况
一方面,主从库间的网络可能会有传输延迟,所以从库不能及时收到主库发送的命令,从库上执行同步命令的时间就会被延后
另一方面,及时从库及时收到了主库的命令,但是,也可能会因为正在处理其他复杂度高的命令而阻塞
为了避免从库会滞后执行同步命令,方法如下:
首先,在硬件配置方面,我们要尽量保证主从库间的网络连接良好。比如,我们要避免把主从库部署在不同机房,或者是避免把网络通信密集的应用(比如数据分析应用)和redis主从库部署在一起。
另外,在主节点写入数据后立即在从节点上读取可能获取不到(由于网络带宽和命令阻塞),因此需要业务场景允许短时间内的数据延迟
如果无法容忍大量延迟场景,可以编写外部监听程序监听主从节点的复制偏移量,当延迟较大时触发报警或者通知客户端避免读取延迟过高的从节点。实现逻辑如下:
监控程序(monitor)定期检查主从节点的偏移量,主节点偏移量在info replication的master_repl_offset指标记录,从节点偏移量可以查询主节点的slave0字段的offset指标,它们的差值就是主从节点延迟的字节量
当延迟字节量过高时,比如超过10MB。监控程序触发报警并通知客户端从节点延迟过高。可以采用Zookeeper的监听回调机制实现客户端通知
客户端接到具体的从节点高延迟通知后,修改读命令路由到其他从节点或者主节点上。当延迟恢复后,再次通知客户端,恢复从基地的读命令请求
这种方案的成本比较高,需要单独修改适配Redis的客户端类库。如果涉及多种语言成本将会扩大。客户端逻辑需要识别出读写请求并自动路由, 还需要维护故障和恢复的通知。采用此方案视具体的业务而定,如果允许不 一致性或对延迟不敏感的业务可以忽略,也可以采用Redis集群方案做水平扩展
读到过期数据
redis的从库是无法主动的删除已经过期的key的,所以如果做了读写分离,就很有可能在从库读到脏数据。
当主节点存储大量设置超时的数据时,如缓存数据,Redis内部需要维护过期数据删除策略。删除策略主要有两种:惰性删除和定时删除。
惰性删除:主节点每次处理读取命令时,都会检查键是否超时,如果超时则执行del命令删除键对象,之后del命令也会异步发送给从节点。需要注意的是为了保证复制的一致性,从节点自身永远不会主动删除超时数据
定时删除:Redis主节点在内部定时任务会循环采样一定数量的键,当发现采样的键过期时执行del命令,之后再同步给从节点,如下图所示
如果此时数据大量超时,主节点采样速度跟不上过期速度而且主节点没有读取过期键的操作,那么从节点将无法收到del命令。此时在从节点上就可以读取到已经超时的数据。
如何避免从库读取到脏数据
通过scan命令扫库
当redis中的key被scan时,相当于访问了该key,同样也会做过期检测,充分发挥redis惰性删除的策略。这个方法能大大降低脏数据读取的概念。缺点也比较明显,会造成一定的数据压力。
升级到redis新版本
如果你使用的是 Redis 3.2 之前的版本,那么,从库在服务读请求时,并不会判断数据是否过期,而是会返回过期数据。在 3.2 版本后,redis做了改进,如果读取到的数据已经过期了,从库虽然不会删除,然后会返回新值,这就避免了客户端读到过期数据。所以,在应用主从集群时,尽量使用 Redis 3.2 及以上版本。