Redis 分布式缓存集群

 

Redis 持久化

RDB 持久化

RDB 全称为 Redis Database Backup File(Redis数据备份文件),也被叫做Redis数据快照。就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后,从磁盘读取快照文件,恢复数据。

Redis 会在正常退出之前,会把数据存储到磁盘中,当Redis再次启动时,会读取磁盘中的数据文件恢复。

我们可以通过命令手动存储数据到磁盘中:

redis-cli 客户端命令

# 保存 Redis 数据到磁盘中,使用单线程存储
127.0.0.1:6379> save

save 命令会占用Redis的主进程,当数据比较多时,会阻塞Rerdis的其它操作。

我们可以使用异步保存命令:

redis-cli 客户端命令

# 异步保存 Redis 数据到磁盘中
127.0.0.1:6379> bgsave

Redis会开启一个新的线程用于存盘操作。

 

Redis RDB 配置说明

  • RDB 默认存储在当前执行命令的目录下
  • RDB 可配置是否进行压缩,在配置项 redis.conf 文件
  • # 是否压缩 ,建议不开启,压缩也会消耗cpu,磁盘的话不值钱
    rdbcompression yes
    
  • RDB 可配置保存文件名,在配置项 redis.conf 文件
  • # RDB文件名称,默认为 dump.rdb
    dbfilename dump.rdb  
    
  • RDB 可配置文件保存路径目录,在配置项 redis.conf 文件
  • # 文件保存的路径目录
    dir ./ 
    
  • RDB 可配置多个保存策略,在配置项 redis.conf 文件
  • # 900秒内,如果至少有1个key被修改,则执行bgsave , 如果是save "" 则表示禁用RDB
    save 900 1     # 900 秒内如果有1个以上key被修改则触发保存
    save 300 10   # 300 秒内如果有10个以上key被修改则触发保存
    save 60 10000   # 60 秒内如果有10000个以上key被修改则触发保存
    

RDB 异步执行原理

bgsave开始时会fork主进程得到子进程,子进程共享主进程的内存数据。完成fork后读取内存数据并写入 RDB 文件。

fork采用的是copy-on-write技术:

  • 当主进程执行读操作时,访问共享内存;
  • 当主进程执行写操作时,则会拷贝一份数据,执行写操作。

图一解析:

  • Redis 主进程通过系统分配的虚拟内存进行读写,并由系统通过虚拟内存表进行操作在内存中的数据

图二解析:

  • 此时的主进程 fork 一个子进程出来,并把同样的虚拟内存页表复制一份给子进程
  • 子进程只需要利用复制的页表来读取物理内存中的数据

 

图四解析:

  • 子进程会对物理内存进行写锁定,这时物理内存中的数据为只读模式
  • 如果此时主进程收到Redis数据的写操作时,会把只读的数据复制一份副本出来

图五解析:

  • 主进程对读操作会直接读原来的内存数据
  • 而对写操作时会写在副本中。

 

 

AOF 持久化

AOF 全称为 Append Only File (追加文件)。Redis 处理的每一个写命令都会记录在AOF文件,可以看做是命令日志文件。

AOF 默认是关闭的,需要修改redis.conf配置文件来开启AOF:

# 是否开启 AOF,默认为 NO
appendonly yes

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

AOF的命令记录的频率也可以通过redis.conf文件来配置:

# 表示每执行一次写命令,立即记录到AOF文件
appendfsync always

# 表示命令执行完先放入AOF缓冲区,然后表示每隔1秒将缓冲区数据写到AOF文件,默认方案
appendfsync everysec

# 表示写命令执行完先放入AOF缓冲区,由操作系统决定何时将缓高区内容写回磁盘
appendfsync no
配置项 刷盘时机 优点 缺点
Always 同步刷盘 可靠性高,几乎不丢数据 性能影响大
everysec 每秒刷盘 性能适中 最多丢失1秒数据
no 操作系统控制 性能最好 可靠性较差,可能丢失大量数据

 

