Redis核心技术与实战【学习笔记】 - 23.Redis 主从切换故障,有哪些坑

news2024/9/21 4:25:26

前言

Redis 的主从同步机制不仅可以让从库服务更多的读请求,分担主库的压力,而且还能在主库发生故障时,进行主从库切换,提供高可靠服务。

不过,在实际使用主从机制时会踩到一些“坑”:主从数据不一致、读到过期数据以及配置项设置得不合理而导致服务挂掉。


主从数据不一致

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

假设,主从库之前保存的用户年龄是 19,但是主库收到了修改命令,已经把这个数据更新为 20 了,但是,从库中的值仍然是 19。那么,如果客户端从从库中读取用户年龄值,就会读到旧值。

因为主从库的命令复制是异步进行的

具体来说,在主从库命令传播阶段,主库收到新的写命令后,会发送给从库。但是,主库并不会等到从库实际执行完命令后,再把结果返回给客户端,而是主库自己在本地执行完命令后,就会向客户端返回结果了。如果从库还没有执行主库同步过来的命令,主从库间的数据就不一致了。

造成从库之后执行同步命令的原因有两个:

  • 一方面,主从库间的网络可能会有传输延迟,所以从库不能及时地收到主库发送的命令,从库上执行命令同步的时间就会被延后。
  • 另一方面,即使从库及时收到了主库的命令,但是,也可能因为正在处理的其他复杂度高的命令而阻塞。此时,从库需要处理完当前的命令,才能执行主库发送的命令,这就造成主从库数据不一致。而在主库命令被滞后执行的这一段时间内,主库本身可能又执行了新的写操作。这样一来,主从库间的数据不一致程度就会进一步加剧。

有两种解决办法:

  1. 在硬件配置方面,要尽量保证主从库间的网络连接状况良好。要避免把主从库部署在不同的机房,或者是避免把网络通信密集的应用和 Redis 主从库部署在一起。
  2. 开发一个外部程序来监控主从库间的复制进度。

因为 Redis 的 INFO replication 命令可以查看主库接收写命令的进度信息(master_repl_offset)和从库复制写命令的进度信息(slave_repl_offset),所以,我们可以开发一个监控程序,先用 INFO replication 命令查到主、从库的进度,然后,用 master_repl_offset 减去 slave_repl_offset,这样就能得到主库和从库间的复制进度差值了。

如果某个从库的进度差值大于我们预设的阈值,可以让客户端不再和这个从库就进行数据读取,这样就可以减少得到不一致数据的情况。不过,为了避免出现客户端和所有从库都不能连接的情况,我们要把复制进度差值的阈值设置得大一些。
在这里插入图片描述
当然,监控程序可以一直监控这从库的复制进度,当从库的复制进度又赶上了主库时,我们允许客户端再次跟这些从库连接。

读取过期数据

我们在使用 Redis 主从集群时,有时会读到过期数据。例如,数据 X 的过期时间是 202401010900,但是客户端在 202401010910 时,仍然可以从从库中读到数据 X。一个数据过期后,应该是被删除的,客户端不能再读取到该数据。

其实,这是有 Redis 的过期策略引起的。Redis 同时使用了两种策略来删除过期数据,分别是惰性删除策略和定期删除策略

  1. 惰性删除策略。当一个数据的过期时间到了以后,并不会立即删除数据,而是等到再由请求来读写这个数据时,对数据进行检查,如果发现数据已经过期了,再删除这个数据。

    这个策略的好处是尽量减少删除操作对 CPU 资源的使用,对于用不到的数据,就不在浪费时间进行检查和删除了。但是,这个策略会导致大量已经过期的数据留存在内存中,占用较多的内存资源。所以,Redis 在使用这个策略的同时,还使用了定期删除策略。

  2. 定期删除策略是指,Redis 每隔一段时间(默认 100ms),就会随机选出一定数量的数据,检查他们是否过期,并把其中过期的数据删除,这样就可以及时释放一些内存。

