Redis核心技术与实战【学习笔记】 - 24.Redis 脑裂

news/2024/3/5 4:53:08/文章来源:https://blog.csdn.net/chenjian723122704/article/details/136038083

简述

所谓脑裂,就是指在主从集群中,同时有两个主节点,它们都能接收写请求。而脑裂最直接的影响就是客户端不知道该往哪个主节点写入数据,结果就是不同的客户端会往不同的主机诶点上写入数据。而且,严重的话,脑裂会导致数据丢失。

在使用主从集群时,曾遇到过这样一个问题:主从集群有 1 个主库、5 个从库和 3 个哨兵实例,在使用过程中,发现客户端发送的一些数据丢失了,这直接影响了业务层的数据可靠性。
通过一系列的问题排查,我们才知道,这其实是主从集群中的脑裂问题导致的。


1.为什么会发生脑裂?

第一步:确认是不是数据同步出现了问题

在主从集群中发生数据丢失,最常见的原因是主库的数据还没有同步到从路,结果主库发生了故障,等从库升级为主库后,未同步的数据就丢失了。

如下图所示,新写入的 a:1、b:3 ,就是因为主库故障前未同步到从库而丢失了。
在这里插入图片描述
在这种情况的数据丢失,我们可以通过比对主从库上的复制进度差值来进行判断,也就是计算 master_repl_offset 和 slave_repl_offset 的差值。如果从库上的 slave_repl_offset 小于原主库的 master_repl_offset ,那么,我们就可以认为数据丢失是由于数据同步未完成就宕机导致的。

我们在部署主从集群时,也监测了主库上的 master_repl_offset ,及从库上的 slave_repl_offset 。但是,发现数据丢失后,我们检查了新主库升级前的 slave_repl_offset ,以及原主库的 master_repl_offset,它们是一致的,也就是说,这个升级为新主库的从库,在升级时已经和原主库的数据保持一致了。那么,为什么会出现客户端发送的数据丢失呢?

分析到这里,第一个设想被推翻了。这是,又想到所有的数据操作都是客户端发送的,那么是不是可以从客户端操作日志中发现问题呢?

第二步:排查客户端的操作日志,发现脑裂现象

在排查客户端的操作日志时,发现,在主从库切换的一段时间内,有一个客户端仍然在和原主库通信,并没有和升级的新主库进行交互。这就相当于主从集群中同时有了两个主库。根据这个迹象,我们就想到了在分布式主从集群发生故障时会出现的一个问题:脑裂。

但是,不同客户端给两个逐鹿发送数据写操作,按道理来说,只会导致新数据会分布在不同的主库上,并不会造成数据丢失。那么,为什么数据仍然丢失了呢?

到这里,我们的排查思路有一次终端了。不过,在分析问题时,“从原理出发”是追本溯源的好方法。脑裂是发生在主从切换过程中,所以把研究方向投向了主从切换的执行过程。

第三步:发现是原主库虚假故障导致的脑裂

我们是采用哨兵机制进行主从切换的,当主从切换发生时,一定是有超过预设数量(quorum)的哨兵实例和主库的心跳多超时了,才会把主库判断为客观下乡,然后,哨兵开始执行切换操作。哨兵切换完成后,客户端会和新主库进行通信,发送请求操作。

但是在切换过程中,既然客户端仍然是和原主库通信,这就表明,原主库并没有真的发生故障(例如主库进程挂掉)。猜测主库是由于某些原因无法处理请求,也没有响应哨兵的心跳,才被哨兵错误地判断为客观下线的。结果,在被判断下线后,原主库又重新开始处理请求了,而此时,哨兵还没有完成主从切换,客户端仍然可以和原主库通信,客户端发送的写请求就会在原主库上写入数据了。

为了验证原主库只是假故障,我们也查看了原主库所在服务器的资源使用监控记录。

的确,我们看到原主库所在的一段时间的 CPU 利用率突然特别高,这是我们在机器上部署的一个数据采集程序导致的。因为这个程序基本上把机器的 CPU 都用满了,导致 Redis 主库无法响应心跳了,这个期间,哨兵就把主库判断为客观下乡,开始主从切换了。不过,这个数据采集程序很快恢复正常,CPU 的使用率也降下来了。此时,原主库又开始正常服务请求了。