AOF 是通过记录日志文件的方式进行持久化的,这样会使得AOF文件会比RDB文件大得多。而且AOF会记录对同一个key的多次写操作,但是只有最后一次的写操作是有意义的,但是AOF会把之前写的命令都会记录在上面,显得没有意义。

Redis 提供了一种命令来重建AOF文件

127.0.0.1:6379> bgrewriteaof

 

Redis 也会在触发阈值时自动去重写aof文件。阈值也可以在redis.conf中配置

# Aof文件比上次文件 增长超过多少百分比则触发重写
auto-aof-rewrite-percentage 100

# AOF 文件体积最小多大以上才触发重写
auto-aof-rewrite-min-size 64mb

 

 

RDB AOF
持久化方式 定时对整个内存做快照 记录每一次执行的命令
数据完整性 不完整,两次备份之间会丢失 相对完整,取决于刷盘策略
文件大小 会有压缩,文件体积小 记录命令,文件体积很大
宕机恢复速度 很快
数据恢复优先级 低,因为数据完整性不如AOF 高,因为数据完整性更高
系统资源占用 高,大量CPU和内存消耗 低,主要是磁盘IO资源

但AOF重写时会占用大量CPU和内存资源

使用场景 可以容忍数分钟的数据丢失,追求更快的启动速度 对数据安全性要求较高常见

 

Redis 主从集群

单节点的Redis的并发能力是有上限的,要进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离

搭建主重架构

准备阶段

准备三台服务器,或在一台服务器中运行三个Redis

三个Redis 需要配置对应的配置项

 

安装三台Redis

yum install -y gcc tcl

 

运行三台Redis

redis-server redis.conf

如果在一台机器上配置三个Redis,需要把它们的端口分别设置。

 

关闭AOF,开启RDB

# 开启RDB
# save "" 
# 下面三行注释也可以,因为Redis 默认就是开启这三行
save 3600 1
save 300 100
save 60 10000

# 关闭AOF
appendonly no

 

配置RDB保存地址

# 三台Redis 的RDB文件可以分别存放在对应的文件夹中
dir ./tmp/xx

 

针对一台服务器以主机解析的话可能会出现多个ip地址,这里可以直接指定Redis服务声明的ip地址

# redis实例的声明 IP
replica-announce-ip 192.168.150.101

 

启动三台Redis服务

# 第1个 存放在 7001 文件夹中
redis-server 7001/redis.conf
# 第2个 存放在 7002 文件夹中
redis-server 7002/redis.conf
# 第3个 存放在 7003 文件夹中
redis-server 7003/redis.conf

 

三台Redis开启主从关系

我们假定把 7001 文件夹中的Redis作为主服务,7002和7003的Redis作为从服务

从机使用 slaveof(5.0之前) 命令或 replicaof 命令连接主机产生主从关系

7002> slaveof 192.168.150.101 7001 # 7002 主动向 7001 连接为从服务器
7003> slaveof 192.168.150.101 7001 # 7003 主动向 7001 连接为从服务器

或 

7002> replicaof 192.168.150.101 7001
7003> replicaof 192.168.150.101 7001

 

主重建立后,主服务器支持读写操作,从服务器只支持读操作。

可通过以下命令查看主从关系

7001> INFO replication

 

数据同步原理

全量同步

主从第一次同步是全量同步,如下图说明:

图一解析:

  • 从服务他主服务发出请求数据同步请求,从服务会尝试使用增量同步的方式
  • 主服务会查看从服务的 Replication Id 是否和主服务一致
  • 如果发现Replication Id不一致,则主服务会认定从服务是第一次进行连接
  • 主服务会把自己的Replication Id发给从服务器,从服务器的Replication Id将与主服务的一样。

 

图二解析:

  • 主服务会对自身数据进行bgsave生成RDB文件,并把RDB文件发送给从服务器,从服务导入RDB
  • 因为使用的是异步数据导出,所以在传送RDB过程中,主服务有可能会有新增数据

 

