Redis高级(Redis持久化,Redis主从模式,Redis哨兵模式,Redis分片集群)

目录

一、单机Redis

1. 问题说明

2. 安装Redis

1 解压安装Redis【备用】

2 配置Redis

3 启动Redis

3. 小结

二、Redis持久化

1. 持久化机制介绍

2. RDB模式

3. AOF模式

4. RDB和AOF对比

5. 小结

三、Redis主从模式

1. 介绍

2. 搭建Redis主从架构【备用】

3. 主从数据同步原理

4. 主从同步优化

5. 小结

四、Redis哨兵模式

1. 介绍

2. 哨兵模式的架构原理

3. 搭建哨兵集群

4. RestTemplate访问哨兵集群

5. 小结

五、Redis分片集群

1. 介绍

2. 分片集群的架构

3. 搭建分片集群

4. 散列插槽(Hash插槽)

5. 集群伸缩

6. 故障转移

7. RedisTemplate访问分片集群

六、大总结


一、单机Redis

1. 问题说明

之前我们在使用Redis的时候,全部使用的是Redis的单机模式。因为单机模式Redis搭建简单、使用方便,利于学习入门。但是在实际使用中,单机Redis存在一些问题需要解决:

  • 数据持久化问题

    Redis为了提高性能,数据是在内存中存储的。而内存不能持久化存储数据,所以Redis提供了持久化机制

  • 并发能力问题

    单机Redis的并发能力有限,可以通过主从集群来实现读写分离,从而提高并发能力

  • 故障恢复问题

    在Redis集群中,一旦某个节点宕机,就必须要手动进行故障转换与恢复。Redis提供了哨兵模式来实现自动故障恢复

  • 存储能力问题

    无论是主从模式的集群,还是哨兵模式的集群,都没有解决存储能力的扩展问题。Redis提供了分片集群模式,可以很方便的动态扩容

2. 安装Redis

提示:虚拟机里,我们都以root帐号登录,密码是 itcast

提示:虚拟机里,先关闭防火墙,再做后边的操作

  • 关闭防火墙:systemctl stop firewalld

  • 防火墙 取消开机自启:systemctl disable firewalld

  • 清理虚拟机里的旧配置 rm -rf ~/0*

1 解压安装Redis【备用】

把资料里的《redis-6.2.4.tar.gz》上传到Linux的/root目录下

或者直接镜像拉取

执行命令安装redis:

#1. 安装Redis需要的依赖程序包
yum install -y gcc tcl

#2. 切换到root用户的家目录
cd ~
#3. 解压Redis程序包
tar -xvf redis-6.2.4.tar.gz
#4. 切换到Redis目录里
cd ~/redis-6.2.4/
#5. 编译并安装Redis。如果此命令不出错,通常就表示安装成功了
make && make install

2 配置Redis

#1. 在root家目录里创建文件夹
mkdir ~/01standalone
#2. 把Redis配置文件拷贝进来
cp ~/redis-6.2.4/redis.conf ~/01standalone/
#3. 切换到~/01standalone/目录里
cd ~/01standalone/
#4. 使用vi编辑redis.conf
vi redis.conf

使用vi修改redis.conf的如下内容,修改之后再保存并退出vi:

# 绑定地址,默认是127.0.0.1,只能在本机访问Redis服务。修改为0.0.0.0则可以在任意IP访问Redis服务
bind 0.0.0.0
# 数据库数量,设置为1
databases 1

3 启动Redis

所有单机问题,我们全在~/01standalone/目录里操作,使用这个目录里的配置文件

  • 启动Redis服务:redis-server ~/01standalone/redis.conf

  • 使用Redis:redis-cli,然后就可以使用Redis的命令了

  • 关闭Redis服务:redis-cli shutdown

💡Redis服务安装好以后,可以同时启动多个实例,只要加载对应的配置文件,使用不同的端口即可

3. 小结

Linux里redis的操作:

  • 启动redis服务:redis-server 配置文件路径

  • 连接Redis:redis-cli

  • 断开连接:exit

  • 关闭Redis服务:redis-cli shutdown

二、Redis持久化

1. 持久化机制介绍

所谓持久化,指的是数据的永久保存。Redis是一个内存数据库,它的所有数据都在内存中,所以有极高的读写性能,但是内存中的数据全部是临时的,所以一旦Redis进程结束,所有数据就会清空了。

为了防止这样的问题,Redis提供了持久化机制:

  • RDB模式:快照模式,默认是开启状态的

  • AOF模式:日志模式

注意:在后续的所有演示中,只要修改了配置文件,就必须要重启redis服务

2. RDB模式

RDB,Redis Database Backup file,称为Redis数据快照。当执行RDB持久化时,会把Redis内存中的数据全部都保存到磁盘文件上;当RDB恢复数据时,会读取磁盘文件,把文件里所有的数据恢复到内存中。

快照文件,也称为RDB文件,默认保存在当前运行目录(在哪个目录里启动Redis服务,就保存在哪个目录)

1 RDB执行时机

在以下情况下,会执行RDB快照持久化:

  • 执行了save命令

  • 执行了bgsave命令

  • Redis服务关闭时

  • 触发自动RDB的条件时

1 save命令

说明

当执行save命令时,Redis会执行一次RDB持久化。但是save命令是一个阻塞式命令:

  • 它由Redis主进程执行RDB持久化,在持久化过程中Redis将不能处理客户端的操作,直到RDB执行完毕

  • 如果Redis中数据非常多,持久化将会花费比较长的时间,Redis也将会阻塞比较长的时间

  • 目前,save命令已经很少使用了,通常使用bgsave代替它

示例

使用redis-cli连接Redis服务之后,执行命令:save,就会执行一次RDB持久化

[root@ithiema ~]# redis-cli
127.0.0.1:6379> save
OK
127.0.0.1:6379> 

退出redis-cli,查看一下有没有RDB文件:

127.0.0.1:6379> exit
[root@ithiema 01standalone]# ls
dump.rdb  redis.conf

2 bgsave命令

说明

bgsave,指background save。当执行bgsave命令时,将会执行一次RDB持久化。

而bgsave是非阻塞的、异步式的:

  • Redis会fork一个子进程,由子进程负责RDB持久化

  • 而主进程仍然可以处理客户端的操作,使用体验更好

示例

使用redis-cli连接Redis服务之后,执行命令:bgsave,就会执行一次RDB持久化

127.0.0.1:6379> bgsave
Background saving started
127.0.0.1:6379> 

3 Redis服务关闭

当结束Redis服务时,Redis会先执行一次save命令,把内存中的数据进行RDB持久化。

关闭Redis服务的方式有很多,可以:

  • 在redis-server服务进程中,按ctrl + c,立即结束redis服务

  • 在redis-cli客户端中,执行命令shutdown,关闭redis服务

  • 或者直接执行Linux命令redis-cli shutdown命令

4 RDB自动触发

Redis的RDB模式提供了默认的自动触发机制,相关的配置参数在配置文件redis.conf中,默认值如下:

  • 如果3600秒内有1次数据变更,就执行一次RDB

  • 如果300秒内有100次数据变更,就执行一次RDB

  • 如果60秒内有10000次数据变更,就执行一次RDB

save 3600 1
save 300 100
save 60 10000

提示:如果写成 save "",表示禁用RDB

2 RDB的其它参数

在Redis的配置文件redis.conf中,还有一些RDB相关的参数,我们可以了解一下

