Redis数据安全分析
Redis压测
set指令,1000000次请求,20个线程
redis-benchmark -t set -n 1000000 -c 20
Redis数据持久化机制
- ⽆持久化:完全关闭数据持久化,不保证数据安全。相当于将Redis完全当做缓存来⽤
- RDB(快照,Snapshotting)
Redis 会在指定的时间点生成数据快照(Snapshot),将当前内存中的所有数据写入一个 .rdb 文件。
RDB 文件紧凑、加载速度快。RDB备份时性能⾮常快,对 Redis 性能影响小(使用 BGSAVE 时主进程继续响应请求)。 - AOF(Append‑Only File)
Redis 将写命令(如 SET、DEL 等)以“追加”方式记录到日志文件 appendonly.aof 中,Redis 重启时会重放这些命令以恢复数据。AOF会自动切换新日志文件防止单个文件过大。当aof出现错误可以使⽤redis-check-aof⼯具恢复。 - 以及两者的混合持久化方式。
RDB详解
核⼼配置
- dir ⽂件⽬录
- dbfilename ⽂件名 默认dump.rdb
- rdbcompression 是否启⽤RDB压缩,默认yes。 如果不想消耗CPU进⾏压缩,可以设置为no
- stop-writes-oin-bgsave-error 默认yes。如果配置成no,表示你不在乎数据不⼀致或者有其他
的⼿段发现和控制这种不⼀致。在快照写⼊失败时,也能确保redis继续接受新的写⼊请求。 - rdbchecksum 默认yes。在存储快照后,还可以让redis使⽤CRC64算法来进⾏数据校验,但是这
样做会增加⼤约10%的性能消耗。如果希望获得最⼤的性能提升,可以关闭此功能。
何时会触发RDB备份
- 到达配置⽂件中默认的快照配置时,会⾃动触发RDB快照
- ⼿动执⾏save或者bgsave指令时,会触发RDB快照。 save⽅法会在备份期间阻塞主线程。
bgsve则不会阻塞主线程。会fork⼀个⼦线程进⾏持久化,这个过程中会要将数据复制⼀份,
因此会占⽤更多内存和CPU。 - 主从复制时会触发RDB备份。
LASTSAVE指令查看最后⼀次成功执⾏快照的时间。
AOF详解
核⼼配置
- appendonly 是否开启aof, 默认不开启。
- appendfilename ⽂件名称。
- appendfsync 何时同步到磁盘。默认everysecond 每秒⼀次。no 由操作系统进⾏内存刷
盘。 always 每次操作立刻调用 fsync 将数据强制刷到磁盘,数据更安全,但性能较低。 - appenddirname AOF⽂件⽬录。根目录是dir
- auto-aof-rewrite-percentage, auto-aof-rewrite-min-size⽂件重写触发策略。默认每个⽂件64M, 写到100%,进⾏⼀次重写。
Redis会定期对AOF中的操作进⾏优化重写,让AOF中的操作更为精简。例如将多个INCR指令,
合并成⼀个SET指令。BGREWRITEAOF可以直接手动触发
- no-appendfsync-on-rewrite 在执行 AOF 重写期间是否允许 fsync(刷盘,同步到磁盘)。
混合持久化策略
核心配置
aof-use-rdb-preamble yes # 默认值是 yes,表示开启混合持久化
Redis7中,对⽂件名称做了调整。从一个文件改成了3个文件base.rdb⽂件是⼆进制数据⽂件;incr.aof是增量的操作⽇志;manifest则是记录⽂件信息的元⽂件。(之前是三个文件内容都在一个文件里)
Redis主从复制(Replica)机制
默认情况下,从库是只读的,不允许写⼊数据。redis.conf中配置了slave的默认权限。
虽然禁⽌了对数据的写操作,但是并没有禁⽌CONFIG、DEBUG等管理指令,这些指令如果和主节点不⼀致,还是容易造成数据不⼀致。如果为了安全起⻅,可以使⽤rename-command屏蔽这些危险的指令。
在redis.conf配置⽂件
rename-command CONFIG ""# 屏蔽CONFIG指令,原理是将指令替换为空字符串
主从复制⼯作流程
- Slave启动后,向master发送⼀个sync请求。等待建⽴成功后,slave会删除掉⾃⼰的数据⽇志⽂件,等待主节点同步。
- master接收到slave的sync请求后,会触发⼀次RDB全量备份,同时收集所有接收到的修改数据的指令。然后master将RDB和操作指令全量同步给slave。完成第⼀次全量同步。
- 主从关系建⽴后,master会定期向slave发送⼼跳包,确认slave的状态。⼼跳发送的间隔通过参数repl-ping-replica-period指定。默认10秒。
- 只要slave定期向master回复⼼跳请求,master就会持续将后续收集到的修改数据的指令传递给slave。同时,master会记录offset,即已经同步给slave的消息偏移量。
- 如果slave短暂不回复master的⼼跳请求,master就会停⽌向slave同步数据。直到slave重新上线后,master从offset开始,继续向slave同步数据。
CONFIG GET repl-diskless-sync。Redis 默认使用了另一种同步机制:无盘复制。主节点使用了 内存中生成 RDB,直接通过 socket 发送给从节点 的方式
主从复制的缺点
- 主从数据最终一致性,存在延迟。网络问题或者从节点过多
- 主从故障切换。原生主从复制主节点宕机不自动切换,引入额外组件Redis Sentinel可以实现
Redis哨兵集群Sentinel机制
Redis Sentinel(哨兵)机制是 Redis 官方提供的一种 高可用 解决方案,用来实现:
- 动故障转移
- 主从自动切换
- 系统监控与通知
Sentinel 的核心功能
功能 | 说明 |
---|---|
主从监控 | 实时监控主节点和从节点是否存活 |
消息通知 | 发现故障时向管理员发送通知(可配置) |
故障转移 | 主节点宕机时,自动将某个从节点提升为新的主节点,并通知其他从节点去复制新主 |
配置中⼼ | 客户端通过连接哨兵可以获取当前Redis服务的master地址 |
Sentinel⼯作原理
如何发现master服务宕机
Sentinel 使用“主动探测 + 分布式投票”方式来判断主节点是否宕机
- 主观下线(sdown):单个 Sentinel 判断;
- 客观下线(odown):多个 Sentinel 达成共识后确认。
如何切换新的master
- master变成O_DOWN后,Sentinel会在集群中选举产⽣⼀个服务节点作为Leader。Leader将负责向其他Redis节点发送命令,协调整个故障切换过程。在选举过程中,Sentinel是采⽤的Raft算法,这就是quorum设置超过半数的原因。
假设有 3 个 Sentinel:A、B、C
Master 挂了,Sentinel A 最先检测到并进入 epoch = 42;
A 广播请求投票;
B、C 还没处理这轮投票,于是它们都接受并投给 A;
A 成为获得多数票(2/3)的 Leader,主导 failover;
若之后 B、C 也发起投票请求,由于它们已经投过票了,其他 Sentinel 会拒绝。
- Leader Sentinel 从从节点中选择replica-priority配置数字最小,再到数据最完整、延迟最小,再到RunID字典顺序最⼩的节点;
- 发送 SLAVEOF NO ONE 提升它为新的主节点;通知其他从节点去 SLAVEOF newmaster;
- 如果旧的master恢复了,Sentinel Leader会让旧的master降级为slave,并从新的master上同步
数据,恢复⼯作。
为什么只允许一个 Leader?
因为如果多个 Sentinel 同时执行 SLAVEOF NO ONE 操作,会导致多个新 master,形成脑裂(split-brain),数据不一致。
Sentinel的缺点
- 对客户端不太友好
由于master需要切换,这也就要求客户端也要将写请求频繁切换到master上。或者实现对哨兵的支持,不支持 Sentinel 的客户端将无法动态感知新的主节点。 - 数据不安全
在主从复制集群中,不管master是谁,所有的数据都以master为主。原master已经完成的但是还没同步完成的操作就丢失了
Redis集群Cluster机制
是一种分布式部署 Redis 的方式,提供数据分片(水平扩展)和高可用性(主从架构)。
特性 | 说明 |
---|---|
无中心架构 | 每个节点平等,互相通信,无中心节点 |
数据分片 | 将 16384 个槽(slot)平均分配给多个节点,实现分布式存储 |
主从复制 | 每个主节点可以有一个或多个从节点,提高高可用性 |
自动故障转移 | 主节点故障时,从节点可自动提升为主节点(需至少 3 个主节点) |
支持水平扩展 | 新节点可动态加入集群,扩容/缩容数据 |
详解Slot槽位
Redis集群设置16384个哈希槽。每个key会通过CRC16校验后,对16384取模,来决定放到哪个
槽。集群的每个节点负责⼀部分的hash槽。
- Slot如何分配
Redis集群中内置16384个槽位。在建⽴集群时,Redis会根据集群节点数量,将这些槽位尽量平均的
分配到各个节点上。并且,如果集群中的节点数量发⽣了变化。(增加了节点或者减少了节点)。就需
要触发⼀次reshard,重新分配槽位。⽽槽位中对应的key,也会随着进⾏数据迁移。
集群会检查每个槽位是否有对应的节点负责如果负责⼀部分槽位的⼀组复制节点(主节点和从节点都挂)都挂了,默认情况下Redis集群就会停⽌服务。其他正常的节点也⽆法接收写数据的请求。在配置文件中可以配置强制开启
cluster-require-full-coverage yes
手动为集群重新分配slot
#⼿动触发reshard,重新分配槽位
redis-cli reshard
- 如何确定key与slot的对应关系
Redis集群中,对于每⼀个要写⼊的key,都会寻找所属的槽位。计算的⽅式是 CRC16(key) mod16384。
这对复合指令不太友好。如果他们分属不同的槽位,就无法进行原子性操作。于是redis有了“哈希标签”(Hash Tag)机制。只要 key 中 包含相同的 {} 部分内容,Redis 会只使用 {} 内部的字符串做 slot 映射。redis还提供了计算key属于哪个slot的指令。
MSET user:{1001}:name Alice user:{1001}:age 20CLUSTER KEYSLOT k1
redis数据倾斜问题
避免⼤量的数据被集中存储到了集群中某⼀个热点Redis节点上。避免 hash tag 让大量 key 映射到相同 slot。
- 调整key的结构,尤其是那些访问频繁的热点key,让数据能够尽量平均的分配到各个slot上。
- 调整slot的分布,将那些数据量多,访问频繁的热点slot进⾏重新调配,让他们尽量平均的分配到不同的Redis节点上。
Redis集群选举原理
gossip协议是一种点对点通信协议,每个节点定期选择其他节点进行交流,交换彼此所知道的其他节点的状态信息(redis使用当前服务端口+10000作为gossip通信的端⼝)。主要作⽤有:
- 节点间发送⼼跳和确认其他节点的存在。
- 通知其他节点新节点的加⼊或已经下线的节点。
- 通过反馈机制更新节点的状态,如权重、过期时间等
gossip协议包含多种消息,包括ping,pong,meet,fail等等。 - meet:某个节点发送meet给新加⼊的节点,让新节点加⼊集群中,然后新节点就会开始与其他 节点进⾏通信;
- ping:每个节点都会频繁给其他节点发送ping,其中包含⾃⼰的状态还有⾃⼰维护的集群元数 据,互相通过 ping交换元数据(类似⾃⼰感知到的集群节点增加和移除,hash slot信息等);
- pong: 对ping和meet消息的返回,包含⾃⼰的状态和其他信息,也可以⽤于信息⼴播和更新;
- fail: 某个节点判断另⼀个节点fail之后,就发送fail给其他节点,通知其他节点,指定的节点宕机 了。
Redis集群选举流程
当slave发现⾃⼰的master变为FAIL状态时,便尝试进⾏Failover,以期成为新的master。由于挂掉
的master 可能会有多个slave,从⽽存在多个slave竞争成为master节点的过程, 其过程如下:
- slave发现⾃⼰的master变为FAIL
- 将⾃⼰记录的集群currentEpoch加1,并⼴播FAILOVER_AUTH_REQUEST信息(currentEpoch可以理解为选举周期,通过cluster info指令可以看到)
- 其他节点收到该信息,只有master响应,判断请求者的合法性,并发送FAILOVER_AUTH_ACK,对每⼀个epoch只发送⼀次ack
- 尝试failover的slave收集master返回的FAILOVER_AUTH_ACK
- slave收到超过半数master的ack后变成新Master(这⾥解释了集群为什么⾄少需要三个主节点,如果只有两 个,当其中⼀个挂了,只剩⼀个主节点是不能选举成功的)
- slave⼴播Pong消息通知其他集群节点
Redis集群能不能保证数据安全
在Redis集群相对⽐较稳定的时候,Redis集群是能够保证数据安全的。因为Redis集群中每个master都是可以配置slave从节点的。这些slave节点会即时备份master的数据。在master宕机时,slave会⾃动切换成master。继续提供服务。在Redis的配置⽂件中,有两个参数⽤来保证每个master必须有健康的slave进⾏备份。
min-replicas-to-write 1
min-replicas-max-lag 10
由于Redis集群的gossip协议在同步元数据时不保证强⼀致性,这意味着在特定的条件下Redis集群可能会丢掉⼀些被系统收到的写⼊请求命令 ,这些特定条件通常都⽐较苛刻,概率⽐较⼩。⽐如⽹络抖动产⽣的脑裂问题。在企业中,有良好运维⽀持,通常可以认为Redis集群的数据是安全的。