清楚了删除策略,看看他们为什么会导致读取到过期数据。

  • 首先,虽然定期删除可以释放一些内存,但是 Redis 为了避免过多删除操作对性能产生影响,每次随机检查数据的数量并不多。如果过期数据很多,并且一直没有再被访问,这些数据就会留存在 Redis 实例中。业务应用之所以会读到过期数据,这些留存数据就是一个重要因素。

  • 其次,惰性删除策略实现后,数据只有被再次访问时,才会被实际删除。如果客户端从主库上读取留存的过期数据,主库会触发删除,此时,客户端并不会读到过期数据。但是,从库本身不会执行删除操作,如果客户端在从库中访问的过期数据,从库并不会触发数据删除。

    那么,从库会给客户端返回过期数据吗?

    如果是 Redis 3.2 之前的版本,那么,从库在服务读取请求时,并不会判断是否过期,而是会返回过期数据。在,Redis 3.2 版本后,Reids 做了改进,如果读取的数据已经过期,从库虽然不删除数据,但是会返回空值,所以就避免了客户端读到过期数据。所以,在应用主从集群时,尽量使用 Redis 3.2 以上版本

另外,Redis 的过期命令也可能导致读取到过期数据。有些命令给数据设置的过期时间在从库上可能会被延后,导致应该该过期的数据又在从库上被读取到了。

设置数据过期时间的命令一个有 4 个,我们可以把他们分成两类:

  • EXPIREPEXPIRE :它们给数据设置的是从命令执行时开始计算的存活时间
  • EXPIREATPEXPIREAT它们会直接把数据的过期时间设置为具体的一个时间点
过期时间
设置命令
参数含义
EXPIRE[key] [ttl]将 key 的存活时间设置为 ttl 秒
PEXPIRE[key] [ttl]将 key 的存活时间设置为 ttl 毫秒
EXPIREAT[key] [timestamp]将key的过期时间设置为timestamp指定的秒数时间点
PEXPIREAT[key] [timestamp]将key的过期时间设置为timestamp指定的毫秒数时间点

再举两个例子。

第一个例子是使用 EXPIRE 命令,当执行下面的命令时,我们就把 testkey 的过期时间设置为 60s 后。

EXPIRE testkey 60

第二例子是使用 EXPIREAT 命令,例如,我们执行下面的命令,就可以让 testkey 在 2024年1月1日上午9点过期,命令中的 1673079000 就是以秒数时间戳表示的 2024年1月1日上午9点。

EXPIREAT testkey 1673079000

下面来看看这些命令是如何导致读到过期数据的。

当主从全量同步时,如果主库收到了 EXPIRE 命令,那么,主库会直接执行这条命令。这条命令会在全量同步完成后,发给从库执行。而从库在执行时,就会在当前时间的基础上加上数据的存活时间,这样一来,从库上的数据的过期时间就会比主库延后了。

假设当前时间是 2024年1月1日上午9点,主从库正在同步,主库收到了一条命令 EXPIRE testkey 60,这就表示,testkey 的过期时间就是 1日上午9 点 1 分,主库直接执行了这条命令。

  • 但是,主从库全量同步花了 2 分支才完成。等从库开始执行这条命令时,时间已经是 9 点 2 分了。而 EXPIRE 命令就是把过期时间设置为当前时间的 60s 后,也就是 9 点 3 分。
  • 如果客户端在 9 点 2 分 30 秒时在从库上读取 testkey,仍可以读取到 testkey 值。但是 testkey 实际已经过期了。

为了避免这种情况,在业务应用中使用 PEXPIREAT、PPEXPIREAT 命令,把数据过期时间设置为具体的时间点,避免读到过期数据。

简单总结下刚刚学到过的这两个坑:

  • 主从数据不一致。Redis 采用的是异步复制,所以无法实现强一致性保证,数据不一致是难免的。应对方法:保证良好网络环境,以及使用程序监控主从复制进度,一旦从库复制进度超过阈值,不让客户端连接从库。
  • 对于读到过期数据,有两个提前规避的方法:一个方法是,使用 Redis 3.2 及以上版本;另外,你可以可以使用 PEXPIREAT、PPEXPIREAT 命令设置过期时间,避免从库上的数据过期时间滞后。不过,这里有个地方注意下,因为 PEXPIREAT、PPEXPIREAT 设置的是时间点,所以 主从节点上的时钟要保持一致,具体做法是,让主从节点和相同的 NTP 服务器(时间服务器)进行时钟同步

不合理配置项导致的服务挂掉

这里涉及到两个配置项: protected-mode 和 cluster-node-timeout。

1、protected-mode

这个配置项的作用是限定哨兵机制能够被其他服务器访问。当这个配置项是 yes 时,哨兵实例只能在部署的服务器本地进行访问。当设置为 no 时,其他服务器也可以访问这个哨兵实例。