rdbcompression yes
dbfilename dump.rdb
dir ./

  • rdbcompression:是否开启RDB文件压缩

    如果参数值为yes,Redis将会对rdb文件进行压缩,最终rdb文件会更小

    占用磁盘空间更小,但是压缩与解压时会消耗CPU

  • dbfilename:RDB文件名称

    自定义RDB文件名称

  • dir:工作路径

    RDB文件保存的路径,默认是./

3 RDB执行原理

默认情况下,Redis服务只有一个进程,由这个进程负责处理客户端的一切操作请求。

当执行bgsave命令时,Redis会fork一个子进程出来,实现异步的RDB:

  • 由子进程负责执行RDB,

  • 而主进程继续负责处理客户端的操作请求。

虽然子进程执行RDB持久化的过程中,并不会对主进程造成影响,所以主进程不会阻塞,可以继续处理客户端的操作请求。但是还有一些新问题需要考虑:

  • fork子进程,也需要一定的时间。但这个时间相对于RDB持久化而言,就非常短暂了,只需要几十毫秒多则1秒钟的时间

  • 如果子进程执行RDB的过程中,客户端有新的操作改变了数据,为了避免这些新改变的数据对RDB过程造成影响,Redis采用了copy-on-write技术:

    • 当主进程执行读操作时,访问共享内存;

    • 当主进程执行写操作时,则会拷贝一份数据,执行写操作。

     

3. AOF模式

AOF,Append Only File。也有人称为“日志模式”。当执行AOF持久化时,会把执行的每个数据变更的命令追加保存到磁盘文件里。当AOF恢复数据时,会读取磁盘文件,按顺序依次执行所有的命令,恢复数据。

1 开启AOF

Redis的AOF默认是关闭状态的,如果要开启AOF模式,则需要:

  1. 修改redis.conf配置文件

appendonly yes
# AOF文件的名称
appendfilename "appendonly.aof"

  • appendonly:AOF模式的开关。如果值是yes,表示开启;如果是no,表示关闭

  • appendfilename:AOF文件的名称。默认是appendonly.aof,可以自定义文件名

      2.重启Redis服务

先关闭Redis服务,执行命令:redis-cli shutdown

再重启Redis服务,执行命令:redis-server redis.conf

2 AOF执行时机

开启AOF模式之后,在redis.conf中配置有的AOF持久化执行时机,可通过appendfsync参数进行配置

#appendfsync always 
appendfsync everysec 
#appendfsync no

  • 如果值为always:同步刷盘,每次的写数据的命令,就立即追加到aof文件中

  • 如果值为everysec:每次的写命令先放到AOF缓冲区,然后以每秒一次的频率,把缓冲区里的命令保存到aof文件

  • 如果值为no:每次的写命令先放到AOF缓冲区,然后完全由操作系统决定何时 把缓冲区里命令保存到aof文件

三种时机的对比:

注意:只要修改了配置文件,就必须要重启redis服务

刷盘:把缓冲区里的数据存储到磁盘上,称为刷盘。 flush

3 AOF文件重写

1 AOF冗余记录

因为AOF保存的是写命令,类似于日志文件,所以通常情况下AOF日志文件都比RDB文件大,且随着使用时间的推移,AOF文件通常会越来越大,从而导致:

  • 磁盘空间的占用越来越大

  • 恢复数据的速度越来越慢

主要原因在于,AOF忠实的记录每次写操作的命令,就可能有大量重复的命令;比如对同一key有多次修改操作,每次修改操作都会记录到AOF文件里,而实际上只有最后一次操作命令才有效。假如先后执行以下命令:

set num 1
set num 10
set num 100
set num 1

那么AOF文件里会保存4条命令,实际上只需要最后一条命令就可以了

2 bgrewriteaof重写

手动执行AOF重写

通过在redis-cli中执行bgrewriteaof命令,可以让AOF文件执行重写:

  1. 删除无效的命令。例如:set a A, set a B, del a三个命令,其实全部都可以删除掉

  2. 合并多余的命令。例如:

自动触发AOF重写

Redis也会在触发阈值时自动去重写AOF文件,阈值的配置在redis.conf文件中

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb 

  • auto-aof-rewrite-min-size:AOF文件必须大于这个值,才可能触发AOF重写

  • auto-aof-rewrite-percentage:当AOF文件 与 上次重写后的文件对比,超过这个百分比后,会触发AOF重写

4. RDB和AOF对比

RDB和AOF两种持久化模式各有各的优缺点。在实际开发中往往是两者结合使用

5. 小结

Redis的持久化机制:

  • RDB机制

    • 如何持久化的:

      把内存里所有的数据,保存到磁盘文件上;当redis服务重启时,就从磁盘上加载文件,把数据恢复到内存中

    • 什么时候持久化的:

      执行save命令时:是一个阻塞式命令,持久化时当前线程会阻塞,影响性能

      执行bgsave命令时:是一个非阻塞式命令,会fork一个子进程。由子进程负责RDB持久化,由主进程负责处理客户端的操作命令

      redis服务关闭时:当redis服务正常关闭时,会先执行RDB持久化,之后才会关闭服务;

      触发redis自动RDB:配置文件里有自动持久化的时机。

      • save 3600 1

      • save 300 100

      • save 60 10000

    • bgsave的原理:

      在bgsave之前,是由主进程通过页表操作物理内存里的数据

      当执行bgsave时:

      • 会fork一个子进程,会拷贝主进程里的页表,然后子进程就可以通过页表读取物理内存里的数据,保存到磁盘文件上

      • 同时,为了防止子进程RDB过程中,物理内存里的数据被修改:会把物理内存里的数据设置为read-only只读。子进程RDB时,数据就不会被修改

      • 如果这时有主进程执行了新的命令要修改数据:物理内存里的数据是只读的,Redis会采用copy-on-write机制,把数据拷贝一个副本,对这个数据副本进行读写操作

      • 当子进程RDB结束之后,会丢弃旧版本的数据,保留最新的副本数据

  • AOF机制

    • 如何持久化的:

      AOF默认是关闭的,需要开启AOF机制才可以

      Redis会把执行所有 数据变更的命令,追加保存到磁盘文件上;当Redis重启恢复数据时,就加载磁盘文件,按顺序执行所有命令

    • 什么时候持久化的:

      always,同步刷盘。redis每执行完成一个命令之后,都会立即把命令追加保存到磁盘文件上

      everysec,每秒刷盘。redis每执行完成一个命令之后,会把命令放到缓冲区里。然后每秒一次 把缓冲区里的命令保存到磁盘文件上

      no,由操作决定何时刷盘。redis每执行完成一个命令之后,会把命令放到缓冲区里。操作系统空闲时刷盘

    • AOF文件大,命令冗余的处理:利用AOF重写

      手动重写,执行命令 bgrewriteaof

      自动重写,根据配置文件里的参数,达到阈值(默认64mb,增长100%)时会自动重写

Redis4.0开始,提供了混合持久化(RDB+AOF)

三、Redis主从模式

1. 介绍

单节点的Redis服务支持的并发是有限的:所有客户端的请求全部冲击到仅有的一台服务器上,服务器可能因为压力过大而阻塞不能及时响应。

这时候可以采用主从架构,做到读写分离:多个Redis节点共同提供服务,所有写数据的操作都请求到主服务器,所有读数据的操作都请求到从服务器。读写分离,提升并发能力

 

 

2. 搭建Redis主从架构【备用】

1 架构说明

我们搭建的Redis主从集群结构,共包含三个节点。其中一个主节点,两个从节点

我们将会在同一个虚拟机里开启三个Redis实例,模拟主从集群,信息如下:

如图所示:

 

2 搭建主从集群

要同时启动三个Redis实例,就需要准备三个单独的文件夹、三个单独的配置文件。

