前言
今天来讲讲 redis 以下知识点,如有不当请多指教!
Redis 持久化
主从复制
Sentinel 机制
Redis Cluster
Redis 持久化
redis 是基于 内存
的,如果不想办法将数据保存在硬盘上,一旦 redis 重启(退出 / 故障),内存的数据将会全部丢失。
Redis 提供了两种持久化方法:
RDB
(基于快照),将某一时刻的所有数据保存到一个 RDB 文件中。AOF
(append-only-file),当 Redis 服务器执行写命令的时候,将执行的写命令保存到 AOF 文件中。
RDB
保存某个时间点的全量数据快照
- 手动触发
SAVE
:阻塞 Redis 的服务器进程,知道 RDB 文件被创建完毕BGSAVE
:Fork 出一个子进程来创建 RDB 文件,不阻塞服务器进程,使用lastsave
指令可以查看最近的备份时间 - 自动触发
根据 redis.conf 配置里的 save m n 定时触发(用的是 BGSAVE)
主从复制时,主节点自动触发
执行 Debug Relaod
执行 Shutdown 且没有开启 AOF 持久化
注意:Redis 服务器在启动的时候,如果发现有 RDB 文件,就会自动载入 RDB 文件(不需要人工干预)
RDB 的优缺点
优点:
RDB 是一个紧凑压缩的二进制文件,代表 Redis 在某个时间点上的数据快照,适合备份,全量复制等场景。
且加载 RDB 恢复数据远远快于 AOF 的方式。
缺点:
没办法做到实时持久化 / 秒级持久化,因为 bgsave 每次运行都要执行 fork 操作创建子进程,属于重量级操作,频繁执行成本过高。
RDB 的相关配置
// 在 n 秒内修改 m 条数据时创建 RDB 文件
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes //bgsave 出错时停止写入
rdbcompression yes // 压缩 RDB 文件
rdbchecksum yes // 校验文件是否损坏
AOF
与 RDB 不一样的是,AOF
记录的是命令,而不是数据。
如何开启 AOF?
只需在配置文件中将 appendonly
设置为 yes 即可。
AOF 的工作流程:
1、所有的写入命令追加到 aof_buf
缓冲区中。
2、AOF 会根据对应的策略向磁盘做同步操作。刷盘策略由 appendfsync
参数决定。
3、定期对 AOF 文件进行重写。重写策略由 auto-aof-rewrite-percentage
,auto-aof-rewrite-min-size
两个参数决定。
appendfsync 参数有如下取值:
appendfsync always # 每次有数据修改发生时都会写入 AOF 文件。appendfsync everysec # 每秒钟同步一次,该策略为 AOF 的默认策略。appendfsync no # 从不同步。高效但是数据不会被持久化。
AOF 重写
为什么要重写?
重写后可以加快节点启动时的加载时间
重写后的文件为什么可以变小?
进程内超时的数据不用再写入到 AOF 文件中
多条写命令可以合并为一个
重写条件
1、手动触发
直接调用 bgrewriteaof
命令
2、自动触发
先来看看有关参数:auto-aof-rewrite-min-size
:执行 AOF 重写时,文件的最小体积,默认值为 64MB。auto-aof-rewrite-percentage
:执行 AOF 重写时,当前 AOF 大小 (即 aof_current_size) 和上一次重写时 AOF 大小 (aof_base_size) 的比值。
只有当 auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage 两个参数同时满足时,才会自动触发 AOF 重写。
后台重写
Redis 将 AOF 重写程序放到 子进程
里执行 (BGREWRITEAOF
命令),像 BGSAVE
命令一样 fork 出一个子进程来完成重写 AOF 的操作,从而不会影响到主进程。
AOF 后台重写是不会阻塞主进程接收请求的,新的写命令请求可能会导致 当前数据库和重写后的 AOF 文件的数据不一致 !
为了解决数据不一致的问题,Redis 服务器设置了一个AOF 重写缓冲区
,当子进程完成重写后会发送信号让父进程将 AOF 重写缓冲区的数据写到新的 AOF 文件。
RDB 和 AOF 比较
RDB 和 AOF 并不互斥,它俩可以同时使用。
RDB 的优点:载入时恢复数据快、文件体积小。
RDB 的缺点:会一定程度上丢失数据(因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失。)
AOF 的优点:丢失数据少 (默认配置只丢失 1 秒 的数据)。
AOF 的缺点:恢复数据相对较慢,文件体积大
如果 Redis 服务器同时开启了 RDB 和 AOF 持久化,服务器会 优先使用 AOF 文件来还原数据
(因为 AOF 更新频率比 RDB 更新频率要高,还原的数据更完善)
主从复制
单机有什么问题?
单机即在一台机器上部署一个 redis 节点,主要会存在以下问题:
1、如果发生机器故障,例如磁盘损坏,主板损坏等,未能在短时间内修复好,客户端将无法连接 redis
2、Redis 的内存是有限的,可能放不下那么多的数据
3、单台 Redis 支持的并发量也是有限的
如图上面是 master 节点
,下面是slave
节点,即主节点和从节点。从节点也是可以对外提供服务的,主节点是有数据的,从节点可以通过复制操作将主节点的数据同步过来,并且随着主节点数据不断写入,从节点数据也会做同步的更新。
整体起到的就是数据备份的效果
除了一主一从模型之外,redis 还提供了一主多从的模型,也就是一个 master 可以有多个 slave,也就相当于有了多份的数据副本。
读写分离
除了作为数据备份,主从模型还能做另外一个功能,就是读写分离。
让 master 节点负责提供写服务,slave 节点提供读服务,而将数据读取的压力进行分流和负载,分摊给所
有的从节点。
主从复制的配置
- slaveof 命令
slaveof 198.162.88.66 6379
执行该命令使当前 redis 节点成为指定 redis 节点的从节点,此复制命令是 异步
进行的,redis 会自动进行后续数据复制的操作
如果想取消从节点可以执行 slave of on one
命令
2、修改配置
# 配置主节点的 IP 和端口号
slaveof ip port
# 从节点只做读的操作,保证主从数据的一致性
slave-read-only yes
runid 和复制偏移量
redis 每次启动的时候都会有一个随机的 ID,作为一个标识,这个 ID 就是 runid,当然重启之后值就改变了。
假如端口为 6380 的 redis 去复制 6379,知道 runid
后,在 6380 上做一个标识,如果 runid
改变了,说明主可能重启了或者发生了其它变化,这时候就可以做一个全量复制把数据同步过来。或者第一次启动时根本不知道 6379 的runid
,也会进行全量复制
偏移量:数据写入量的字节
比如主执行 set hello world,就会有一个偏移量,然后从同步数据,也会记录一个偏移量
当两个偏移量达到一致时候,实际上数据就是完全同步的状态。
全量复制
全量复制主节点会将 RDB 文件也就是当前状态去同步给 slave,在此期间主节点新写入的命令会单独记录起来,然后当 RDB 文件加载完毕之后,会通过偏移量对比将这个期间产生的写入值同步给 slave,这样就能达到数据完全同步的效果
全量复制过程
- 在其内部有一条命令
psync
,是做同步的命令,它可以完成全量复制和部分复制的功能,当启动 slave 节点时,它会发送psync
命令给主节点,需要传递两个参数,runid
和offset
(偏移量),也就是从节点向主节点传递主节点的 runid 以及自己的偏移量,对于第一次复制而言,就直接传递?和 -1,当然这个参数是由 slave 内部传的。 - master 接收到命令后知道从希望做全量复制,主就会将自己的 runid 和 offset 传递给从节点
- slave 节点保存 master 的基本信息
- master 执行
bgsave
生成 RDB 文件,并且在此期间新产生的写入命令会被记录到repl_back_buffer
(复制缓冲区) - 主节点向从节点传输 RDB 文件
- 主节点向从节点发送复制缓冲区内容
- 清空从节点旧的数据
- 从节点加载 RDB 文件到内存中,同时加载缓冲区数据
执行全量复制除了开销大之外,还会有个问题:
假如 master 和 slave 网络发生了抖动,那一段时间内这些数据就会丢失,对于 slave 来说这段时间 master 更新的数据是不知道的。最简单的方式就是再做一次全量复制,从而获取到最新的数据,在 redis2.8 之前是这么做的。
部分复制
redis2.8 之后提供部分复制,如果发生类似网络抖动,可以有这样一种机制将这种损失降低到最低,如何实现的?
部分复制过程
- 如果发生了抖动,相当于连接断开了
- 主节点会将写命令记录到缓冲区(repl_back_buffer)
- 当 slave 再次去连接 master 时候,就是说网络抖动结束之后,会触发部分复制
- 从节点会执行 pysnc 命令,将当前自己的 offset 和主的 runid 传递给 master
- 如果发现传输的 offset 偏移量是在 buffer 内的,如果不在内就说明你已经错过了很多数据,buffer 也是有限的,默认是 1M(建议调为 10M),会将 offset 开始到队列结束的数据同步给从节点。这样 master 和 slave 就达到了一致。
通过部分复制有效的降低了全量复制的开销。
Redis Sentinel
简介
Sentinel
(哨兵)是用于监控 redis 集群中 master 状态的工具,是 Redis 的高可用性解决方案,sentinel 哨兵模式已经被集成在 redis2.4 之后的版本中。
Sentinel 系统可以监视一个或者多个 redis master 服务,以及这些 master 服务的所有从服务;当某个 master 服务下线时,自动将该 master 下的某个从服务升级为新的 master 服务,替代已下线的 master 服务继续处理请求,等挂掉的主服务器重连上来,会将它变成从服务器。
三个定时任务
Sentinel 在内部有 3 个定时任务
1)每隔 10 秒——每个 sentinel 会对 master 和 slave 执行 info 命令 ,这个任务主要用来发现 slave 节点和确认主从关系。
2)每隔 2 秒—— 每个 sentinel 通过 master 节点的 channel 交换信息(pub/sub)。master 节点上有一个名为 __sentinel__:hello
的发布订阅的频道。sentinel 节点通过__sentinel__:hello 频道进行信息交换 (对节点的 ” 看法 ” 和自身的信息),达成共识。
3)每隔 1 秒—— 每个 sentinel 对其他 sentinel 和 redis 节点执行 ping 操作(相互监控),这个其实是一个心跳检测,是失败判定的依据。
主观 / 客观下线
判断主服务器是否下线有两种情况:
主观下线
Sentinel 会以每秒一次的频率向与它创建命令连接的实例 (包括主从服务器和其他的 Sentinel) 发送 PING 命令,通过 PING 命令返回的信息判断实例是否在线
如果一个主服务器在 down-after-milliseconds 毫秒内连续向 Sentinel 发送无效回复,那么当前 Sentinel 就会主观认为该主服务器已经下线了。
客观下线
当 Sentinel 将一个主服务器判断为主观下线以后,为了确认该主服务器是否真的下线,它会向同样监视该主服务器的 Sentinel 询问,看它们是否也认为该主服务器是否下线。
如果足够多的 Sentinel 认为该主服务器是下线的,那么就判定该主服务为客观下线,并对主服务器执行故障转移操作。
conf 文件配置
在 redis-sentinel 的 conf 文件里有这么两个配置:
1)sentinel monitor <masterName> <ip> <port> <quorum>
四个参数含义:masterName
这个是对某个 master+slave 组合的一个区分标识。ip
和 port
就是 master 节点的 ip 和 端口号。quorum
这个参数是进行客观下线的一个依据,意思是 至少有 quorum 个 sentinel 主观的认为这个 master 有故障,才会对这个 master 进行下线以及故障转移。因为有的时候,某个 sentinel 节点可能因为自身网络原因,导致无法连接 master,而此时 master 并没有出现故障,所以这就需要多个 sentinel 都一致认为该 master 有问题,才可以进行下一步操作,这就保证了公平性和高可用。
2)sentinel down-after-milliseconds <masterName> <timeout>
这个配置其实就是进行主观下线的一个依据,表示:如果这台 sentinel 超过 timeout 这个时间都无法连通 master 包括 slave 的话,就会主观认为该 master 已经下线。
领导者选举
当一个主服务器被认为客观下线以后,此时需要一个 Sentinel 服务器对该服务器进行处理,因此监视这个下线的主服务器的各个 Sentinel 会进行协商,选举出一个领头的 Sentinel,领头的 Sentinel 会对下线的主服务器执行故障转移操作。
选举领头 Sentinel 的规则也比较多,总的来说就是 先到先得(哪个快,就选哪个)
故障转移处理
- 多个 sentinel 发现并确认 master 有问题
- 选举出一个 sentinel 作为领导
- 选出一个 slave 作为 master
- 通知其余 slave 成为新的 master 的 slave
- 通知客户端主从变化
- 等待老的 master 复活成为新 master 的 slave
注意:挑选某一个从服务器作为主服务器也是有策略的,大概如下:
(1)跟 master 断开连接的时长
(2)slave 优先级
(3)复制 offset 大小
(4)run id
Redis Cluster
Redis3.0 版本之前,可以通过 Redis Sentinel(哨兵)来实现高可用,从 3.0 版本之后,官方推出了Redis Cluster
,它的主要用途是实现数据分片,并且实现高可用。
在 Redis Sentinel 模式中,每个节点需要保存全量数据,冗余比较多,而在 Redis Cluster 模式中,每个分片只需要保存一部分的数据,
Redis Cluster 每个节点都保存各自的数据和整个集群的状态。每个节点都和其他所有节点连接,而且这些连接保持活跃,这样就保证了我们只需要连接集群中的任意一个节点,就可以获取到其他节点的数据。
Redis Cluster 的具体实现细节是采用了 Hash 槽
的概念,集群会预先分配 16384
个槽,并将这些槽分配给具体的服务节点,通过对 Key 进行 CRC16(key)%16384
运算得到对应的槽是哪一个,从而将读写操作转发到该槽所对应的服务节点。当有新的节点加入或者移除的时候,再来迁移这些槽以及其对应的数据。在这种设计之下,我们就可以很方便的进行动态扩容或缩容。
Redis Cluster 为了保证数据的高可用性,加入了主从模式,一个主节点对应一个或多个从节点,主节点提供数据存取,从节点则是从主节点拉取数据备份,当这个主节点挂掉后,就会有这个从节点选取一个来充当主节点,从而保证集群不会挂掉。
有关 redis 集群的安装配置在这里就不多说了,在这里对先对 redis cluster 有个了解,日后再来补充。
参考
从零单排学 Redis
Redis Cluster