Redis高级

0 阅读10分钟

1.持久化

1.1.RDB

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

快照文件默认保存在当前运行目录。(默认关闭redis会执行一次save操作)

# Redis主进程执行RDB,会阻塞所有命令:
save

# 开启子进程执行RDB,避免主进程受到影响
bgsave

Redis内部有触发RDB的机制,可以在redis.conf文件中的配置找到

# 900 秒内,如果至少有1个key被修改,则执行bgsave,如果save "" 则表示禁用RDB
save 900 1

RDB的其他在redis.conf文件中的配置

#是否压缩,建议不开启,压缩也会消耗cpu,磁盘不值钱
rdbcompression yes
 
#RDB文件名
dbfilename dump.db
 
#文件保存的路径目录
dir ./

RDB方式的bgsave的基本流程

  1. 触发检查:当执行bgsave命令时,Redis父进程首先检查是否存在正在执行的子进程(如RDB/AOF子进程)。如果存在,则直接返回,避免并发执行导致资源竞争。
  2. 创建子进:父进程通过fork()系统调用创建子进程。此过程会短暂阻塞父进程(阻塞时间与内存大小相关)。子进程与父进程共享内存数据,通过写时复制(Copy-On-Write, COW)技术保证数据一致性。
  3. 异步执行快照:子进程独立遍历父进程的内存数据,生成临时RDB文件(默认命名为temp-pid.rdb),写入完成后原子替换旧的RDB文件(如dump.rdb)。
  4. 父进程在fork()完成后立即恢复响应客户端请求,仅子进程负责持久化操作。
  5. 完成通知:子进程完成RDB文件写入后,向父进程发送信号。父进程更新统计信息(如rdb_last_save_time、rdb_last_bgsave_status等),记录持久化结果。

image.png

写时复制

多个调用者共享同一份资源(如内存或磁盘数据),直到某个调用者尝试修改资源时,系统才会复制一份专用副本供其修改,其他调用者仍访问原始资源。这种延迟复制机制减少了不必要的内存开销和复制时间

RDB的缺点?

  • RDB的执行间隔时间长,两次RDB之间写入数据有丢失的风险
  • fork子进程、压缩、写出RDB文件都比较耗时

1.2.AOF

AOF全称Append Only File(追加文件)。

Redis处理的每个写命令都会记录到AOF文件,可以看做是命令日志文件。redis故障以后,重新执行一遍aof文件就可以恢复了。

redis.conf配置

#是否开启AOF功能,默认是no
appendonly yes
 
#AOF文件的名
appendfilename "appendonly.aof"

# 表示每执行一次写命令,立即记录到AOF文件
appendfsync always
 
#写命令执行完先放入AOF缓存区,然后表示每周1秒将缓存区数据写到AOF文件,是默认方案
appendfsync everysec
 
#写命令执行先放入AOF缓冲区,有操作系统决定何时将缓冲区内容写回磁盘
appendfsync no
配置项刷盘时机优点缺点
Always同步刷盘可靠性高,几乎不丢失数据性能影响大
everysec每秒刷盘性能适中最多丢失1秒数据
no操作系统控制性能最好可靠性较差,可能丢失大量数据

因为是记录命令,AOF文件会比RDB文件大的多。而且AOF会记录对同一个key的多次写操作,但只有最后一次写操作才有意义。通过执行bgrewriteaof命令,可以让AOF文件执行重写功能,用最少的命令达到相同效果。

比较RDBAOF
持久化方式定时对整个内存做快照记录每一次执行的命令
数据完整性不完整,两次备份之间会丢失相对完整,即决于刷盘策略
文件的大小会有压缩,文件体积小记录命令,文件体积很大
宕机恢复速度很快
数据恢复优先级低,因为数据完整性不如AOF高,因为数据完整性更高
数据的优先级高,大量CPU和内存消耗低,主要是磁盘IO资源,但是AOF重写时会占用大量CPU和内存资源
使用场景可以容忍数分钟的数据丢失,追求更快的启动速度对数据安全性要求较高单常见

2.redis主从

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

image.png

2.1.搭建集群

www.bilibili.com/video/BV1cr…

2.2.数据同步原理

Redis主从同步通过全量增量机制结合,在保证数据一致性的同时兼顾效率。 全量同步用于初始化或数据差异过大时,增量同步则优化了断线恢复的性能。

全量同步

第一次建立主从节点建立连接的时候进行全量同步: image.png

master如何判断slave是不是第一次来同步数据?这里会用到两个很重要的概念:

  • Replication ld:简称replid,是数据集的标记,id一致则说明是同一数据集。每一个master都有唯一的replid,slave则会继承master节点的replid
  • offset:偏移量,随着记录在repl_baklog中的数据增多而逐渐增大。slave完成同步时也会记录当前同步的offset。如果slave的offset小于master的offset,说明slave数据落后于master,需要更新。

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

所以全量同步的流程可以描述为:

  1. slave节点请求增量同步
  2. master节点判断replid,发现不一致,拒绝增量同步
  3. master将完整内存数据生成RDB,发送RDB到slave
  4. slave清空本地数据,加载master的RDB
  5. master将RDB期间的命令记录在repl_baklog,并持续将log中的命令发送给slave
  6. slave执行接收到的命令,保持与master之间的同步

增量同步

增量同步用于主从断线重连后恢复同步,避免全量复制

image.png

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

优化

全量同步的性能较差,可以从以下几点来优化

  • 在master中配置repl-diskless-sync yes启用无磁盘复制,避免全量同步时的磁盘IO(使用网络IO流)。
  • Redis单节点上的内存占用不要太大,减少RDB导致的过多磁盘IO
  • 适当提高repl_baklog的大小,发现slave宕机时尽快实现故障恢复,尽可能避免全量同步
  • 限制一个master上的slave节点数量,如果实在是太多slave,则可以采用主-从-从链式结构,减少master压力 image.png

3. 哨兵

slave节点宕机恢复后可以找master节点同步数据,那master节点宕机怎么办?

监控master的状态,如果master宕机,选一个从节点成为新的master

Redis提供了哨兵(Sentinel)机制来实现主从集群的自动故障恢复。

搭建哨兵:www.bilibili.com/video/BV1cr…

Spring的RedisTemplate哨兵模式:www.bilibili.com/video/BV1cr…

3.1 哨兵的作用和原理

哨兵的结构和作用如下:

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

服务状态监控

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

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

选举依据

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

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

故障迁移步骤

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

image.png

4. 分片集群

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

  1. 海量数据存储问题
  2. 高并发写的问题

使用分片集群可以解决上述问题,分片集群特征:

  • 集群中有多个master,每个master保存不同数据
  • 每个master都可以有多个slave节点
  • master之间通过ping监测彼此健康状态(不需要哨兵了)
  • 客户端请求可以访问集群任意节点,最终都会被转发到正确节点
image.png

搭建Redis分片集群:www.bilibili.com/video/BV1cr…

Spring RedisTemplate访问分片集群:www.bilibili.com/video/BV1cr…

4.1 散列插槽

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

image.png 数据key不是与节点绑定,而是与插槽绑定(节点宕机,可以把插槽分配到其他节点)。redis会根据key的有效部分计算插槽值,分两种情况:

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

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

如何将同一类数据固定的保存在同一个Redis实例?

答:这一类数据使用相同的有效部分,例如key都以{typeld}为前缀

4.2 故障迁移

分片集群有自动的故障迁移功能

但有的时候需要手动故障转移,比如想让节点B替换原来的master节点A作为新的master

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

image.png

5. 多级缓存

www.bilibili.com/video/BV1cr…