#1. 准备一个文件夹02master,把主从集群所有相关的配置全放到这个文件夹里
mkdir ~/02master
#2. 在02master里准备三个文件夹,作为三个redis实例的工作目录
cd ~/02master/
mkdir 6380 6381 6382

#3. 把Redis的配置文件,分别拷贝到6380,6381,6382三个文件夹里。
echo 6380 6381 6382 | xargs -t -n 1 cp ~/redis-6.2.4/redis.conf

#4. 分别修改6380、6381、6382三个文件夹里的配置文件
#    把端口分别修改为6380、6381、6382
#    把配置文件里的dir,全部修改为各自的工作目录
cd ~/02master
sed -i -e 's/6379/6380/g' -e 's/dir .\//dir \/root\/02master\/6380\//g' -e 's/bind 127.0.0.1 -::1/bind 0.0.0.0/g' 6380/redis.conf
sed -i -e 's/6379/6381/g' -e 's/dir .\//dir \/root\/02master\/6381\//g' -e 's/bind 127.0.0.1 -::1/bind 0.0.0.0/g' 6381/redis.conf
sed -i -e 's/6379/6382/g' -e 's/dir .\//dir \/root\/02master\/6382\//g' -e 's/bind 127.0.0.1 -::1/bind 0.0.0.0/g' 6382/redis.conf

#5. 修改每个Redis实例的ip
#    虚拟机本身有多个虚拟网卡,有多个ip地址。为了防止地址混乱,我们直接在配置文件里指定要绑定的ip地址
#    6380、6381、6382三个文件夹里的配置文件都要修改,执行以下命令
cd ~/02master
printf '%s\n' 6380 6381 6382 | xargs -I{} -t sed -i '1a replica-announce-ip 192.168.200.136' {}/redis.conf

3 启动集群

为了方便查看每个Redis实例的日志,我们开启三个ssh容器,分别启动3个redis实例。命令如下:

# 第一个Redis实例
redis-server ~/02master/6380/redis.conf
# 第二个Redis实例
redis-server ~/02master/6381/redis.conf
# 第三个Redis实例
redis-server ~/02master/6382/redis.conf

附加:如果想要一键停止所有Redis实例,可以执行以下命令:

printf '%s\n' 6380 6381 6382 | xargs -I{} -t redis-cli -p {} shutdown

4 开启主从关系

到目前为止,三个Redis实例之间还没有任何关系。要配置主从关系,可以使用Redis提供的replicaof命令(Redis5.0开始)或者slaveof命令(Redis5.0以前),两个命令效果一致。

主从关系的配置有临时配置和永久配置两种

  • 永久生效:修改配置文件,然后重启所有Redis服务

    在redis.conf中增加一行配置:slaveof masterIp masterPort

  • 临时生效:使用redis-cli连接Redis服务,执行slaveof命令(Redis实例重启后失效)

    slaveof masterIp masterPort

这里为了方便演示,我们使用方式二临时生效,将6380节点设置为master:

1. 连接6381,执行命令

#连接6381实例
redis-cli -p 6381
#设置为6380的从节点
slaveof 192.168.200.136 6380

2. 连接6382,执行命令

#连接6382实例
redis-cli -p 6382
#设置为6380的从节点
slaveof 192.168.200.136 6380

3. 连接6380,查看集群状态

#连接6380实例
redis-cli -p 6380
#查看集群状态
info replication

5 测试

按顺序执行以下操作:

  • 使用redis-cli连接6380,执行 set sum 100

  • 使用redis-cli连接6381,执行get num, 然后再执行set num 101

  • 使用redis-cli连接6382,执行get num,然后再执行set num 102

结果发现:

  • 只有6380这个节点上可以进行写操作

  • 6381和6382两个节点上只能进行读操作

3. 主从数据同步原理

1 全量同步

当主从第一次建立连接时,会执行全量同步:将master节点的所有数据都拷贝给slave节点,流程如下:

 

但是这里有一个问题:Master如何判断 slave是否第一次连接呢?我们需要先了解一个概念:

  • Replication Id:简称replid,由40位十六进制字符组成。每个Master节点启动时都会给自己的数据集生成一个replid,而slave会继承其master的replid。

  • offset:偏移量,是一个数字,是记录的指令的位置,会随着记录在repl_baklog中的数据增多而增大。slave完成同步时也会记录当前同步的offset。如果slave的offset小于master的offset,说明slave的数据落后于master,需要更新。

因此slave做数据同步,必须向master声明自己的replication id 和offset,master才可以判断到底需要同步哪些数据:

  • 而slave原本也是一个master,有自己的replid和offset。当第一次变成slave,与master建立连接时,发送的replid和offset是自己的replid和offset。

  • master判断发现slave发送来的replid与自己的不一致,说明这是一个全新的slave,就知道要做全量同步了。

  • master会将自己的replid和offset都发送给这个slave,slave保存这些信息。以后slave的replid就与master一致了。

因此,master判断一个节点是否是第一次同步的依据,就是看replid是否一致

完整流程描述:

  • slave节点请求增量同步

  • master节点判断replid,发现不一致,拒绝增量同步

  • master将完整内存数据生成RDB,发送RDB到slave

  • slave清空本地数据,加载master的RDB

  • master将RDB期间的命令记录在repl_baklog,并持续将log中的命令发送给slave

  • slave执行接收到的命令,保持与master之间的同步

2 增量同步

全量同步需要先做RDB,然后将RDB文件通过网络传输个slave,成本太高了。因此除了第一次做全量同步,其它大多数时候slave与master都是做增量同步。例如:slave节点宕机后重连master,就优先判断能否使用增量同步。

什么是增量同步?就是只更新slave与master存在差异的部分数据。如图:

 

3 repl_backlog原理

什么是repl_backlog

repl_backlog是由Master主节点创建和维护的一个环形缓冲区。正常情况下,Master会把自己执行的所有写命令,也会向repl_backlog缓冲区里写一份;所有Slave执行增量同步时的数据都来自repl_backlog缓冲区。

repl_backlog是一个固定大小的数组,只不过数组是环形,也就是说角标到达数组末尾后,会再次从0开始读写,这样数组头部的数据就会被覆盖。

repl_backlog实现增量复制的过程

1) Master把写命令存储到repl_backlog里

repl_backlog中会记录Redis处理过的写命令日志及offset,包括master当前的offset,和slave已经拷贝到的offset。slave与master的offset之间的差异,就是salve需要增量拷贝的数据了。

2) Slave同步repl_backlog中的命令

随着不断有数据写入,master的offset逐渐变大。

slave也不断的读取repl_backlog中的命令,同步到slave中,追赶master的offset:

3) repl_backlog填满后环绕从头开始

直到repl_backlog数组被完全填满,如果再有新的数据写入,就会覆盖数组中的旧数据。不过,旧的数据只要是绿色的,说明是已经被同步到slave的数据,即便被覆盖了也没什么影响。因为未同步的仅仅是红色部分。

4) Slave落后过多后只能全量同步

但是,如果slave出现网络阻塞,导致master的offset远远超过了slave的offset;如果master继续写入新数据,其offset就会覆盖旧的数据,直到将slave现在的offset也覆盖:

棕色框中的红色部分,就是尚未同步,但是却已经被覆盖的数据。此时如果slave恢复,需要同步,却发现自己的offset都没有了,无法完成增量同步了。只能做全量同步。

注意:repl_backlog大小有上限,写满后会覆盖最早的数据。如果slave断开时间过长,导致尚未备份的数据被覆盖,则无法基于log做增量同步,只能再次进行全量同步

