文章目录

  • 1.主从复制
    • 主从复制的作用
    • 实现
    • 同步
    • 命令传播
    • 心跳检测
  • 2.Sentinel
  • 3.集群
    • 节点
    • 槽指派
    • 重新分片
    • ASK错误
    • 故障检测和故障转移

参考《Redis设计与实现》

博客原文链接:《Redis设计与实现》笔记2

1.主从复制

主从复制的作用

  • 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。

  • 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。

  • 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。

  • 读写分离:可以用于实现读写分离,主库写、从库读,读写分离不仅可以提高服务器的负载能力,同时可根据需求的变化,改变从库的数量;

  • 高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。

实现

通过slaveof命令可以实现主从辅助,被复制的服务器叫主服务器,执行复制的服务器叫从服务器,例如

127.0.0.1:6379> slaveof 127.0.0.1:12345

127.0.0.1:6379表示主服务器,127.0.0.1:12345表示从服务器,其主从服务器的数据库状态一致,在主服务器进行增删改,从服务器也会执行相应的增删改。

redis的复制分为两个操作:同步(psync)和命令传播(command propagate)

同步

同步又分为完整重同步和部分重同步

  • 完整重同步用于初次处理复制的情况,步骤如下

    • 从服务器向主服务器发送psync ? -1命令
    • 收到psync命令的主服务器执行bgsave,生成RDB文件,并用缓冲区记录所有写命令
    • 主服务器的bgsave执行完毕后,将RDB文件发送给从服务器,从服务器接收这个RDB文件,并将数据库状态更新
    • 主服务器将缓冲区里所有的写命令发送给从服务器,从服务器接收命令并更新服务器状态至主服务器数据库当前状态
  • 部分重同步用于断线后复制情况,步骤和上述类似,不过从服务器发送给主服务器的命令是psync <runid> <offset>,其中runid为上一次复制的主服务器id,offset为从服务器当前的偏移量,同时还有一个复制积压缓冲区,当主服务器进行命令传播时,除了会把命令发送给从服务器,还会把目录写入到复制积压缓冲区(复制积压缓冲区是定长的,默认大小为1M,所以只能保存最近的一些命令),还会给缓冲区的每个字节添加复制偏移量。如果复制积压缓冲区有数据,就不用每次都执行完整的重同步,可以节省开销

命令传播

在同步完成后,此时的主从状态是一致的,但是如果主服务器再次执行写命令时,主服务器的状态会被修改,将会导致主从不一致,所以需要命令传播,即主服务器会将自己的写命令同时发送给从服务器,从服务器执行了相同的写命令后,主从服务器会再次回到数据一致状态

心跳检测

在命令传播阶段,从服务器会以每秒一次的频率,向主服务器发送命令,主要作用有3个:

  • 检测主从服务器的网络连接状态,如果主服务器超过1秒没有收到从服务器发来的心跳检测命令,说明从服务器和主服务器连接出现了问题
  • 主服务器有两个配置:min-slaves-to-write和min-slaves-max-lag,分别表示可正常连接的从服务器允许的最小数量和从服务器延迟最大值,如果不符合这两个配置值,主服务器将拒绝执行写命令
  • 检测命令是否丢失,如果丢失,将去复制积压缓冲区找出丢失的指令,重新执行

2.Sentinel

Sentinel模式(哨兵模式)是redis高可用性(高可用通常来描述一个系统经过专门的设计,从而减少停工时间,而保持其服务的高度可用性)的解决方案:由一个或多个实例组成的sentinel系统可以监视任意多个主服务器以及其属下的从服务器,并且主服务器下线时,自动将其属下的某个从服务器升级为新的主服务器,然后由新的主服务器代替已下线的主服务器

1.服务器与sentinel系统 2.主服务器Server1下线
3.Server1属下的其中一个从服务器升级为主服务器,sentinel继续监视Server1服务器,当Server1重新上线时,成为Server2的从服务器 4.Server1降级为Server2的从服务器

3.集群

节点

一个redis集群由多个节点(node)组成,刚开始都是独立的,只有将各个独立的节点连接起来才能形成一个集群。通过命令cluster meet <ip> <port>可以将指定的ip:port节点添加到当前node节点集群中,如下把127.0.0.1 7001、127.0.0.1 7002、127.0.0.1 7003这三个独立的节点添加节点127.0.0.1 7000所在的集群:

$redis-cli -c -p 7000                    #进入端口为7000的节点
127.0.0.1:7000> cluster meet 127.0.0.1 7001
OK
127.0.0.1:7000> cluster meet 127.0.0.1 7002
OK
127.0.0.1:7000> cluster meet 127.0.0.1 7003
OK
127.0.0.1:7000> cluster nodes
884181f538839b57f8c1a87bb2283a91a9d9c8fb 127.0.0.1:7000@17000 myself,master - 0 1640615250000 2 connected
e16e3ac84ce031552407d012b079b486658ba11d 127.0.0.1:7002@17002 master - 0 1640615251000 0 connected
5861712d516c4dd9392bcab9e3c57128ef4d8c4f 127.0.0.1:7001@17001 master - 0 1640615251979 1 connected
69ddaf2f271e65865a903190fe584bd9ff7e4295 127.0.0.1:7003@17003 master - 0 1640615250969 3 connected
127.0.0.1:7000>

上述操作之前还需要配置集群环境,我参考这篇文章:搭建本地redis集群环境

槽指派

redis集群通过分片的方式来保存数据库中的键值对:集群的整个数据库分为16384个槽(slot),数据库中的每个键都属于这16384个槽的其中一个,集群的每个节点都可以处理0个或最多16384个槽

只有当数据库中的16384(0~16383)个槽都有节点在处理时,集群才能处于上线状态(ok),相反,如果数据库汇总有任何一个槽没有得到处理,那么集群都将处于下线状态(fail)

上述7000、7001、7002、7003虽然处于同一集群,但是仍为下线状态

127.0.0.1:7000> cluster info
cluster_state:fail      #下线状态
cluster_slots_assigned:0
cluster_slots_ok:0
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:4
cluster_size:0
cluster_current_epoch:3
cluster_my_epoch:2
cluster_stats_messages_ping_sent:3061
cluster_stats_messages_pong_sent:3137
cluster_stats_messages_meet_sent:6
cluster_stats_messages_sent:6204
cluster_stats_messages_ping_received:3137
cluster_stats_messages_pong_received:3067
cluster_stats_messages_received:6204

通过命令cluster addsolts <slot> [slot ...]可以将一个或多个槽指派给节点负责

$ redis-cli -h 127.0.0.1 -p 7000 cluster addslots {0..5460}
OK
$ redis-cli -h 127.0.0.1 -p 7001 cluster addslots {5461..7000}
OK
$ redis-cli -h 127.0.0.1 -p 7002 cluster addslots {7001..10000}
OK
$ redis-cli -h 127.0.0.1 -p 7003 cluster addslots {10001..16383}
OK
$ redis-cli -c -p 7000
127.0.0.1:7000> cluster nodes
884181f538839b57f8c1a87bb2283a91a9d9c8fb 127.0.0.1:7000@17000 myself,master - 0 1640617981000 2 connected 0-5460
e16e3ac84ce031552407d012b079b486658ba11d 127.0.0.1:7002@17002 master - 0 1640617983512 0 connected 7001-10000
5861712d516c4dd9392bcab9e3c57128ef4d8c4f 127.0.0.1:7001@17001 master - 0 1640617982805 1 connected 5461-7000
69ddaf2f271e65865a903190fe584bd9ff7e4295 127.0.0.1:7003@17003 master - 0 1640617982300 3 connected 10001-16383
127.0.0.1:7000> cluster info
cluster_state:ok        # cluster_state为ok状态,表示集群上线了
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:4
cluster_size:4
cluster_current_epoch:3
cluster_my_epoch:2
cluster_stats_messages_ping_sent:4414
cluster_stats_messages_pong_sent:4530
cluster_stats_messages_meet_sent:6
cluster_stats_messages_sent:8950
cluster_stats_messages_ping_received:4530
cluster_stats_messages_pong_received:4420
cluster_stats_messages_received:8950

可见16384个槽都分配完了,cluster_state为ok状态,表示集群上线了,这时客户端就可以向集群中的结点发送数据了

判断键属于哪个槽是通过该公式计算出来的:CRC16(key) & 16383

127.0.0.1:7000> set key1 100
-> Redirected to slot [9189] located at 127.0.0.1:7002
OK
127.0.0.1:7002> get key1
"100"
127.0.0.1:7002> cluster keyslot "key1"
(integer) 9189
127.0.0.1:7002>

因为键key1对应的槽为9189,是7002结点负责的,所以节点7000会返回moved错误给客户端,指引客户端转向节点7002,然后客户端重新向7002结点发送set命令

重新分片

redis集群的重新分片可以将任意数量已经指派给某个节点的槽改为指派给另一个节点,且相关槽所属的键值对也会从源节点移动到目标节点,重新分片不需要下线,可以在线进行

ASK错误

在重新分片的过程中,会先在自己的数据库里查找指定的键,如果源节点没能在自己的数据库找到指定的键,那么这个键有可能已经被迁移到了目标节点,源节点将向客户端返回一个ASK错误,指引客户端转向正在导入槽的目标节点,并再次发送之前想要执行的命令,注意:ASK错误的这种转向只会执行一次,除非下次再次收到ASK错误;这里和MOVED错误有区别,MOVED所指向的目标节点可以持续执行命令,直到结束

故障检测和故障转移