图三解析:

  • 主服务对新增的数据临时记录在 repl_baklog 文件中
  • 主服务把 repl_baklog 发送给从服务器中,从服务器再次导入 repl_baklog
  • 完成全量同步。

 

全量同步的依据

Redis 中包含两个条件数据,Replication Id 和 offset

Replication Id 是 Redis 的唯一性ID,每一个Redis 服务的Replication Id都不一样,如果当从服务器第一次向主服务器发送主从请求时,从服务的Replication Id必定和主服务的Replication Id不一致,通过对比,Redis主服务来判断从服务是否第一次连接。

 

offset 是 Redis 的写操作计算器,Redis的每一次写操作都会增加一次offset,主服务在Replication Id相同的情况下,通过从服务的 offset 的值来判断从服务的数据是否过期,从服务的offset小于主服务的offset时,则主服务认为从服务需要更新数据。

 

增量同步

当从服务器不是第一次连接主服务器时,就有可能使用增量同步

图一解析:

  • 从服务器向主服务器发生连接
  • 主服务器发现从服务器的Replication Id不一致,会认为是第一次主从连接,触发全量同步

图二解析:

  • 若从服务器连接主服务器后,主服务器发现Replication Id一致,则主服务对从服务的offset进行判断
  • 若从服务 offset 小于主服务的 offset ,则主服务使用增量同步。

图三解析:

  • 主服务会在 repl_baklog 中查找从服务的 offset 所在的位置,从而判断从服务需要增量同步多少数据。

图四解析:

  • repl_baklog 是一个Redis内部管理的特殊的数组,它包含一定量的存储大小,Redis的每一个写操作,都会记录在 repl_baklog 数组中
  • repl_baklog 中的存储大小会循环利用,当repl_baklog 中的数组容量存满后,数组会从头开始写记录覆盖旧记录。

图五解析:

  • 当从服务正常同步时,从服务会通过 repl_baklog 实时同步主服务的数据
  • 如果当从服务发生故障时,在 repl_baklog 还没有完全存满前恢复工作,那么从服务依然可以通过 repl_baklog 来执行增量同步

图六解析:

  • 当从服务发生故障,主服务的 repl_baklog 会不断累加,从服务依然没有做同步

图七解析:

  • 此时主服务的 repl_baklog 数据已经存满,并覆盖了从服务尚未同步的数据时
  • 从服务将无法使用增量同步方式,当从服务恢复工作后,只能使用全量同步了。

 

调整 repl_baklog 与全量同步的配置

我们可以在 Redis 的配置中做一些全量和增量的同步配置设置

如,

1.因为全量同步需要使用大量资源,所以我们可以提高 repl_baklog 的存储大小,以便可以存入更多的写日志,尽可能让故障的从服务恢复后依然能用增量同步,避免使用全量同步。

 

2.全量同步中使用无磁盘复制,Redis 中默认生成的RDB先存到磁盘中,再从磁盘读取传给从服务,这无疑大大占用了磁盘的读写资源,我们可以配置Redis通过无磁盘复制RDB,使生成出来的RDB文件直接通过网络传递。

# 启用无磁盘复制
repl-diskless-sync yes

 

3.使用【主-从-从】的方式进行同步

如果从服务太多,每个从服务都向一个主服务进行请求同步,那么主服务的压力会很大,我们可以让从服务也充当主服务的功能,把数据传给下一层的从服务上。

具体方法只需要在从服务中 slaveof 另一个从服务即可。

 

Redis 哨兵

Redis 哨兵的原理

Redis提供了哨兵(Sentinel)机制来实现主从集群的自动故障恢复。哨兵的结构和作用如下:

  • 监控:Sentinel 会不断检查您的master和slave是否按预期工作
  • 自动故障恢复:如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后也以新的master为主
  • 通知:Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis的客户端

 