4. 主从同步优化

主从同步可以保证主从数据的一致性,非常重要。

可以从以下几个方面来优化Redis主从就集群:

  • Redis单节点上的内存占用不要太大,减少RDB导致的过多磁盘IO

  • 在master中配置repl-diskless-sync yes启用无磁盘复制,避免全量同步时的磁盘IO

  • 适当提高repl_backlog的大小,发现slave宕机时尽快实现故障恢复,尽可能避免全量同步

  • 限制一个master上的slave节点数量,如果实在是太多slave,则可以采用主-从-从链式结构,减少master压力

主从从架构图:

5. 小结

主从集群:解决的问题

  • 并发能力问题。由多个节点共同组成集群,并发能力就提升了。特别读的并发能力

  • 防止单点故障。只有一个节点的话,一旦节点宕机,整个服务不可用。集群可以防止这样的单点故障,提高可用性

主从集群:角色的划分。一主多从

  • 主节点:只有一个,可以提供读和写服务

  • 从节点:可以有多个,只提供读数据的服务

  • 主从之间需要进行数据同步

主从全量同步:

  • 发生在什么时候:一个节点第一次变成slave的时候,先进行一次全量同步

  • 全量同步的过程:

    第一阶段:建立主从关系

    • slave把自己数据集的id(replication id)发送给master

    • master收到以后判断 slave给的replication id 和自己 replicationid不同:确定slave是第一次连接master

    • master会首先把自己的replication id发送给slave;slave丢弃自己的replication id,接受master replication id

    第二阶段:开始全量同步

    • master执行bgsave,生成RDB。把RDB发送给slave

    • slave丢弃自己的所有的旧数据,加载主节点提供的RDB

    第三阶段:后续的增量同步

    • 在第二阶段全量同步期间,master所执行的新命令会存储到repl_backlog里

    • master在第二阶段结束,会把repl_backlog里的命令发送给slave

    • slave再执行接收到的命令。最终slave的数据和master同步了

repl_backlog:

  • 是什么:是Master创建并维护的一个环形缓冲区。其实就是一个数组,Master不断把它执行的命令添加到数组里,如果数组满了,就从头开始把旧的数据覆盖掉;slave要从repl_backlog里不断的读取命令,同步到slave里

  • 增量同步的过程:

    slave向master发请求,要求同步数据,会给master发送自己的replication id,和上次同步的位置offset

    master判断replication id和自己的相同,再判断offset和自己master的offset

    • 如果slave的offset 与 master的offset 相差超过一圈:就只能全量同步了

    • 如果slave的offset 与master的offset 相差不超过一圈:就从slave的offset开始,把后边的所有命令同步给slave,是增量同步

  • 增量同步:

    • 通常是在slave重启,会进行全量同步

    • 如果slave节点宕机时间过长,可能就要进行全量同步了

主从集群的优化:

  • 单个redis节点尽量不要分配太大内存,主从同步时比较慢

  • 可以启动无磁盘复制,减少磁盘IO操作

  • 提升repl_backlog的大小,减少全量同步的机率和次数

  • 如果slave过多,可以采用主-从-从链式结构

四、Redis哨兵模式

1. 介绍

Redis的主从模式,可以保证主从的数据一致性,并且可以实现读写分离,有效提升并发能力。但是主从集群有一个缺陷:当Master宕机之后,需要人工干预进行故障恢复。

这时候,可以引入哨兵模式,实现集群故障的自动恢复

2. 哨兵模式的架构原理

1 哨兵模式的架构

哨兵的作用如下:

  • 监控:Sentinel 会不断检查您的master和slave是否按预期工作

  • 自动故障恢复:如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后也以新的master为主

  • 通知:Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis的客户端

2 集群监控原理

Sentinel基于心跳机制监测服务状态,定时(每秒1次)向集群的每个实例发送ping命令:

  • 主观下线:如果某sentinel节点发现某实例未在规定时间(通过参数配置)响应,则认为该实例主观下线

  • 客观下线:若超过指定数量(quorum)的sentinel都认为该实例主观下线,则该实例客观下线。quorum值最好超过Sentinel实例数量的一半。

3 集群故障恢复原理

选举原则

一旦发现master故障,sentinel需要在salve中选择一个作为新的master,选择依据是这样的:

  • 首先会判断slave节点与master节点断开时间长短,如果超过指定值(down-after-milliseconds * 10)则会排除该slave节点

  • 然后判断slave节点的slave-priority值(通过参数配置),越小优先级越高,如果是0则永不参与选举

  • 如果slave-prority一样,则判断slave节点的offset值,越大说明数据越新,优先级越高

  • 最后是判断slave节点的runid大小,越小优先级越高(越小表示redis实例启动的越早)。

切换Master

当选出一个新的master后,该如何实现切换呢?

流程如下:

  • sentinel给备选的slave节点发送slaveof no one命令,让该节点成为master

  • sentinel给所有其它slave发送slaveof 命令,让这些slave成为新master的从节点,开始从新的master上同步数据。

  • 最后,sentinel将故障节点标记为slave,当故障节点恢复后会自动成为新的master的slave节点

3. 搭建哨兵集群

1 架构说明

这里我们搭建一个三节点形成的Sentinel集群,来监管之前的Redis主从集群。如图

 

三个Sentinel实例信息如下:

 

2 准备实例和配置

准备文件夹

要在同一台虚拟机开启3个实例,必须准备三份不同的配置文件和目录,配置文件所在目录也就是工作目录。

先创建文件夹~/03sentinel文件夹,然后在~/03sentinel里创建三个文件夹,名字分别叫s1、s2、s3:

 mkdir ~/03sentinel
cd ~/03sentinel/
mkdir s1 s2 s3

准备sentinel配置文件

准备s1实例的配置

切换到s1文件夹里:cd ~/03sentinel/s1

使用创建文件sentinel.confvi sentinel.conf,添加如下内容:

 port 16380
sentinel announce-ip 192.168.200.136
sentinel monitor mymaster 192.168.200.136 6380 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
dir "/root/03sentinel/s1"

说明:

  • port 16380: 当前Sentinel实例的端口号

  • sentinel monitor mymaster 192.168.200.136 6380 2:指定主节点master的信息

    • mymaster:主节点名称,任意写

    • 192.168.200.136 6380:主节点的ip和端口

    • 2:选举时的quorum

  • down-after-milliseconds:哨兵心跳监测的时间间隔

  • failover-timeout:故障转移的超时失败时间

准备s2和s3实例的配置

~/03sentinel/s1/sentinel.conf拷贝到s2和s3两个目录中

cp ~/03sentinel/s1/sentinel.conf  ~/03sentinel/s2
cp ~/03sentinel/s1/sentinel.conf  ~/03sentinel/s3

修改s2和s3的配置文件,将其端口分别修改为16381、16382

sed -i -e 's/16380/16381/g' -e 's/s1/s2/g' ~/03sentinel/s2/sentinel.conf
sed -i -e 's/16380/16382/g' -e 's/s1/s3/g' ~/03sentinel/s3/sentinel.conf

3 启动

要使用Sentinel,我们必须保证刚才的Redis主从集群是启动运行状态,并且要保证6380是Master节点

如果Redis主从集群未启动,就执行以下命令:

1. 启动三个Redis实例

redis-server ~/02master/6380/redis.conf
redis-server ~/02master/6381/redis.conf
redis-server ~/02master/6382/redis.conf

2. 设置主从关系:把6380设置为master节点

  1. redis-cli -p 6381,然后执行命令 slaveof 192.168.200.136 6380
  2. redis-cli -p 6382,然后执行命令 slaveof 192.168.200.136 6380
  3. redis-cli -p 6380,然后执行命令查看集群状态 info replication

