惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

H
Heimdal Security Blog
A
Arctic Wolf
K
Kaspersky official blog
V
Vulnerabilities – Threatpost
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Simon Willison's Weblog
Simon Willison's Weblog
L
LINUX DO - 热门话题
MongoDB | Blog
MongoDB | Blog
T
Threat Research - Cisco Blogs
D
Docker
爱范儿
爱范儿
T
Tenable Blog
C
Check Point Blog
B
Blog
C
Cisco Blogs
Vercel News
Vercel News
The Cloudflare Blog
T
Threatpost
NISL@THU
NISL@THU
T
Tor Project blog
V2EX - 技术
V2EX - 技术
P
Palo Alto Networks Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
T
Tailwind CSS Blog
G
GRAHAM CLULEY
P
Privacy & Cybersecurity Law Blog
SecWiki News
SecWiki News
博客园 - 司徒正美
S
Security @ Cisco Blogs
GbyAI
GbyAI
S
Secure Thoughts
Microsoft Security Blog
Microsoft Security Blog
The Register - Security
The Register - Security
Recorded Future
Recorded Future
Cloudbric
Cloudbric
Webroot Blog
Webroot Blog
N
News and Events Feed by Topic
Y
Y Combinator Blog
博客园_首页
T
Troy Hunt's Blog
The Hacker News
The Hacker News
雷峰网
雷峰网
Google DeepMind News
Google DeepMind News
U
Unit 42
AWS News Blog
AWS News Blog
PCI Perspectives
PCI Perspectives
V
Visual Studio Blog
博客园 - 聂微东
有赞技术团队
有赞技术团队
酷 壳 – CoolShell
酷 壳 – CoolShell

郑文峰的博客

使用dify对接飞书多维表格 使用n8n对接飞书多维表格 服务启动时出现 OOM 一次服务升级时pg表DDL执行超时失败 Go语言高效IO缓冲技术详解 Go语言延迟初始化(Lazy Initialization)最佳实践 Go语言字符串拼接性能对比与优化指南 Go语言结构体内存对齐完全指南 Go语言空结构体:零内存消耗的高效编程 Go语言堆栈分配与逃逸分析深度解析 Go语言原子操作完全指南 Go语言内存预分配完全指南 Go语言不可变数据共享:无锁并发编程实践 Go语言零拷贝技术完全指南 Go语言遍历性能深度解析:从原理到优化实践 Go语言Interface Boxing原理与性能优化指南 Go协程池深度解析:原理、实现与最佳实践 使用etcd分布式锁导致的协程泄露与死锁问题 基于pre-commit的Python代码规范落地实践 初识 MCP Server pulsar阻塞导致logstash无法接入日志 django-prometheus使用及源码分析 kube-proxy源码分析 kubernetes service如何通过iptables转发 tcp缓存引起的日志丢失 django-apschedule定时任务异常停止 理解calico容器网络通信方案原理 理解flannel的三种容器网络方案原理 理解Linux IPIP隧道 理解VXLAN网络 理解Linux TunTap设备 快速了解iptables kafka中listener和advertised.listeners的作用 django rest_framework 分页 django后端服务、logstash和flink接入VictoriaMetrics指标监控 python中import原理 docker容器单机网络 手动实现docker容器bridge网络模型 mysql之MVCC原理 mysql之日志 使用java开发logstash的filter插件 使用python实现单例模式的三种方式 redis之缓存 redis之分片集群 redis之哨兵机制 redis之持久化 redis之五种基本数据类型 go中如何处理error pod中将代码与运行环境分离 ddt源码分析 python装饰器的使用方法 读书笔记:如何阅读一本书 使用ddt实现unittest的参数化测试 使用kubeadm安装k8s 优化gin表单的错误提示信息 gin中validator模块的源码分析 go简单使用grpc python简单使用grpc k8s之PV、PVC和StorageClass k8s之StatefulSet k8s之DaemonSet k8s之Job和CronJob k8s之ConfigMap和Secret k8s之Service k8s之Pod k8s之Deployment 容器的本质 docker容器 python迭代器与生成器 python元编程 python垃圾回收机制 python上下文管理器 django rest_framework使用jwt django rest_framework异常处理 django rest_framework 自定义文档 django压缩文件下载 django rest_framework使用pytest单元测试 django restframework choice 自定义输出数据 django Filtering 使用 django viewset 和 Router 配合使用时报的错 django model的序列化 django中使用AbStractUser django.core.exceptions.ImproperlyConfigured Application labels aren't unique, duplicates users django 中 media配置 django 外键引用自身和on_delete参数 django 警告 while time zone support is active Flask使用flask_socketio实现websocket flask结合mongo tornado 文件上传 tornado 使用jwt完成用户异步认证 tornado 用户密码 bcrypt加密 tornado 结合wtforms使用表单操作 tornado finish和write区别 tornado 使用peewee-async 完成异步orm数据库操作 pyspark streaming简介 和 消费 kafka示例 使用hue创建ozzie的pyspark action workflow count的性能优化 django rest_framework Authentication django celery 结合使用 网站
redis之主从库同步
zhengwenfeng · 2022-12-01 · via 郑文峰的博客

