redis主从同步与故障切换的配置项可能出现的问题
redis主从同步的机制出现的问题:
第一:主从同步不一致
原因:主从库在命令传播阶段:主从库间的命令复制是异步进行的。
即主库将接受到的写命令发发送给从库后,就会去处理客户端的其他请求,而不会等到从库执行完写命令后在返回去处理其他请求,这时如果由于网络原因,从库没有及时从缓冲区读出主库的写命令,
而此时其他客户端访问该数据,得到的就是修改之前的数据,这样就会存在数据不一致的问题。另一方面如果从库接收到了命令,但是其在处理其他高复杂度的命令,也会造成主库传播来的命令延迟执行,
而在主库命令被滞后处理的这段时间内,主库本身可能又执行了新的写操作。这样一来,主从库间的数据不一致程度就会进一步加剧。
解决方案:
(1)在硬件环境配置方面,我们要尽量保证主从库间的网络连接状况良好
(2)因为主从库之间都会维护一个变量:主库(master_repl_offset)与从库(slave_repl_offset)所以当主从库之间如果master_repl_offset-slave_repl_offset的差值达到规定的量后,就可以让客户端与该从服务器关闭连接,
使得客户端避免从从库中读取数据,可以使用外部程序来监控主从库之间的复制进度,进行判断。监控程序可以一直监控着从库的复制进度,当从库的复制进度又赶上主库时,我们就允许客户端再次跟这些从库连接。
第二:因为过期数据出现的问题:
当我们为一个键设置过期时间后,但是在从库中我们还能读到过期的数据,这就与redis删除键的淘汰机制有关:
如果是惰性删除,那么只有在主库访问到过期数据后,才会执行删除,并将删除命令传播给从库
这样在数据过期后,但是为从主库中读,这样就能读到过期数据。
如果是定时删除,但是每次都会从过期键中抽取部分检查删除,每次随机检查数据的数量并不多。如果过期数据很多,并且一直没有再被访问的话,这些数据就会留存在 Redis 实例中。业务应用之所以会读到过期数据,这些留存数据就是一个重要因素。
如果是这样的话,解决方案:
:使用redis3.2之后的版本,redis3.2之后的版本如果读取键,会判断键是否过期,如果键过期那么就会返回空,但是不会删除键。
但是还有一个问题并不是使用3.2之后的版本就代表并不会读到过期数据,
如果对一个键执行EXPIRE 和 PEXPIRE:它们给数据设置的是从命令执行时开始计算的存活时间;那么由于从库传播阶段到执行阶段这之间的时间差,就会造成在从库,该键的过期时间被延后,那么在该键过期后,我们依然可以在一定时间内
从从库读到该数据,这样的话就会造成错误。
解决方案:
EXPIREAT 和 PEXPIREAT:它们会直接把数据的过期时间设置为具体的一个时间点。
故障切换不合理配置项出现的问题:
protected-mode 配置项
这个配置项的作用是限定哨兵实例能否被其他服务器访问。当这个配置项设置为 yes 时,哨兵实例只能在部署的服务器本地进行访问。当设置为 no 时,其他服务器也可以访问这个哨兵实例。
正因为这样,如果 protected-mode 被设置为 yes,而其余哨兵实例部署在其它服务器,那么,这些哨兵实例间就无法通信。当主库故障时,哨兵无法判断主库下线,也无法进行主从切换,最终 Redis 服务不可用。
我们在应用主从集群时,要注意将 protected-mode 配置项设置为 no,并且将 bind 配置项设置为其它哨兵实例的 IP 地址。这样一来,只有在 bind 中设置了 IP 地址的哨兵,才可以访问当前实例,既保证了实例间能够通信进行主从切换,
也保证了哨兵的安全性。(注意是protected-mode结合 bind一起使用)
slave-read-only 是设置从库能否处理写命令,slave-read-only 设置为 yes 时,从库只能处理读请求,无法处理写请求。slave-read-only 的作用,它主要用来控制 slave 是否可写,但是否主动删除过期 key,根据 Redis 版本不同,执行逻辑也不同。
1、如果版本低于 Redis 4.0,slave-read-only 设置为 no,此时 slave 允许写入数据,但如果 key 设置了过期时间,那么这个 key 过期后,虽然在 slave 上查询不到了,但并不会在内存中删除,这些过期 key 会一直占着 Redis 内存无法释放。
2、Redis 4.0 版本解决了上述问题,在 slave 写入带过期时间的 key,slave 会记下这些 key,并且在后台定时检测这些 key 是否已过期,过期后从内存中删除。
但是请注意,这
2 种情况,slave 都不会主动删除由 *master 同步过来带有过期时间的 key*。也就是 master 带有过期时间的
key,什么时候删除由 master 自己维护(即master执行del命令,并进行命令传播到从库),slave 不会介入。如果 slave 设置了 slave-read-only = no,而且是
4.0+ 版本,slave 也只维护直接向自己写入 的带有过期的 key,过期时只删除这些 key。
主从同步其余 2 个问题:
1、主从库设置的 maxmemory 不同,如果 slave 比 master 小,那么 slave 内存就会优先达到 maxmemroy,然后开始淘汰数据,此时主从库也会产生不一致。
2、如果主从同步的
client-output-buffer-limit 设置过小,并且 master 数据量很大,主从全量同步时可能会导致 buffer
溢出,溢出后主从全量同步就会失败。如果主从集群配置了哨兵,那么哨兵会让 slave 继续向 master 发起全量同步请求,然后 buffer
又溢出同步失败,如此反复,会形成复制风暴,这会浪费 master 大量的 CPU、内存、带宽资源,也会让 master 产生阻塞的风险。