确定6380是master节点,6381和6382是slave节点

为了方便查看日志,我们打开3个ssh客户端,分别启动3个redis实例,启动命令:

# 第1个
redis-sentinel ~/03sentinel/s1/sentinel.conf
# 第2个
redis-sentinel ~/03sentinel/s2/sentinel.conf
# 第3个
redis-sentinel ~/03sentinel/s3/sentinel.conf

4 测试

尝试让master节点6380宕机: redis-cli -p 6380 shutdown

查看sentinel日志。发现已经有了新的master节点:6382

4. RestTemplate访问哨兵集群

在Sentinel集群监管下的Redis主从集群,其节点会因为自动故障转移而发生变化,Redis的客户端必须感知这种变化,及时更新连接信息。Spring的RedisTemplate底层利用lettuce实现了节点的感知和自动切换。

导入资料里的《redis-cluster》工程,然后:

1 添加依赖坐标

添加spring-data-redis起步依赖

最终依赖如下:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.9.RELEASE</version>
    <relativePath/>
</parent>

<properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
</properties>

<dependencies>
    <!--Redis起步依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

2 准备配置文件

spring:
  redis:
    sentinel:
      master: mymaster  #主节点的名称。在创建Sentinel集群时指定的
      nodes: #Sentinel哨兵的地址
        - 192.168.200.136:16380
        - 192.168.200.136:16381
        - 192.168.200.136:16382

3 配置读写策略

在引导类或者配置类里,添加以下代码,配置Redis集群的读写策略:

/**
     * 配置Redis的Sentinel集群读写策略
     *      ReadFrom.MASTER: 仅从master节点读数据
     *      ReadForm.REPLICA:仅从slave节点读数据
     *      ReadForm.MASTER_PREFERRED:优先从Master节点读数据
     *      ReadFrom.REPLICA_PREFERRED:优先从Slave节点读数据
     */
@Bean
public LettuceClientConfigurationBuilderCustomizer clientConfigurationBuilderCustomizer(){
    return clientConfigurationBuilder -> clientConfigurationBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);
}

 

3 测试

创建测试类,读写数据

package com.itheima;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;

@SpringBootTest
public class RedisSentinelTest {
    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Test
    public void test(){
        // redisTemplate.opsForValue().set("key::sentinel", "hello, sentinel");
        String s = redisTemplate.opsForValue().get("key::sentinel");
        System.out.println("s = " + s);
    }
}

5. 小结

主从集群存在的问题:故障转移的问题,一旦Master节点宕机,就需要人工干预切换Master主节点。可以增加哨兵解决问题

哨兵模式:在主从集群基础上,增加哨兵集群。哨兵的职责:

  • 监控:哨兵会监控主从集群的健康状态,采用的是心跳机制,默认每1条ping一次

    主观下线:某个哨兵在规定时间内ping节点,但是没有收到回应,就把节点标记为主观下线

    客观下线:超过指定数量的哨兵,都把某个节点标记为主观下线,就是客观下线

  • 故障转移:哨兵在发现Master宕机之后,会自动选举挑选新的Master节点。选举的策略

    先把上次同步时间过长的slave节点排除掉,没有选举资格

    对比节点的slave-priority值。值越小,优先级越高。0表示不参与选举

    如果slave-priority相同,对比offset。offset越大,优先级越高。因为offset越大,说明数据和master越接近

    如果offset相同,对比runid。runid越小,优先级越高。因为runid越小,说明启动的越早

  • 通知:通知主从切换、通知集群状态

    通知主从切换:通知新的Master执行 slaveof no one;通知其它所有节点执行 slaveof 新master的ip 新master端口

    还会通知整个集群的信息状态(默认10s一次)

五、Redis分片集群

1. 介绍

主从和哨兵可以解决高可用、高并发读的问题。但是依然有两个问题没有解决:

  • 海量数据存储问题

  • 高并发写的问题

使用分片集群可以解决上述问题

2. 分片集群的架构

分片集群特征:

  • 集群中有多个master,每个master保存不同数据

  • 每个master都可以有多个slave节点

  • master之间通过ping监测彼此健康状态

  • 客户端请求可以访问集群任意节点,最终都会被转发到正确节点

3. 搭建分片集群

1 架构说明

分片集群需要的节点数量较多,这里我们搭建一个最小的分片集群,包含3个master节点,每个master包含一个slave节点,结构如下:

这里我们会在同一台虚拟机中开启6个redis实例,模拟分片集群,信息如下:

2 准备实例和配置

准备文件夹

创建04cluster文件夹,并在文件夹里准备7380 7381 7382 8380 8381 8382六个文件夹

 mkdir ~/04cluster
cd ~/04cluster/
mkdir 7380 7381 7382 8380 8381 8382

准备配置文件

准备7380的配置

  • 切换到7380目录:cd ~/04cluster/7380

  • 使用vi编辑文件:vi redis.conf, 内容如下

port 7380
# 开启集群功能
cluster-enabled yes
# 集群的配置文件名称,不需要我们创建,由redis自己维护
cluster-config-file /root/04cluster/7380/nodes.conf
# 节点心跳失败的超时时间
cluster-node-timeout 5000
# 持久化文件存放目录
dir /root/04cluster/7380
# 绑定地址
bind 0.0.0.0
# 让redis后台运行
daemonize yes
# 注册的实例ip
replica-announce-ip 192.168.200.136
# 保护模式
protected-mode no
# 数据库数量
databases 1
# 日志
logfile /root/04cluster/7380/run.log

准备其它实例的配置

将7380里的配置文件,拷贝到其它每个目录里

cd ~/04cluster
# 执行拷贝
echo 7381 7382 8380 8381 8382 | xargs -t -n 1 cp ~/04cluster/7380/redis.conf

修改每个目录下的redis.conf,修改其端口:

cd ~/04cluster
# 执行修改
printf '%s\n' 7381 7382 8380 8381 8382 | xargs -I{} -t sed -i 's/7380/{}/g' {}/redis.conf

3 启动所有实例

因为已经配置了Redis后台运行,所以可以直接启动,不需要开多个shell端口。执行以下命令:

cd ~/04cluster
#启动7380 7381 7382 8380 8381 8382 六个Redis服务
printf '%s\n' 7380 7381 7382 8380 8381 8382 | xargs -I{} -t redis-server {}/redis.conf

查看是否成功启动,执行命令:ps -ef | grep redis。如果看到以下结果,说明6个redis实例都启动成功

如果要关闭所有进程,可以执行命令:

  • 方式一:ps -ef | grep redis | awk '{print $2}' | xargs kill

  • 方式二:printf '%s\n' 7380 7381 7382 8380 8381 8382 | xargs -I{} -t redis-cli -p {} shutdown

4 创建集群

虽然服务已经成功启动,但目前6个Redis实例还是独立的,它们之间没有任何关系。

我们需要执行命令来创建集群。在Redis5.0之前创建集群比较麻烦,5.0之后集群管理命令都集成到了redis-cli中

命令说明

redis5.0之前的命令

Redis5.0之前集群命令都是用redis安装包下的src/redis-trib.rb来实现的。因为redis-trib.rb是有ruby语言编写的所以需要安装ruby环境:

 # 安装依赖
 yum -y install zlib ruby rubygems
 gem install redis

然后通过命令管理集群:

# 进入redis的src目录
cd ~/redis-6.2.4/src
# 创建集群
./redis-trib.rb create --replicas 1 192.168.200.136:7380 192.168.200.136:7381 192.168.200.136:7382 192.168.200.136:8380 192.168.200.136:8381 192.168.200.136:8382