# 0. 前言

在单点故障后,我们需要保证服务不间断,所以需要使用冗余的副本提供集群服务,从而达到服务的高可用。redis 提供了主从库数据同步机制,从而保证数据副本的一致性,而主从库使用的是读写分离的机制。

# 1. 读写分离模式

通过该模式构建多个数据副本,使用读写分离的方式

  • 读操作: 主从库都可以进行读取。
  • 写操作: 先写入到主库,在同步到从库。

为什么要读写分离呢?

如果从库也可以进行写操作,那么主从库在同一个 key 中存储的值可能会不一致,如果要保证一致性,需要通过加锁来解决,但这样会造成性能的损耗。

但如果只有主库写入,在同步给从库,则能保证所有实例中数据的一致性。

# 2. 全量复制

# 2.1 主从库同步的过程

第一阶段

从库向主库发送psync命令进行数据同步,该命令包含主库的runID和复制进度offset

  • runID,每个实例自动生成的随机 ID,第一次从库不知道主库 runID,设置为?
  • offset,记录复制的进度,第一次进度设置为-1

主库会回复 runID 和 offset 给从库

第二阶段

主从第一次同步是采用全量复制的方式,主库生成 RDB 文件,然后发送给从库,从库清空当前数据再读入 RDB 文件完成第一次同步。

在生成 RDB 文件时,还是会有新的操作的会进行,为了保持数据的一致性,会将新的操作记录到 replication buffer 中。

第三阶段

RDB 文件发送到从库后,再 replication buffer 中的操作再发送给从库。

# 2.2 如何减少主从同步时,对主库的压力?

主从同步有哪些压力?

  • 生成 RDB,这个操作会 fork 子进程,会阻塞主线程的正常请求。
  • 传输 RDB,会占用主库的网络带宽

主-从-从模式

可以手动选择一个从库(比如选择内存资源配置较高的从库),用于级联其他的从库。由从库生成 RDB 再传输给从库,减少了主库的压力。

在主库全量复制之后,会维护一个长连接,后需的操作命令通过该连接同步给从库

# 3. 增量复制

全量复制是通过生成 RDB 发送到从库然后进行读取后进行同步。我们知道生成 RDB 本身就是一个消耗 CPU 的操作,并且存在阻塞主线程的风险,所以我们需要尽量少的执行全量复制的操作。

所以在第一次使用 RDB 全量复制后,主从库会建立一个长连接,主库收到的新命令会再同步到从库,这样就避免频繁全量复制。

但是有一个风险点是,如果过程发生了网络中断或者阻塞,该如何解决?

# 3.1 增量复制的过程

当主从连接时,会将新的操作的命令写入 replication bufferrepl_backlog_buffer 缓冲区中

repl_backlog_buffer 是一个环形缓冲区,主库会记录当前的偏移量 master_repl_offset,记录自己写到的位置,而从库在上面也有对应的偏移量 slave_repl_offset,记录自己正在读到的偏移量。

在恢复连接时,从库会通过 psync 命令将自己的偏移量 slave_repl_offset 发送给主库,主库会将 slave_repl_offsetmaster_repl_offset 之间的命令同步给从库即可。

举了例子: 主库和从库之间相差了 put d e 和 put d f 两个操作,在增量复制时,主库只需要把它们同步给从库,就行了。

因为 replication buffer 是一个环形的缓存,当主从库长期断开时,是有可能被覆盖掉旧的数据,这个时候是会重新发起全量复制,主库根据从库发送的 slave_repl_offset 来判断是增量还是全量的复制。

那为什么全量复制使用 RDB 而不是使用 AOF 呢?

  • RDB 文件是经过压缩的二进制文件,AOF 文件是记录每一次的操作,包含对同一个 key 的多次冗余操作,文件比 RDB 要大的多,使用 AOF 可以减少带宽
  • RDB 是二进制数据,从库还原速度快。而 AOF 需要依次重放每一个命令,恢复速度慢。

# 4. 参考文章

  • 本文主要是学习《极客时间-redis 核心技术与实战》专栏总结而来