正是因为原主库并没有真的故障,在客户端操作日志中就看到了和原主库的通信记录。等到从库被升级为新主库后,主从集群里就有了两个主库,到这里,脑裂的原因就摸清楚了。

在这里插入图片描述

2.为什么脑裂会导致数据丢失?

主从库切换后,从库一旦升级为新主库,哨兵就会让原从库执行 slave of 命令,和新主库重新进行全量同步。而在全量同步执行的最后阶段,原主库需要清空本地的数据,加载新主库发送的 RDB 文件,这样一来,原主库在主从切换期间保存的新写数据就丢失了。
在这里插入图片描述
到这里,我们就完全弄明白了这个问题的发生过程和原因。

主从切换的过程中,如果原主库只是假故障,它会触发哨兵启动主从切换,一旦等它从假故障中恢复后,又开始处理请求,这样一来,就会和新主库同时存在,形成脑裂。等到哨兵让原主库和新主库做全量同步后,原主库在切换期间保存的数据就丢失了。

3.如何应对脑裂

刚刚说了,主从集群中的数据丢失事件,归根接地是因为发生了脑裂。所以,必须找到脑裂问题的策略。

既然问题是出现在原主库发生假故障后仍然能接收请求上,我们就开始在主从集群机制的配置项中查找是否有限制主库接收请求的设置。

Redis 提供了两个配置项来限制主库的请求处理,分别是 min-slaves-to-writemax-slaves-max-lag

  • min-slaves-to-write:设置主库能进行数据同步的最少数据量
    * max-slaves-max-lag:设置了主从库间进行数据复制时,从库给主库发送 ACK 消息的最大演出(以秒为单位)

可以把 min-slaves-to-writemax-slaves-max-lag 搭配起来使用,分别给它们设置一定的阈值,假设为 N 和 T。这两个配置项组合后的要求是,主库连接的从库中至少有 N 个从库,和主库进行数据复制时的 ACK 消息延迟不能超过 T 秒,否则,主路就不会在接收客户端的请求了

即使,原主库是假故障,它在假故障期间也无法响应哨兵心跳,也不能和从库进行同步,自然也就无法和从库进行 ACK 确认了。这样一来, min-slaves-to-writemax-slaves-max-lag 的组合要求就无法得到满足,原主库就会被限制接收客户端的请求,客户端也就不能在原主库中写入数据了。

等到新主库上线时,就只有新主库能接收和处理客户端的请求,此时,新写的数据会被直接写到新主库中。而原主库会被哨兵降为从库,即使它的数据被清空了,也不会有新数据丢失。

再来举个例子。
假设我们将 min-slaves-to-write 设置为 1,把 max-slaves-max-lag 设置为 12 s,把哨兵的 down-after-milliseconds 设置为 10s,主库因为某些原因卡主了 15s,导致哨兵判断客观下线,开始进行主从切换。同时,因为原主库卡主了 15s,没有一个从库能和原主库在 12s 内进行数据复制,原主库也无法收到客户端的请求了。这样一来,主从切换完成后,只有新主库可以接受请求,不会发生脑裂。

小结

脑裂是指在主从集群中,同时有2个主库都能接收写请求。在 Redis 切换过程中,如果发生了脑裂,客户端数据就会写入到原主库,原主库被降为从库,这些新写入的数据就丢失了。

脑裂发生的原因主要是原主库发生了假故障,总结下假故障的原因:

  1. 和主库部署在同一台服务器上的其他程序临时占用了大量资源(例如 CPU 资源),导致主库资源使用受限,短时间内无法响应心跳。其他程序不再使用资源时,主库又恢复正常。
  2. 主库自身遇到了阻塞的情况,例如,处理 bigkey 或是发送内存 swap(可以复习下《11.响应延迟的波动问题及解决方案》总结的导致实例阻塞的原因),短时间内无法响应心跳,导致阻塞解除后,又恢复正常的请求处理了。