服务状态监控

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

  • 主观下线:如果某sentinel节点发现某实例未在规定时间响应,则认为该实例主观下线
  • 客观下线:若超过指定数量(quorum)的sentinel都认为该实例主观下线,则该实例客观下线。quorum值最好超过Sentinel实例数量的一半。

 

选举新的master

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

  • 首先会判断slave节点与master节点断开时间长短,如果超过指定值(down-after-milliseconds * 10)则会排除该slave节点,不会进入选举
  • 然后判断slave节点的slave-priority值,越小优先级越高,如果是0则永不参与选举
  • 如果slave-prority一样,则判断slave节点的offset值,越大说明数据越新,优先级越高
  • 最后是判断slave节点的运行id大小,越小优先级越高。

 

实现故障转移

当选中了其中一个slave为新的master后(例如slave1),故障的转移的步骤如下:

  • sentinel给备选的slave1节点发送slaveof no one命令,让该节点成为master
  • sentinel给所有其它slave发送slaveof 192.168.150.101 7002 命令,让这些slave成为新master的从节点,开始从新的master上同步数据。
  • 最后,sentinel将故障节点标记为slave,当故障节点恢复后会自动成为新的master的slave节点

 

搭建哨兵集群

按照下图中的哨兵结构,我们搭建一个3个哨兵组成的集群:

 

准备阶段

安装Redis哨兵

apt install redis-sentinel

 

分别对这三个哨兵创建配置文件:

port 27001/27002/27003
sentinel announce-ip 192.168.150.101
sentinel monitor mymaster 192.168.150.101 7001 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
dir "/tmp/s1"/s2/s3
  • port 27001:是当前sentinel实例的端口
  • sentinel monitor mymaster 192.168.150.101 7001 2:指定主节点信息
    • mymaster:主节点名称,自定义,任意写
    • 192.168.150.101 7001:主节点的ip和端口
    • 2:选举master时的quorum值
  • sentinel down-after-milliseconds 设置master断开时间,默认为5秒
  • sentinel failover-timeout 故障切换超时,默认为60秒
  • dir "/tmp/s1"

 

启动哨兵

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

 

启动示例

启动三个哨兵服务,实时对Redis做监控。

 

我们尝试把7001服务进行中断

 

此时 7003 的服务被选举作为master

 

7002 的服务器被转移到 7003 作为 master 的主从服务

 

RedisTemplate 使用哨兵模式

RedisTemplate 是我们在SpringBoot中经常使用的Redis客户端对象了,那么我们需要对RedisTemplate进行配置,使得它能在我们的Redis发生故障时,自动优选其它能用的Redis服务器。

步骤:

1.引入依赖

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

 

2.配置文件中,我们需要为Resid配置多个Redis服务器

spring:
  redis:
    sentinel:
      master: mymaster # 指定master名称
      nodes: # 指定redis-sentinel集群信息 
        - 192.168.150.101:27001
        - 192.168.150.101:27002
        - 192.168.150.101:27003

 

3.因为主从服务是具有读写分流的,写只会写在主服务上,而读可以在主和从之间进行,且优先在从服务器中做读操作,所以我们需要对RedisTemplate设置主从的读写分离。

Redis使用Lettuce技术做的节点切换和节点感知的,所以我们可以配置Lettuce的感知策略

@Bean
public LettuceClientConfigurationBuilderCustomizer configurationBuilderCustomizer(){
    return configBuilder -> configBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);
}

ReadFrom 的四个策略:

  • MASTER:从主节点读取
  • MASTER_PREFERRED:优先从master节点读取,master不可用才读取replica
  • REPLICA:从slave(replica)节点读取
  • REPLICA _PREFERRED:优先从slave(replica)节点读取,所有的slave都不可用才读取master

 

 

Redis 分片集群

与哨兵主从服务不一样,分片集群解决了高并发的读和写的问题。

如果使用了主从配置的话,主只有一台,从有多台,那么如果此时我们除了读需要高并发外,我们的写也需要高并发时,主服务会压力非常大,因此我们可以做分片集群