集群中的每个节点都会定期向集群中的其他节点定期发送ping消息,以此检测对方是否在线

当一个从节点发现自己复制的主节点下线了,就会开始故障转移,从一个从节点中新选举一个主节点,新选举主节点的方式和Sentinel模式有点类似,都是基于Raft算法的领头选举方法来实现的

Redis设计与实现笔记2相关推荐

  1. Redis设计与实现 笔记 第十七章 集群 cluster

    集群 Redis 集群是 Redis 提供的分布式数据库方案,集群通过分片来进行数据共享,并提供复制和故障转移功能 17.1 节点 一个 Redis 集群通常由多个节点组成,在刚开始的时候,每个节点都 ...

  2. Redis设计与实现-笔记(一)

    文章目录 缓存理解 带来问题 本地缓存解决方案 分布式缓存 缓存读写模式/更新策略 正文 第一部分 数据结构与对象 第2章 简单动态字符串 2.1 SDS的定义 2.2 SDS与C字符串的区别 2.2 ...

  3. redis设计与实现 笔记(一)

    Redis简单动态字符串 Redis 没有直接使用 C 语言传统的字符串表示(以空字符结尾的字符数组,以下简称 C 字符串), 而是自己构建了一种名为简单动态字符串(simple dynamic st ...

  4. Redis设计与实现笔记

    第一部分:数据结构与对象     简单动态字符串     链表     字典     跳跃表     整数集合     压缩列表     对象 第二部分:单机数据库的实现     数据库     RD ...

  5. 《redis 设计与实现》读书笔记

    大家好,我是烤鸭:     <redis 设计与实现>,读书笔记. 第一部分 数据结构与对象 第2章 简单动态字符串 Redis 使用SDS 作为字符串表示. O(1) 复杂度获取字符串长 ...

  6. Redis 设计与实现 读书笔记(菜鸟版)

    Redis 设计与实现 读书笔记(简略版) 写在前面 第一章(内部数据结构) SDS List Dictionary Rehash Rehash 与 COW 渐进式Rehash 字典收缩 Skipli ...

  7. 《Redis设计与实现》笔记|SDS动态字符串|链表字典跳跃表整数集合压缩列表结构|redis中的对象|数据库原理|RDB持久化|AOF持久化|事件与多路利用模型|发布订阅原理|事务原理|慢查询日志

    <Redis设计与实现>笔记 前记: 参考配套网站:http://redisbook.com 带注释的源码地址:https://github.com/huangz1990/redis-3. ...

  8. 《redis设计与实现》 读书笔记

    <redis设计与实现> 作者:黄健宏 读书笔记 一.前言 什么是redis: Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数 ...

  9. redis设计与实现学习笔记1

    文章目录 1.对象 1.1 类型 1.2 内存回收 1.3 对象共享 1.4 对象空转时长 2.单机数据库 2.1 RDB 2.2 AOF 2.3 事件 2.4客户端 2.5服务器 3.常用命令 参考 ...

最新文章

  1. 数据结构与算法:14 Leetcode同步练习(五)
  2. js 对动态添加的table 排序
  3. c++ 形参用指针 还是对象_C语言:聚会上,我发现只有我没有对象!
  4. sockaddr与 sockaddr_in
  5. Comet OJ - Contest #0题解
  6. 编译我的hello.ko
  7. Android官方开发文档Training系列课程中文版:性能优化建议
  8. 【java】Java实现异步调用方法(jdk1.8)
  9. 一步一步写算法(之字符串查找 中篇)
  10. udp广播收到重复包
  11. rgb转nv12 nv12转rgb
  12. Github上的1000多本免费电子书重磅来袭!
  13. 【Python基础】初识-与君初相识,犹如故人归
  14. 语法错误 : 缺少“;”(在“*”的前面)_学科教学不能缺少生本意识
  15. 6.Vue教程:http://www.jb51.net/Special/874.htm
  16. 健康知识竞答线上活动方案——微信答题小程序实现
  17. SAP BP 业务实践与ABAP 分享
  18. 有限等距性质RIP理解
  19. iOS 开发者账号总结
  20. 金蝶插件常用引用dl

热门文章

  1. KVM 虚拟化 介绍
  2. 做好准备:独立游戏开发人员适用的 4P 营销理论
  3. 如何做一个基于微信酒店预订小程序系统毕业设计毕设作品
  4. Lua不同类型变量做比较时的问题
  5. C# Winform 置顶属性Topmost 的误区
  6. 这个发现是否会是RSA算法的BUG、或者可能存在的破解方式?
  7. python requests接口自动化测试 (数据库断言)
  8. 点击遮罩层的背景关闭遮罩层(HTML)
  9. HUAWEI交换机的Hybrid接口(混杂模式)详解与实验配置演示
  10. 手把手带你 arduino 开发:基于ESP32S 的第一个应用-红外测温枪(带引脚图)