redis5.0开始的命令

我们使用的是Redis6.2.4版本,集群管理以及集成到了redis-cli中,格式如下:

redis-cli --cluster create --cluster-replicas 1 192.168.200.136:7380 192.168.200.136:7381 192.168.200.136:7382 192.168.200.136:8380 192.168.200.136:8381 192.168.200.136:8382

命令说明:

  • redis-cli --cluster或者./redis-trib.rb:表示要操作redis集群

  • create:表示要创建集群

  • --cluster-replicas 1或者--replicas:指令集群中每个master的副本个数为1。这样:

    master节点的数量:节点总数 / (replicas + 1),得到的就是master节点的数量

    节点列表中前n个就是master节点,其它是slave节点。这些slave随机分配给不同的master

执行命令创建集群

如果在执行下面的命令创建集群时报错:[ERR] Node 192.168.200.136:7380 is not empty. Either the node already knows other nodes

主要原因可能是:该节点默认生成的配置与历史存储的数据不一致导致的

解决的方法是:

1. 关闭所有redis实例:

printf '%s\n' 7380 7381 7382 8380 8381 8382|xargs -I{} -t redis-cli -p {} shutdown

2. 清除对应节点的dump.rdb、nodes.conf等文件

find / -name nodes.conf | xargs rm -rf
find / -name dump.rdb | xargs rm -rf

3. 然后重启redis实例,再执行创建集群的命令

cd ~/04cluster
#启动7380 7381 7382 8380 8381 8382 六个Redis服务
printf '%s\n' 7380 7381 7382 8380 8381 8382 | xargs -I{} -t redis-server {}/redis.conf

执行以下的命令,创建集群

redis-cli --cluster create --cluster-replicas 1 192.168.150.132:7380 192.168.150.132:7381 192.168.150.132:7382 192.168.150.132:8380 192.168.150.132:8381 192.168.150.132:8382

需要我们确证一下,输入“yes“,就开始创建集群了:

开始创建集群

 

查看集群状态

redis-cli -p 7380 cluster nodes

5 测试

使用命令连接7380节点:redis-cli -c -p 7380,做如下操作:

  1. 存储数据:set num 10

  2. 获取数据:get num

  3. 再次存储:set a 1

在使用redis-cli连接分片集群时,必须加上参数-c,否则操作时可能会出错。示例:

尝试连接7380节点:redis-cli -p 7380,做如下操作:

  1. 存储数据:set num 10

  2. 获取数据:get num

  3. 再次存储:set a 1

发现报错了:

4. 散列插槽(Hash插槽)

Redis会把每一个master节点映射到0~16383共16384个插槽(hash slot)上,查看集群信息时就能看到:

数据key不是与节点绑定,而是与插槽绑定。redis会根据key的有效部分计算插槽值,分两种情况:

  • key中包含"{}",且“{}”中至少包含1个字符,“{}”中的部分是有效部分

  • key中不包含“{}”,整个key都是有效部分

例如:key是num,那么就根据num计算;如果是{itcast}num,则根据itcast计算。计算方式是利用CRC16算法得到一个hash值,然后对16384取余,得到的结果就是slot值。

提问:如何将一批key固定的保存在同一个Redis实例上呢?

答案:可以给这些数据的key,增加相同的{标识}。例如这些key都以{typeId}为前缀

查看key的slot值:cluster keyslot key

5. 集群伸缩

因为Redis的分片集群,数据并没有与Redis节点直接进行绑定,而是:数据与插槽绑定,插槽与Redis节点绑定。这样就可以很方便的进行集群的伸缩,增加或减少Redis节点。

我们通过下面例子,演示一下给集群增加节点,需要如何实现。要求:向集群中添加一个新的master节点,并向其中存储num = 10

  1. 启动一个新的Redis实例,端口为7383

  2. 把7383节点添加到之前的集群,并作为一个master节点

  3. 给7383节点分配插槽,使得这个num可以存储到7383实例

这需要有两大步:

  1. 添加一个节点到集群中

  2. 转移插槽

1 添加新节点到集群中

创建7383节点

#创建7383文件夹
mkdir ~/04cluster/7383
#把配置文件拷贝到7383文件夹里
cp ~/04cluster/7380/redis.conf ~/04cluster/7383
#修改配置文件
sed -i s/7380/7383/g ~/04cluster/7383/redis.conf
#启动7383实例
redis-server ~/04cluster/7383/redis.conf

添加到集群

查看redis集群操作帮助: redis-cli --cluster help

执行命令:

#把7383节点添加到集群
redis-cli --cluster add-node  192.168.150.132:7383 192.168.150.132:7380
#查看集群状态
redis-cli -p 7380 cluster nodes

2 转移插槽

查看num的插槽

我们要将num存储到7383节点,因此需要先看看num的插槽是多少:redis-cli -c -p 7380 cluster keyslot 键

我们可以将0~3000插槽,从7380节点移动到7383节点上

转移插槽

转换插槽的命令,操作如下:

1. 建立连接:redis-cli --cluster reshard 192.168.200.136:7380

 2. 输入要移动的插槽数量,我们输入 3000

3. 输入目标节点的id(哪个节点要接收这些插槽,就输入哪个节点的id)

我们从刚刚的输出结果中,找到7383节点的id设置过来

4. 从哪个节点里转移出插槽?

输入all,表示全部。即从现有每个master节点中各转移一部分出来

输入节点id,表示从指定节点中转移出来

输入done,表示输入完毕了

我们这里从7380节点转移出来一部分,要输入7380节点的id

 5. 输入yes,确认转移

确认结果

执行命令:redis-cli -p 7380 cluster nodes,可以看到7383节点拥有0~2999插槽,说明转移插槽成功了

 

6. 故障转移

当master宕机时,Redis的分片集群同样具备故障转移的能力。

接下来给大家演示一下分片集群的故障转换,先确认一下集群的初始状态:

  • 7380是master节点,8382是其slave节点

  • 7381是master节点,8380是其slave节点

  • 7382是master节点,8381是其slave节点

  • 7383是master节点,没有slave节点

1 自动故障转移

当master宕机时,分片集群会自动将其slave节点提升为master。

我们直接将7381节点关闭:redis-cli -p 7381 shutdown

然后查看集群状态 redis-cli -p 7380 cluster nodes,发现7381是fail状态,8380已经提升成为master

 

再次启动7381节点:redis-server 7381/redis.conf

再次查看集群状态:redis-cli -p 7380 cluster nodes,发现7381节点成为了slave

 

2 手动故障转移

利用cluster failover命令可以手动让集群中的某个master宕机,切换到执行cluster failover命令的这个slave节点,实现无感知的数据迁移。

这种failover命令可以指定三种模式:

  • 缺省:默认的流程,如图1~6歩

  • force:省略了对offset的一致性校验

  • takeover:直接执行第5歩,忽略数据一致性、忽略master状态和其它master的意见

其流程如下:

我们以7381这个slave节点为例,演示如何手动进行故障转移,让7381重新夺回master:

  1. 利用redis-cli连接7381这个节点:redis-cli -p 7381

  2. 在7381节点上执行cluster failover命令

  3. 重新查看集群状态:cluster nodes

 

7. RedisTemplate访问分片集群

RedisTemplate底层同样基于lettuce实现了分片集群的支持,而使用的步骤与哨兵模式基本一致:

1)引入redis的starter依赖

2)配置分片集群地址

3)配置读写分离

与哨兵模式相比,其中只有分片集群的配置方式略有差异,如下:

spring:
  redis:
    cluster:
      nodes:
        - 192.168.200.136:7380
        - 192.168.200.136:7381
        - 192.168.200.136:7382
        - 192.168.200.136:8380
        - 192.168.200.136:8381
        - 192.168.200.136:8382

六、大总结

Redis的持久化机制

RDB:快照模式
    怎么持久化的:将内存里的数据持久化保存到磁盘文件上;当需要恢复数据时,加载磁盘文件,直接恢复到内存中
    何时持久化:
        执行save命令:阻塞式命令,即持久化过程中redis不能处理其它操作命令
        执行bgsave命令:非阻塞式的命令,持久化过程中不影响redis继续处理其它操作命令
        redis服务关闭时:正常关闭时,会先执行一次持久化
        触发自动持久化:配置文件里有触发的参数时机
            save 3600 1
            save 300 100
            save 60 10000
    bgsave的原理:
        在bgsave之前,redis主进程正在处理客户端的操作命令
        当执行bgsave时
            redis会fork一个子进程,把主进程的页表拷贝到子进程;还会把内存里的数据设置为read-only
            子进程负责根据页表读取数据,持久化保存到RDB文件
            主进程负责继续处理客户端的操作命令,如果要写数据,不能直接操作read-only的数据,会采用copy-on-write机制,把数据拷贝一个副本,主进程操作这个副本

AOF:日志模式
    怎么持久化的:把redis执行的所有 写操作 的命令,追加保存到磁盘文件;当需要恢复数据时,加载磁盘文件,依次执行所有命令
    何时持久化:
        always:同步刷盘。当redis执行完成命令之后,立即把命令追加保存到磁盘文件上
        everysec:每秒刷盘。当redis执行完操作命令之后,把命令放到缓冲区里。每秒一次把缓冲区里命令保存到磁盘文件上
        no:由操作系统决定何时刷盘。当redis执行完操作命令之后,把命令放到缓冲区里。在操作系统空闲时执行刷盘

RDB和AOF的对比:
    安全性:AOF更不容易丢失数据;RDB可能丢失数据
    文件大小:AOF文件更大,因为有比较多的冗余命令
    恢复速度:RDB更快,因为直接加载RDB数据恢复到内存中;而AOF需要读取命令、依次执行所有命令才能恢复
    优先级:AOF优先级更高。如果开启了AOF,Redis就采用AOF

Redis主从集群:

  • 解决了什么问题:

    解决了高并发读的问题:多个节点共同提供服务,并发能力更强

    解决了可用性问题:防止单点故障,一个节点宕机,还有其它节点可用

  • 主从集群的结构:一主多从

    主节点:一个主从集群只能有一个主节点,具备读、写数据的能力

    从节点:一个主从集群可以有多个从节点,具备读数据的能力

    我们如果要写数据,就操作主节点;主节点数据再同步给从节点;然后我们从从节点读取数据

  • 主从同步的过程:

    一阶段:确认主从关系

    • slave向master发送同步数据的请求,向master提交自己的replication id 和 offset

    • master判断replication id 和自己的replication id是否相同:

      • 如果不同,需要建立主从关系:master会把自己的replication id发送给slave;slave丢弃自己的replication id,接受master传递过来的replication id

      • 如果相同,说明之前已经建立过主从关系了,这次判断是要全量同步还是增量同步

        如果slave的offset,比master的offset落后一圈:只能进行全量同步

        否则:就进行增量同步

    二阶段:全量同步数据

    • Master会执行bgsave,生成RDB;把RDB传输给slave

    • slave收到RDB之后:丢弃自己的所有的旧数据,加载Master传递过来的RDB数据

    三阶段:增量同步数据

    • 在二阶段RDB过程中,如果有新的命令,master会把命令存储到repl_backlog里

    • 在二阶段结束后,把repl_backlog里的命令发送给slave;slave接收并执行

  • repl_backlog原理:

    是一个环形缓冲区,其实是一个数组

    Master会把执行的写操作命令添加到数组里;如果数组满了,就会从头开始覆盖掉数据

    slave根据自己的offset,从repl_backlog里读取尚未同步的命令,执行命令同步数据

    如果slave长时间没有进行数据同步(比如宕机时间较长,或者网络断开时间较长):导致未同步的数据被覆盖掉,只能进行全量同步

  • 主从集群的优化:

    每个redis节点的内存不要设置过大,否则RDB和数据同步耗时较长

    可以开启无磁盘复制

    可以增大repl_backlog的大小,减少全量同步的机率

    如果slave过多,可以采用主-从-从链式结构

哨兵模式:在主从集群基础上,增加哨兵集群

  • 哨兵解决了什么问题:

    主从集群的故障转移问题,不能自动实现故障转移,必须人工干预

  • 哨兵的作用

    监控:哨兵会监控主从集群的所有节点健康状态,通过心跳机制,每秒一次ping

    • 主观下线:当哨兵发现,某个节点在规定时间内没有回应ping,就把节点标记为主观下线

    • 客观下线:当超过规定数量的哨兵都认为某节点主观下线,就是客观下线

    故障转移:当发现Master宕机,就会重新选举Master

    • 如果slave上次同步的时间太久,直接失去选举资格

    • 哪个节点的slave-priority值越小,优先级就越高;0表示不参与选举

    • 如果slave-priority值相同,就比较offset。哪个节点的offset越大,优先级越高

    • 如果offset相同,就比较runid。哪个节点的runid越小,优先级越高

    通知:

    • 重新选举之后通知主从集群切换主从关系

      通知新的主节点执行 slaveof no one,再通知其它节点 slaveof 新主ip 新主端口

    • 通知集群的状态

分片集群:

  • 分片集群解决了什么问题

    解决了主从集群的高并发写的问题。因为主从集群里只有一个master具备写数据的能力

    解决了主从集群的海量数据存储问题。因为主从集群里各节点数据相同,如果数据太多,就存不下

  • 分片集群的特征:

    集群里可以有多个master主节点,每个主节点存储一部分数据

    每个master节点可以有多个slave节点,实现高可用

    分片集群不需要再有哨兵监控和选举,而是master之间互相心跳监控、实现选举

    连接任意一个master,master都会帮我们重定向到正确的master上存取数据

  • hash插槽:

    总共有16384个hash插槽,每个master节点负责一部分插槽

    当存取某个key时,redis会计算key的hash值:

    • 如果key里没有{}crc16算法(key) % 16384 结果就是最终的hash值。根据hash值判断存储到哪个节点

    • 如果key里有{}crc16算法({}里的内容)%16384结果就是最终的hash值。根据hash值判断存储到哪个节点

    hash插槽:数据和节点之间没有直接的关联关系,方便进行集群的伸缩与数据的转移

  • 集群伸缩:

    向集群里增加新的节点之后,需要给新的节点分配一些hash插槽

  • 故障转移:

    自动实现故障转移

    手动实现故障转移:在节点上执行cluster failover,这个节点就会成为master

Java代码操作Redis集群:

  • 添加spring-boot-starter-data-redis

  • 配置:

    • 主从+哨兵的配置,或者 cluster集群的配置不同

    • 再设置读写的策略,建议使用 REPLICA_PREFERED,slave优先读

  • 操作:仍然是使用RedisTemplate操作Redis集群,和之前学习的没有区别

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

软件测试与管理:黑盒测试-判定表驱动法

知识思维导图&#xff1a; 例题1&#xff1a;运用判定表驱动法设计测试用例。 某学生成绩管理系统&#xff0c;要求对“平均成绩在90分以上&#xff0c;且没有不及格科目的学生&#xff0c;或班级成绩排名在前5的学生&#xff0c;在程序中将学生的姓名用红色标识”&#xff0c;…