为了应对脑裂,你可以在主从集群部署时,通过合理地配置参数 min-slaves-to-writemax-slaves-max-lag ,来预防脑裂的发生。

给你的建议是,假设从库有 N 个,可以将 min-slaves-to-write 设置为 K/2 + 1(如果 K 为 1 ,就设为1),将 max-slaves-max-lag 设置为十几秒(例如 10~20 秒),在这个配置下,如果有一半以上的从库和主库进行 ACK 消息延迟超过十几秒,就会进制主库接收客户端写请求。

这样一来,就可以避免脑裂带来的数据丢失问题,而且,也不会因为只有少数几个从库因为网络阻塞连不上主库,就进制主库接收请求了。

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

如若内容造成侵权/违法违规/事实不符,请联系编程新知网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android Studio 中使用 Gradle 配置多渠道打包 配置不同的渠道名称 配置不同的App名称 配置不同的Logo

废话 三种操作都是可以混合一起用的,本来也不是很难的事情,为了方便分别理解,这里我就分开处理了。 如果需要将打包出来的apk的名称自动命名成指定格式,也可以进行配置,我这里没这个需求,所以这里就不讨论了…

应用程序数字证书生成及签名步骤

1.非管理员权限切换到管理员用户下,再C盘找到C:\Program Files (x86)\Windows Kits,查看当前路径下是否包含生成数字签名的文件。 注意: 此时不同的电脑会有不同的路径(如蓝色框),需要找到具备生成工具的相…

Spring是怎么解决循环依赖的

首先先解释一下什么叫循环依赖 循环依赖:循环依赖其实就是循环引用,也就是两个或两个以上的bean互相持有对方,最终形成闭环.比如A依赖于B,B依赖于A 循环依赖在spring中是允许存在的,spring框架依据三级缓存已经解决了大部分的循环依赖 一级缓存:单例池,缓存已经经历了完整的…

【漏洞复现】狮子鱼CMS文件上传漏洞(image_upload.php)

Nx01 产品简介 狮子鱼CMS(Content Management System)是一种网站管理系统,它旨在帮助用户更轻松地创建和管理网站。该系统拥有用户友好的界面和丰富的功能,包括页面管理、博客、新闻、产品展示等。通过简单直观的管理界面&#xf…

re:从0开始的CSS学习之路 1. CSS语法规则

0. 写在前面 现在大模型卷的飞起,感觉做页面的活可能以后就不需要人来做了,不知道现在还有没有学前端的必要。。。 1. HTML和CSS结合的三种方式 在HTML中,我们强调HTML并不关心显示样式,样式是CSS的工作,现在就轮到C…

disql备份还原

disql备份还原 前言 本文档根据官方文档,进行整理。 一、概述 在 disql 工具中使用 BACKUP 语句你可以备份整个数据库。通常情况下,在数据库实例配置归档后输入以下语句即可备份数据库: BACKUP DATABASE BACKUPSET db_bak_01;语句执行完…

Spring基础 - Spring简单例子引入Spring要点

Spring基础 - Spring简单例子引入Spring要点 设计一个Spring的Hello World 设计一个查询用户的案例的两个需求&#xff0c;来看Spring框架帮我们简化了什么开发工作 pom依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"htt…

x-cmd pkg | httpx - 为 Python 设计的下一代 HTTP 客户端库

目录 简介首次用户功能特点进一步探索 简介 HTTPX 是一个为 Python 设计的下一代 HTTP 客户端库&#xff0c;由 Tom Christie 创建。它提供了同步和异步的 API&#xff0c;并支持 HTTP/1.1 和 HTTP/2 协议。与 Requests 库类似&#xff0c;但增加了对异步请求的支持和 HTTP/2 …

《UE5_C++多人TPS完整教程》学习笔记9 ——《P10 创建会话(Creating A Session)》

本文为B站系列教学视频 《UE5_C多人TPS完整教程》 —— 《P10 创建会话&#xff08;Creating A Session&#xff09;》 的学习笔记&#xff0c;该系列教学视频为 Udemy 课程 《Unreal Engine 5 C Multiplayer Shooter》 的中文字幕翻译版&#xff0c;UP主&#xff08;也是译者&…