分片集群不需要哨兵,因为存在多个master主服务和多个slave从服务,主服务之间会互相做心跳检测,相当于做了哨兵的工作,因此在分片集群上,无需哨兵。

客户端请求可以访问任意节点,都会被正确转发到正确的节点进行处理。

 

搭建分片集群

准备阶段

1.准备6台Redis服务,本次使用的是一主一从,共三主三从的服务器集群结构,对这6台服务的配置如下:

port 6379
# 开启集群功能
cluster-enabled yes
# 集群的配置文件名称,不需要我们创建,由redis自己维护
cluster-config-file /tmp/6379/nodes.conf
# 节点心跳失败的超时时间
cluster-node-timeout 5000
# 持久化文件存放目录,自定义路径
dir /tmp/6379
# 绑定地址
bind 0.0.0.0
# 让redis后台运行
daemonize yes
# 注册的实例ip,如果使用不同服务器配置,需要配置对应的ip
replica-announce-ip 192.168.150.101
# 保护模式,不做密码效验等,每一台都能直接连接
protected-mode no
# 数据库数量
databases 1
# 日志,因为使用了后台运行因此控制台没有了输出,可把输出写到文档中
logfile /tmp/6379/run.log

 

启动阶段

我们对这6个配置分别存放在6个地方(或服务器),并依照配置运行 redis-server

# 一键启动所有服务,当然也可以一个个进行启动
printf '%s\n' 7001 7002 7003 8001 8002 8003 | xargs -I{} -t redis-server {}/redis.conf

 

启动完成后可以查看运行状态

ps -ef | grep redis

 

批量关闭所有服务

ps -ef | grep redis | awk '{print $2}' | xargs kill

printf '%s\n' 7001 7002 7003 8001 8002 8003 | xargs -I{} -t redis-cli -p {} shutdown

 

集群阶段

当前6台Redis服务都已启动,但它们之间并没有做集群关系,我们需要对这6台服务做集群的主从关系。

 

在Redis 5.0 之后,可以使用 redis-cli 进行集群的主从关系

redis-cli --cluster create --cluster-replicas 1 192.168.150.101:7001 192.168.150.101:7002 192.168.150.101:7003 192.168.150.101:8001 192.168.150.101:8002 192.168.150.101:8003

命令说明:

  • redis-cli --cluster或者./redis-trib.rb(5.0之前的):代表集群操作命令
  • create:代表是创建集群
  • --replicas 1或者--cluster-replicas 1 :指定集群中每个master的副本个数为1,此时节点总数 ÷ (replicas + 1) 得到的就是master的数量。因此节点列表中的前n个就是master,其它节点都是slave节点,随机分配到不同master
  • 举例子:--cluster-replicas 1则总个数为6,则是6÷(1+1)=3,共3台master,每台master后有一台Slave

输入yes,则集群开始创建:

 

通过命令可以查看集群状态:

redis-cli -p 7001 cluster nodes

可以连接任意节点进行读写操作了。

 

散列插槽

在Redis的集群中,Redis会把所有的数据以分片的形式平均分存储在各个主服务中,一共有16384个,名为插槽。

Redis会根据 key 的值进行 CRC16 运算得出的结果后,再余16384得出的值,来判断该key中的值应该存储到第几个插槽上,从而找到管理该插槽的主服务,把key的数据插入到该主服务中。

  • 如果key的名称前不带{}的话,则使用key作为CRC16运算对象
  • 如果key的名称前带有{}的话,则使用{}中的值作为CRC16运算对象
  • 这是因为考虑到key的多样性为,假如我希望把一系列相关的数据存放到同一个分片中,但key有可能会各不相同,这会导致同一相关的数据因key不同而被存放到不同的服务中,那么就可以使用{xxx}来使这一系列的数据统一存放到一个分片中。如 {type}name, {type}age.

使用集群模式访问Redis:

redis-cli -c -p 6379

使用集群模式连接需要加 -c 参数,然后连接集群内的任意节点即可。

如果我们访问到的key不在当前连接的服务时,Redis会自动地切换到正确的服务并进行读写操作:

 

集群伸缩

我们可以通过增加Redis服务器来实现集群的伸缩功能。

在 redis-cli 中除了可以对集群进行创建外,还支持集群的伸缩:

我们通过使用 add-node 参数来新增一个新的Redis服务进集群:

redis-cli --cluster add-node 192.167.101.127:7004 192.168.101.127:7001

add-node 需要一个新的Redis服务和任意一个已存在集群中的旧的服务的地址,这样cli才知道要把这个新的Redis服务并入到哪一个集群中

--cluster-slave 指的是当新Redis服务加入集群后将成为从服务,而新Redis加入后默认为主服务

--cluster-master-id <arg> 指的是如果新的Redis服务加入后要成为从服务,那么要加入到哪个主服务下做从服务?提供该主服务的id值。

 

迁移分片插槽

新加入的Redis是没有任何分片的,因为这些分片都被先前的主服务给分配好了,所以把新服务加入集群后,不对新服务进行分片重新分配的话,是没有意义的,所以我们需要对新加入的Redis服务进行分配插槽:

使用以下命令重新分配插槽:

redis-cli --cluster reshard 192.168.101.127:7001

这里是指,连接任意集群节点进行准备迁移操作,

 

询问要迁移多少个分片,

 

询问要把这些分片迁移到哪个主服务的id上

 

输入从那些主节点的ID上迁移分片,选择all则从所有的主服务中取出3000个分片,也可以单独设置某个主服务的id,则只在这个id上取出分片进行迁移。

 

输入 done 则表示要迁移的主服务id设置完成。

询问是否开始迁移后,就可以成功把对应分片数从其它主服务迁到新服务中了。

 

手动故障转移

Redis集群会对丢失宕机的Redis服务自动进行故障转移,当Redis服务发生宕机后,其下的从服务会马上接手作为宕机后的主服务,当宕机的服务恢复后,会成为从服务。

但有些时候,我们希望通过手动设置主从服务的关系,我们可以通过手动把从服务切换为主服务:

解析:

  • 从服务开始成为新主服务之前向旧主服务发送通知,旧主服务开始拒绝所有客户端请求,保持数据统一性
  • 旧主服务向从服务发送offset进行最终的数据同步
  • 从服务数据同步完成后,向旧主服务与其它主服务通知,从服务已成为新的主服务,旧主服务下降为从服务。

手动的Failover支持三种不同模式:

  • 缺省:默认的流程,如图1~6歩
  • force:省略了对offset的一致性校验
  • takeover:直接执行第5歩,忽略数据一致性、忽略master状态和其它master的意见

操作:

# 使8002从服务切换为主服务,旧主服务7002变为从服务
8002> cluster failover

 

RedisTemplate访问分片集群

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

  • 1.引入redis的starter依赖
  • 2.配置分片集群地址
  • 3.配置读写分离

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

spring:
  redis:
    cluster: # 哨兵模式为 sentinel
      nodes: # 指定分片集群的每一个节点信息
        - 192.168.150.101:7001
        - 192.168.150.101:7002
        - 192.168.150.101:7003 
        - 192.168.150.101:8001
        - 192.168.150.101:8002
        - 192.168.150.101:8003

 

如果您喜欢本站,点击这儿不花一分钱捐赠本站

这些信息可能会帮助到你: 下载帮助 | 报毒说明 | 进站必看

修改版本安卓软件,加群提示为修改者自留,非本站信息,注意鉴别

THE END
分享
二维码
打赏
海报
Redis 分布式缓存集群
  Redis 持久化 RDB 持久化 RDB 全称为 Redis Database Backup File(Redis数据备份文件),也被叫做Redis数据快照。就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后,从磁……
<<上一篇
下一篇>>