这是因为这样,如果 protected-mode 设为 yes ,而其余哨兵实例部署在其他服务器,那么这些哨兵视力健就无法通信,当主库故障时,哨兵无法判断主库下线,也无法进行主从切换,最终 Redis 不可用。

所以,我们在应用主从集群时,要注意将 protected-mode 配置项设置为 no,并且将 bind 配置项设置为 其他哨兵实例的 IP 地址。这样一来,只有在 bind 中配置了 IP 地址的哨兵,才可以向才可以访问,既保证了实例间能够通信进行主从切换,也保证了哨兵的安全。

如果设置了下面的配置项,那么,部署在 192.168.10.3/4/5 这三台服务器上的哨兵实例就可以相互通信,执行主从切换。

protected-mode no
bind 192.168.10.3 192.168.10.4 192.168.10.5

2、cluster-node-timeout

我们在 Redis Cluster 集群中为每个实例配置了 “一主一从” 模式时,如果实例发送故障,从实例会切为主实例,受网络延迟和切换操作执行的影响,切换时间可能更长,就会导致实例的心跳超时(超出 cluster-node-timeout)。实例超时后,就会被 Redis Cluster 判断为异常。而 Redis Cluster 能正常运行的条件是,有半数以上的实例都能正常运行。

如果,执行主从切换的实例超过半数,而且主从库切换时间又过长的话,就可能有半数以上的实例心跳超时,从而可能导致整个集群挂掉。所以,建议你将 cluster-node-timeout 调大些。(例如 10 到 20 秒)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1441449.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构第十四天(树的存储/双亲表示法)

目录 前言 概述 接口: 源码: 测试函数: 运行结果: 往期精彩内容 前言 孩子,一定要记得你的父母啊!!! 哈哈,今天开始学习树结构中的双亲表示法,让孩…

YOLOv5独家原创改进:大核卷积涨点系列| Shift-ConvNets,稀疏/移位操作让小卷积核也能达到大卷积核效果 | 2024年最新论文

💡💡💡本文独家改进:大的卷积核设计成为使卷积神经网络(CNNs)再次强大的理想解决方案,Shift-ConvNets稀疏/移位操作让小卷积核也能达到大卷积核效果,创新十足实现涨点,助力YOLOv8 💡💡💡在多个私有数据集和公开数据集VisDrone2019、PASCAL VOC实现涨点 收录…

VSCode如何让先前打开的文件不被自动关闭,一直保持在标签栏里(关闭预览模式)

第一次接触VSCode-Huawei IDE编辑器,每次打开一个新的代码文件,旧的代码文件都会被自动关闭(现在才知道是因为文件默认是以预览模式打开展示的)。 那么如何才能让先前打开的文件一直保持在标签栏里呢? 我们需要去设置…

Mac电脑如何通过终端隐藏应用程序?

在我们使用Mac电脑的时候难免会遇到想要不想看到某个应用程序又不想卸载它们。值得庆幸的是,macOS具有一些强大的文件管理功能,允许用户轻松隐藏(以及稍后显示)文件甚至应用程序。 那么,Mac电脑如何通过终端隐藏应用程…

Composition Local