拟合案例1:matlab积分函数拟合详细步骤及源码

本文介绍一下基于matlab实现积分函数拟合的过程。采用的工具是lsqcurvefit和nlinfit两个函数工具。关于包含积分运算的函数,这里可以分为两大类啊。我们用具体的案例来展示:一种是积分运算中不包含这个自变量,如下图的第一个公式,也就是说它这个积分运算只有R和Q这两个待定…

基于图像掩膜和深度学习的花生豆分拣(附源码)

目录 项目介绍 图像分类网络构建 处理花生豆图片完成预测 项目介绍 这是一个使用图像掩膜技术和深度学习技术实现的一个花生豆分拣系统 我们有大量的花生豆图片&#xff0c;并以及打好了标签&#xff0c;可以看一下目录结构和几张具体的图片 同时我们也有几张大的图片&…

《CSS 简易速速上手小册》第6章:高级 CSS 技巧(2024 最新版)

文章目录 6.1 使用 CSS 变量进行设计&#xff1a;魔法配方的调配6.1.1 基础知识6.1.2 重点案例&#xff1a;创建可定制的主题6.1.3 拓展案例 1&#xff1a;响应式字体大小6.1.4 拓展案例 2&#xff1a;使用 CSS 变量创建动态阴影效果 6.2 calc(), min(), max() 等函数的应用&am…

【机器学习与自然语言处理】预训练 Pre-Training 各种经典方法的概念汇总

【机器学习与自然语言处理】预训练 Pre-Training 各种经典方法的概念汇总 前言请看此正文预训练 Pre-Training无监督学习 unsupervised learning概念&#xff1a;标签PCA 主成分分析&#xff08;Principal Component Analysis&#xff09;降维算法LSA 潜在语义分析&#xff08;…

Github 2024-02-07 开源项目日报 Top9

根据Github Trendings的统计&#xff0c;今日(2024-02-07统计)共有9个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量Rust项目2TypeScript项目2Python项目2Ruby项目1HTML项目1NASL项目1Go项目1C项目1Svelte项目1C项目1 React Nat…

【MySQL】——数值函数的学习

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-Z1fAnfrxGD7I5gqp {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

阿里云带宽计费模式怎么选?如何收费的?

阿里云服务器带宽计费模式分为“按固定带宽”和“按使用流量”&#xff0c;有什么区别&#xff1f;按固定带宽是指直接购买多少M带宽&#xff0c;比如1M、5M、10M、100M等&#xff0c;阿里云直接分配用户所购买的带宽值&#xff0c;根据带宽大小先付费再使用&#xff1b;按使用…

React Native开发iOS实战录

文章目录 背景环境准备基础工具&#xff1a;xcode安装主要工具安装CocoaPods 基本步骤采用Expo go运行iOS模拟器运行安装在真机上测试发布到苹果商店 原生模块与编译链接问题静态库和 Frameworkuse_frameworks!和use_modular_headers! 常见问题ruby3在macOS上编译失败import of…

bert-vits2本地部署报错疑难问题汇总

环境&#xff1a; bert-vits2.3 win 和wsl 问题描述&#xff1a; bert-vits2本地部署报错疑难问题汇总 解决方案&#xff1a; 问题1: Conda安装requirements里面依赖出现ERROR: No matching distribution found for opencc1.1.6 解决方法 需要在 Python 3.11 上使用 Op…

Stable Diffusion 模型下载:RealCartoon-Realistic - V13

本文收录于《AI绘画从入门到精通》专栏,专栏总目录:点这里。 文章目录 模型介绍生成案例案例一案例二案例三案例四案例五案例六案例七案例八案例九案例十

STM32 寄存器操作 GPIO 与中断

一、如何使用stm32寄存器点灯&#xff1f; 1.1 寄存器映射表 寄存器本质就是一个开关&#xff0c;当我们把芯片寄存器配置指定的状态时即可使用芯片的硬件能力。 寄存器映射表则是开关的地址说明。对于我们希望点亮 GPIO_B 的一个灯来说&#xff0c;需要关注以下的两个寄存器…