【前端】HTML实现个人简历信息展示页面

文章目录 前言一、 综合案例&#xff1a;个人简历信息展示页面 前言 这篇博客仅仅是对HTML的基本结构进行了一些说明&#xff0c;关于HTML的更多讲解以及CSS、Javascript部分的讲解可以关注一下下面的专栏&#xff0c;会持续更新的。 链接&#xff1a; Web前端学习专栏 下面我…

Python | Leetcode Python题解之第73题矩阵置零

题目&#xff1a; 题解&#xff1a; class Solution:def setZeroes(self, matrix: List[List[int]]) -> None:m, n len(matrix), len(matrix[0])flag_col0 Falsefor i in range(m):if matrix[i][0] 0:flag_col0 Truefor j in range(1, n):if matrix[i][j] 0:matrix[i]…

实时音视频通信的主要矛盾及解决方法

实时音视频通信的主要矛盾及解决方法 实时音视频通信的主要矛盾及解决方法实时音视频通信的主要矛盾矛盾的解决方法增加带宽减少数据量适当增加延时提高网络质量快速准确地评估带宽 总结参考 实时音视频通信的主要矛盾及解决方法 实时音视频通信的主要矛盾 实时音视频通信的主…

工厂模式+策略模式完成多种登录模式的实现

前提 &#xff08;简单工厂不属于设计模式&#xff0c;而是一种编程思想【抽象一层出来】&#xff09;工厂方法模式、抽象工厂模式 以上都是为了解耦&#xff0c;如果考虑多个纬度&#xff08;如需要同时考虑多种电器&#xff0c;多种品牌&#xff09;则优先考虑抽象工厂。 …

公网tcp转流

之前做过几次公网推流的尝试, 今天试了UDP推到公网, 再用TCP从公网拉下来, 发现不行, 就直接改用TCP转TCP了. 中间中转使用的python脚本, 感谢GPT提供技术支持: import socket import threadingdef tcp_receiver(port, forward_queue):"""接收TCP数据并将其放入…

Liunx磁盘管理(下)

中篇&#xff1a;https://blog.csdn.net/Lzcsfg/article/details/138355036 一.逻辑卷 逻辑卷&#xff08;Logical Volume&#xff09;是逻辑卷管理 (LVM) 中的一个概念&#xff0c;它为 Linux 系统中的存储管理提供了更大的灵活性和可扩展性。LVM 允许你将物理存储设备&…

用js代码实现贪吃蛇小游戏

js已经学了大部分了&#xff0c;现在就利用我所学的js知识试试做贪吃蛇小游戏吧 以下部分相关图片以及思路笔记均出自渡一陈老师的视频 首先制作简单的静态页面&#xff0c;添加贪吃蛇移动的背景和相关图片&#xff0c;比如开始游戏等等 将各个功能均封装在函数中&#xff0…

multipass launch失败:Could not generate a new UUID vboxmanage添加环境变量path

C:\WINDOWS\system32>multipass launch [2024-05-06T15:48:54.320] [error] [relished-jerboa] Could not unregister VM: Process failed to start: 系统找不到指定的文件。 launch failed: Could not generate a new UUID: Process failed to start: 系统找不到指定的文件…

服务攻防-数据库安全RedisCouchDBH2database未授权访问CVE漏洞

#知识点&#xff1a; 1、数据库-Redis-未授权RCE&CVE 2、数据库-Couchdb-未授权RCE&CVE 3、数据库-H2database-未授权RCE&CVE#章节点&#xff1a; 1、目标判断-端口扫描&组合判断&信息来源 2、安全问题-配置不当&CVE漏洞&弱口令爆破 3、复现对象-数…

Chat2DB Pro 重磅发布 !!!

在过去的几个月中&#xff0c;我们的团队一直默默耕耘&#xff0c;全心投入到Chat2DB Pro版本的开发之中。这段时间里&#xff0c;我们暂停了新动态的发布&#xff0c;以至于有趣的误解在社群中出现&#xff0c;有人调侃我们是否“倒闭”了。然而&#xff0c;我们今天携带着全新…

C++ | Leetcode C++题解之第71题简化路径

题目&#xff1a; 题解&#xff1a; class Solution { public:string simplifyPath(string path) {auto split [](const string& s, char delim) -> vector<string> {vector<string> ans;string cur;for (char ch: s) {if (ch delim) {ans.push_back(mov…

Docker——consul的容器服务更新与发现

一、什么是服务注册与发现 服务注册与发现是微服务架构中不可或缺的重要组件。起初服务都是单节点的&#xff0c;不保障高可用性&#xff0c;也不考虑服务的压力承载&#xff0c;服务之间调用单纯的通过接口访问。直到后来出现了多个节点的分布式架构&#xff0c;起初的解决手段…

密码学《图解密码技术》 记录学习 第十五章

目录 十五章 15.1本章学习的内容 15.2 密码技术小结 15.2.1 密码学家的工具箱 15.2.2 密码与认证 15.2.3 密码技术的框架化 15.2.4 密码技术与压缩技术 15.3 虚拟货币——比特币 15.3.1 什么是比特币 15.3.2 P2P 网络 15.3.3地址 15.3.4 钱包 15.3.5 区块链 15.3.…

力扣每日一题114:二叉树展开为链表

题目 中等 提示 给你二叉树的根结点 root &#xff0c;请你将它展开为一个单链表&#xff1a; 展开后的单链表应该同样使用 TreeNode &#xff0c;其中 right 子指针指向链表中下一个结点&#xff0c;而左子指针始终为 null 。展开后的单链表应该与二叉树 先序遍历 顺序相同…

JavaScript基础(五)

三目运算符 用于判断并赋值 语法: 判断条件?条件成立执行语句:条件不成立执行语句; (条件&#xff1f;"true":"false";) 例: <script> var age prompt(请输入年龄) var name (age>18)?"已成年":"未成年禁止登录" a…

Spring与AI结合-spring boot3整合AI组件

⛰️个人主页: 蒾酒 &#x1f525;系列专栏&#xff1a;《spring boot实战》 目录 写在前面 spring ai简介 单独整合al接口 整合Spring AI组件 起步条件 ​编辑 进行必要配置 写在最后 写在前面 本文介绍了springboot开发后端服务中&#xff0c;AI组件(Spring A…

笔试强训Day15 二分 图论

平方数 题目链接&#xff1a;平方数 (nowcoder.com) 思路&#xff1a;水题直接过。 AC code&#xff1a; #include<iostream> #include<cmath> using namespace std; int main() {long long int n; cin >> n;long long int a sqrtl(n);long long int b …

【1】STM32·FreeRTOS·新建工程模板【一步到位】

目录 一、获取FreeRTOS源码 二、FreeRTOS源码简介 2.1、FreeRTOS源码文件内容 2.2、FreeRTOS内核 2.3、Source文件夹 2.4、portable文件夹 三、FreeRTOS手把手移植 3.1、FreeRTOS移植准备 3.2、FreeRTOS移植步骤 3.2.1、将 FreeRTOS 源码添加至基础工程、头文件路径等…

LLaMA 羊驼系大语言模型的前世今生

关于 LLaMA LLaMA是由Meta AI发布的大语言系列模型&#xff0c;完整的名字是Large Language Model Meta AI&#xff0c;直译&#xff1a;大语言模型元AI。Llama这个单词本身是指美洲大羊驼&#xff0c;所以社区也将这个系列的模型昵称为羊驼系模型。 Llama、Llama2 和 Llama3…