陈年旧事 Redis:Redis进阶知识 - 乐心湖's Blog | 技术小白的技术博客
目录
单机的 Redis 存在以下四大问题,我们将学着去解决。
Redis持久化
- RDB 持久化
- AOF 持久化
RDB持久化
RDB 全称 Redis Database Backup file(Redis数据备份文件),也被叫做 Redis 数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。当 Redis 实例故障重启后,从磁盘读取快照文件,恢复数据。快照文件称为 RDB 文件,默认是保存在当前运行目录。
RDB 持久化在四种情况下会执行
- 执行 save 命令
- 执行 bgsave 命令
- Redis 停机时
- 触发 RDB 条件时
save 命令
执行下面的命令,可以立即执行一次 RDB
save 命令会导致主进程执行 RDB,这个过程中其它所有命令都会被阻塞。只有在数据迁移时可能用到。
bgsave 命令
下面的命令可以异步执行 RDB
这个命令执行后会开启独立进程完成 RDB,主进程可以持续处理用户请求,不受影响。
停机时
Redis 停机时会执行一次 save 命令,实现 RDB 持久化。
自动触发 RDB 条件
Redis 内部有触发 RDB 的机制,可以在 redis.conf 文件中找到,格式如下:
# 900秒内,如果至少有1个key被修改,则执行bgsave
# save "" 则表示禁用RDB
save 900 1
save 300 10
save 60 10000
RDB 的其它配置也可以在 redis.conf 文件中设置
# 是否压缩 ,建议不开启,压缩也会消耗cpu,磁盘的话不值钱
rdbcompression yes
# RDB文件名称
dbfilename dump.rdb
# 文件保存的路径目录
dir ./
bgsave 开始时会 fork 主进程得到子进程,子进程共享主进程的内存数据。完成 fork 后读取内存数据并写入 RDB 文件。
fork 采用的是 copy-on-write 技术:当主进程执行读操作时,访问共享内存;当主进程执行写操作时,则会拷贝一份数据,执行写操作。
RDB 方式 bgsave 的基本流程?
- fork主进程得到一个子进程,共享内存空间
- 子进程读取内存数据并写入新 的RDB 文件
- 用新 RDB 文件替换旧的 RDB 文件
RDB 会在什么时候自动执行?save 60 1000代表什么含义?
- 默认是服务停止时
- 代表 60s 内至少执行 1000 次修改则触发 RDB
RDB 的缺点?
- RDB 执行间隔时间长,两次 RDB 之间写入数据有丢失的风险
- fork 子进程、压缩、写出 RDB 文件都比较耗时
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
三种策略对比
AOF文件重写
因为是记录命令,AOF 文件会比 RDB 文件大的多。而且 AOF 会记录对同一个 key 的多次写操作,但只有最后一次写操作才有意义。通过执行 bgrewriteaof 命令,可以让 AOF 文件执行重写功能,用最少的命令达到相同效果。
如图,AOF 原本有三个命令,但是这三个都是对 num 的操作,第二次会覆盖第一次的值,因此第一个命令记录下来没有意义。
所以重写命令后,AOF文件内容就是:mset name jack num 666
Redis 也会在触发阈值时自动去重写 AOF 文件。阈值也可以在 redis.conf 中配置
# AOF文件比上次文件 增长超过多少百分比则触发重写
auto-aof-rewrite-percentage 100
# AOF文件体积最小多大以上才触发重写
auto-aof-rewrite-min-size 64mb
RDB 和 AOF 各有自己的优缺点,如果对数据安全性要求较高,在实际开发中往往会结合两者来使用。
Redis 支持同时开启 RDB 和 AOF,在这种情况下当 Redis 重启的时候会优先载入 AOF 文件来恢复原始的数据,因为在通常情况下 AOF 文件保存的数据集要比 RDB 文件保存的数据集完整。
Redis主从复制
单节点 Redis 的并发能力是有上限的,要进一步提高 Redis 的并发能力,就需要搭建主从集群,实现读写分离。
搭建主从
跳过部署三个 Redis,很简单。
共包含三个节点,一个主节点,两个从节点。
这里我们在同一台虚拟机中开启 3 个 Redis 实例,模拟主从集群,信息如下:
IP | PORT | 角色 |
---|---|---|
192.168.150.101 | 7001 | master |
192.168.150.101 | 7002 | slave |
192.168.150.101 | 7003 | slave |
为了方便查看日志,我们打开 3 个 ssh 窗口,分别启动 Redis 实例,启动命令:
# 第1个
redis-server 7001/redis.conf
# 第2个
redis-server 7002/redis.conf
# 第3个
redis-server 7003/redis.conf
如果要一键停止,可以运行下面命令:
printf '%s\n' 7001 7002 7003 | xargs -I{} -t redis-cli -p {} shutdown
开启主从关系
现在三个实例还没有任何关系,要配置主从可以使用 replicaof
或者 slaveof
(5.0以前)命令。
有临时和永久两种模式:
1.修改配置文件(永久生效)
在 redis.conf 中添加一行配置:slaveof <masterip> <masterport>
2.使用 redis-cli 客户端连接到redis服务,执行slaveof命令(重启后失效)
slaveof <masterip> <masterport>
在 5.0 以后新增命令 replicaof,与 salveof 效果一致。
这里我们为了演示方便,使用方式二。通过 redis-cli 命令连接 7002,执行下面命令
# 连接 7002
redis-cli -p 7002
# 执行slaveof
slaveof 192.168.150.101 7001
通过 redis-cli 命令连接 7003,执行下面命令
# 连接 7003
redis-cli -p 7003
# 执行slaveof
slaveof 192.168.150.101 7001
然后连接 7001 节点,查看集群状态:
# 连接 7001
redis-cli -p 7001
# 查看状态
info replication
执行下列操作以测试
-
利用 redis-cli 连接7001,执行
set num 123
-
利用 redis-cli 连接7002,执行
get num
,再执行set num 666
-
利用 redis-cli 连接7003,执行
get num
,再执行set num 888
可以发现,只有在 7001 这个 master 节点上可以执行写操作,7002 和 7003 这两个 slave 节点只能执行读操作。
同步原理
全量同步
主从第一次建立连接时,会执行全量同步,将 master 节点的所有数据都拷贝给 slave 节点,流程如下
有几个概念需要知道:
- Replication Id:简称 replid,是数据集的标记,id 一致则说明是同一数据集。每一个 master 都有唯一的replid,slave 则会继承 master 节点的 replid;
- offset:偏移量,随着记录在 repl_baklog 中的数据增多而逐渐增大。slave 完成同步时也会记录当前同步的offset,即 slave 的 offset 永远小于等于 master 的 offset;当 slave 的 offset 小于 master 的 offset,说明 slave 数据落后于 master,需要更新。
因此 slave 做数据同步,必须向 master 声明自己的 replid 和 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 之间的同步
增量同步
全量同步需要先做 RDB,然后将 RDB 文件通过网络传输给 slave,成本太高。因此除了第一次做全量同步,其它大多数时候 slave 与 master 都是做增量同步。
什么是增量同步?就是只更新 slave 与 master 存在差异的部分数据。
repl_backlog 原理
master 怎么知道 slave 与自己的数据差异在哪里?
这就要说到全量同步时的 repl_baklog 文件了。
这个文件是一个固定大小的数组,只不过数组是环形,也就是说角标到达数组末尾后,会再次从 0 开始读写,这样数组头部的数据就会被覆盖。repl_baklog 中会记录 Redis 处理过的命令日志及 offset,包括 master 当前的 offset 和 slave 已经拷贝到的 offset
slave 与 master 的 offset 之间的差异,就是 salve 需要增量拷贝的数据了。
随着不断有数据写入,master 的 offset 逐渐变大,slave 也不断的拷贝,追赶 master 的 offset,直到数组被填满:
此时,如果有新的数据写入,就会覆盖数组中的旧数据。不过,旧的数据只要是绿色的,说明是已经被同步到slave 的数据,即便被覆盖了也没什么影响。因为未同步的仅仅是红色部分。但是,如果 slave 出现网络阻塞,导致 master 的 offset 远远超过了 slave 的 offset,如下图
如果 master 继续写入新数据,其 offset 就会覆盖旧的数据,直到将 slave 现在的 offset 也覆盖了
棕色框中的红色部分,就是尚未同步,但是却已经被覆盖的数据。此时如果 slave 恢复,需要同步,却发现自己的 offset 都没有了,无法完成增量同步了,只能做全量同步。
主从同步优化
主从同步可以保证主从数据的一致性,非常重要。可以从以下几个方面来优化 Redis 主从集群
- 在 master 中配置
repl-diskless-sync yes
启用无磁盘复制,避免全量同步时的磁盘 IO - Redis 单节点上的内存占用不要太大,减少 RDB 导致的过多磁盘IO
- 适当提高 repl_baklog 的大小,发现 slave 宕机时尽快实现故障恢复,尽可能避免全量同步
- 限制一个 master 上的 slave 节点数量,如果实在是太多 slave,则可以采用主-从-从链式结构,减少 master 压力
简述全量同步和增量同步区别?
- 全量同步:master 将完整内存数据生成 RDB,发送 RDB 到 slave。后续命令则记录在 repl_baklog,逐个发送给slave
- 增量同步:slave 提交自己的 offset 到 master,master 获取 repl_baklog 中从 offset 之后的命令给slave
什么时候执行全量同步?
- slave 节点第一次连接 master 节点时
- slave 节点断开时间太久,repl_baklog 中的 offset 已经被覆盖时
什么时候执行增量同步?
- slave 节点断开又恢复,并且在 repl_baklog 中能找到 offset 时