1.显示传参 package com.jmj.jetpackcomposecompositionlocalimport org.junit.Testimport org.junit.Assert.*/*** 显示传参*/ class ExplicitText {private fun Layout(){var color:String "黑色";//参数需要通过层层传递,比较繁琐Text(color)Grid(c…

idea: 无法创建Java Class文件(SpringBoot)已解决

第一:点击file-->project Sructure... 第二步:点击Moudules 选择自己需要创建java的文件夹(我这里选择的是main)右键点击Sources,然后点击OK即可 然后就可以创建java类了

EasyRecovery免费版2024电脑数据恢复利器

在数字化时代,我们的生活和工作都离不开电脑,电脑硬盘中的数据却时常面临丢失的风险,无论是因为误删除、格式化、病毒感染还是硬件故障,都可能让我们付出沉重的代价,在这种情况下,一款强大的数据恢复软件就…

深度学习在知识图谱问答中的革新与挑战

目录 前言1 背景知识2 基于深度学习改进问句解析模型2.1 谓词匹配2.2 问句解析2.3 逐步生成查询图 3 基于深度学习的端到端模型3.1 端到端框架3.2 简单嵌入技术 4 优势4.1 深入的问题表示4.2 实体关系表示深挖4.3 候选答案排序效果好 5 挑战5.1 依赖大量训练语料5.2 推理类问句…

Python常见的免杀方式

10.1节介绍了通过msfvenom生成shellcode ,并通过Python程序加载执行,又 介绍了如何将Python的.py文件生成为exe文件。使用pyinstaller生成的可执行文件 本身就具有一定的免杀能力,但是在与杀毒软件对抗时,部分杀毒软件也可以通 过…

双非本科准备秋招(21.2)—— ReentrantLock

一、vs synchronized 可中断可以设置超时时间可以设置为公平锁支持多个条件变量 语法: // 获取锁reentrantLock.lock();try {// 临界区} finally {// 释放锁reentrantLock.unlock();} 二、可重入 连续三次上锁。 Slf4j(topic "c.test") public class…

软件应用实例分享,电玩计时计费怎么算,佳易王PS5游戏计时器系统程序教程

软件应用实例分享,电玩计时计费怎么算,佳易王PS5游戏计时器系统程序教程 一、前言 以下软件教程以 佳易王电玩计时计费管理系统软件V17.9为例说明 软件文件下载可以点击最下方官网卡片——软件下载——试用版软件下载 点击开始计时后,图片…

【深度学习:SegGPT】在上下文中分割所有内容 [解释]

【深度学习:SegGPT】在上下文中分割所有内容 [解释] SegGPT与以前的模型相比如何?SegGPT在实践中是如何工作的?SegGPT培训计划上下文着色上下文集成上下文调整SegGPT 训练参数 如何尝试 SegGPT?使用哪些数据集来训练 SegGPT&#…

前端页面禁止debugger调试并跳转空白页面----文心一言官网实现方式

技术点:setInterval定时器Object.defineProperty 背景: 某天打开文心一言想看看接口返回结构是怎样的,熟练的打开浏览器开发者工具查看网络请求。 发现出现了以下debugger断点 这难不倒我,去掉断点调试,继续下一步不…

Django模板(三)

一、标签URL 返回与给定视图和可选参数相匹配的绝对路径引用(不含域名的 URL) {% url some-url-name v1 v2 %} 第一个参数是url模式名称,后面跟着的是参数,以空格分隔可以使用关键字: {% url some-url-name arg1=v1 arg2=v2 %}如果您想检索命名空间的URL,请指定完全限定…

【前端web入门第四天】01 复合选择器与伪类选择器

文章目录: 1. 复合选择器 1.1 后代选择器 1.2 子代选择器 1.3 并集选择器1.4 交集选择器(了解) 2.伪类选择器 2.1 伪类-文本2.2 伪类-超链接(拓展) 1. 复合选择器 什么叫复合选择器? 由两个或多个基础选择器,通过不同的方式组合而成。 复合选择器的作…

《动手学深度学习(PyTorch版)》笔记7.6

注:书中对代码的讲解并不详细,本文对很多细节做了详细注释。另外,书上的源代码是在Jupyter Notebook上运行的,较为分散,本文将代码集中起来,并加以完善,全部用vscode在python 3.9.18下测试通过&…

【新书推荐】7.2 while语句

本节必须掌握的知识点: 掌握if语句语法 熟练使用if语句 7.2.1 示例二十三 ■while语句其语法形式: while(表达式) { 语句块; } ●语法解析: 第一步:执行表达式,如果表达式为真,则执行第…

Elasticsearch:通过 ingest pipeline 对大型文档进行分块

在我之前的文章 “Elasticsearch:使用 LangChain 文档拆分器进行文档分块” 中,我详述了如何通过 LangChain 对大的文档进行分块。那个分块的动作是通过 LangChain 在 Python 中进行实现的。对于使用版权的开发者来说,我们实际上是可以通过 i…

【Fabric.js】监听画布or元素的点击、选中、移动、添加、删除销毁、变形等各事件

在fabric使用过程中,如果想要玩各种花样,那么fabric的事件监听是一定、必须、肯定要掌握!!! 例子就用vue项目组件里的代码,fabric的使用跟vue、react、angular之类的框架都没任何关系! 并且本de…

MySQL学习记录——칠 表操作

文章目录 1、了解2、创建和插入1、基本创建和插入2、插入并更新on duplicate3、插入并替换replace 3、Retrieve1、查询select2、条件查询where3、结果排序order by4、限制行数limit 4、更新Update5、删除delete6、去重7、聚合函数(5个)1、count2、sum3、…