二、Redis主从、哨兵及集群架构实战

710 阅读6分钟

一、Redis持久化


1、RDB快照(snapshot)

在默认情况下,Redis将内存数据库快照保存在名字为dump.rdb的二进制文件中。可以通过对Redis进行设置,让它(在n秒内数据集至少有m个改动)的条件被满足时,自动保存一次数据集。

比如说,以下设置会让Redis在满足(60秒内有至少有1000个键被改动)这一条件时,自动保存一次数据集:

# save 60 1000 

默认配置为:

# 15分钟内至少修改1次
save 900 1
# 5分钟内至少修改10次
save 300 10
# 1分钟内至少修改10000次
save 60 10000
# 生成的快照文件的文件名称
dbfilename dump.rdb
# 生成的快照文件的位置
dir ./

关闭RDB只需要将所有的save保存策略注释掉即可

还可以手动执行命令生成RDB快照,进入redis客户端执行命令save或bgsave可以生成dump.rdb文件,每次命令执行都会将所有redis内存快照到一个新的rdb文件里,并覆盖原有rdb快照文件。save是同步命令,bgsave是异步命令,bgsave会从redis主进程fork出一个子进程专门用来生成rdb快照文件

save与bgsave对比:

命令 save bgsave
IO类型 同步 异步
是否阻塞redis其它命令 否(在生成子进程执行调用fork函数时会有短暂阻塞)
复杂度 O(n) O(n)
优点 不会消耗额外内存 不会阻塞客户端命令
缺点 会阻塞客户端命令 需要fork子进程,消耗内存

配置自动生成rdb文件后台使用的是bgsave方式

2、AOF(append-only file)

快照功能并不是非常耐久(durable):如果Redis因为某些原因而造成故障停机,那么服务器将丢失最近写入、且仍未保存到快照中的那些数据。从1.1版本开始,Redis增加了一种完全耐久的持久化方式:AOF持久化,将修改的每一条指令记录进文件appendonly.aof中,可以通过修改配置文件来打开AOF功能:

appendonly yes

默认配置为:

# 默认是关闭状态
appendonly no

appendfilename "appendonly.aof"
dir ./

从现在开始,每当Redis执行一个改变数据集的命令时,这个命令就会被追加到AOF文件的末尾。

这样的话,当Redis重新启动时,程序就可以通过重新执行AOF文件中的命令来达到重建数据集的目的。

可以配置Redis多久才将数据fsync到磁盘一次,有如下三个选项:

  • appendfsync always:每次有新命令追加到AOF文件时就执行一次fsync,非常慢,也非常安全。
  • appendfsync everysec:每秒fsync一次,足够快(和使用 RDB 持久化差不多),并且在故障时只会丢失1秒钟的数据。
  • appendfsync no:从不fsync,将数据交给操作系统来处理。更快,也更不安全的选择。

推荐(并且也是默认)的措施为每秒fsync一次,这种fsync策略可以兼顾速度和安全性。

# appendfsync always
appendfsync everysec
# appendfsync no

AOF重写

AOF文件里可能有太多没用指令,所以AOF会定期根据内存的最新数据生成aof文件

例如,执行了如下几条命令:

127.0.0.1:6379> incr readcount
(integer) 1
127.0.0.1:6379> incr readcount
(integer) 2
127.0.0.1:6379> incr readcount
(integer) 3
127.0.0.1:6379> incr readcount
(integer) 4
127.0.0.1:6379> incr readcount
(integer) 5
127.0.0.1:6379> incr readcount
(integer) 6

重写后AOF文件里变成:

*2
$6
SELECT
$1
0
*3
$3
SET
$9
readcount
$1
5

如下两个配置可以控制AOF自动重写频率:

# aof文件自上一次重写后文件大小增长了100%则再次触发重写
auto-aof-rewrite-percentage 100
# aof文件至少要达到64M才会自动重写,文件太小恢复速度本来就很快,重写的意义不大
auto-aof-rewrite-min-size 64mb

当然aof还可以手动重写,进入redis客户端,执行命令bgrewriteaof重写aof

127.0.0.1:6379> bgrewriteaof
Background append only file rewriting started

注意:AOF重写redis会fork出一个子进程去做,不会对redis正常命令处理有太多影响

RDB 和 AOF ,我应该用哪一个?

命令 RDB AOF
启动优先级
体积
恢复速度
数据安全性 容易丢数据 根据策略决定

redis启动时如果既有rdb文件又有aof文件则优先选择aof文件恢复数据,因为aof一般来说数据更全一点

3、混合持久化

重启Redis时,我们很少使用RDB来恢复内存状态,因为会丢失大量数据。我们通常使用AOF日志重放,但是重放AOF日志性能相对RDB来说要慢很多,这样在Redis实例很大的情况下,启动需要花费很长的时间。Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化。

通过如下配置可以开启混合持久化:

# 默认是开启状态
aof-use-rdb-preamble yes

如果开启了混合持久化,AOF在重写时,不再是单纯将内存数据转换为RESP命令写入AOF文件,而是将重写这一刻之前的内存做RDB快照处理,并且将RDB快照内容和增量的AOF存在一起,都写入新的AOF文件,新的文件一开始不叫appendonly.aof,等到重写完新的AOF文件才会进行改名,原子的覆盖原有的AOF文件,完成新旧两个AOF文件的替换。

于是在Redis重启的时候,可以先加载RDB的内容,然后再重放增量AOF日志就可以完全替代之前的AOF全量文件重放,因此重启效率大幅得到提升。

混合持久化AOF文件结构:

二、Redis主从架构


1、Redis主从工作原理

如果为master配置了一个slave,不管这个slave是否是第一次连接上Master,它都会发送一个SYNC命 令(redis2.8版本之前的命令)给master请求复制数据。

master收到SYNC命令后,会在后台进行数据持久化,通过bgsave生成最新的rdb快照文件,持久化期间, master会继续接收客户端的请求,它会把这些可能修改数据集的请求缓存在内存中。当持久化进行完毕以 后,master会把这份rdb文件数据集发送给slave,slave会把接收到的数据进行持久化生成rdb,然后再加 载到内存中。然后,master再将之前缓存在内存中的命令发送给slave。

当master与slave之间的连接由于某些原因而断开时,slave能够自动重连Master,如果master收到了多 个slave并发连接请求,它只会进行一次持久化,而不是一个连接一次,然后再把这一份持久化的数据发送给多个并发连接的slave。

当master和slave断开重连后,一般都会对整份数据进行复制。但从redis2.8版本开始,master和slave断开重连后支持部分复制。

2、redis主从搭建

redis主从架构搭建,配置主从节点步骤:

$ cd /usr/local/redis/

$ mkdir -p data/6379
$ mkdir -p data/6380
$ mkdir -p data/6381

$ mkdir config
$ cp redis.conf ./config/redis-6379.conf
$ cp redis.conf ./config/redis-6380.conf
$ cp redis.conf ./config/redis-6381.conf

$ ./src/redis-server ./config/redis-6379.conf 
$ 127.0.0.1:6379> auth cyan001
$ ./src/redis-server ./config/redis-6380.conf 
$ 127.0.0.1:6380> auth cyan001
$ ./src/redis-server ./config/redis-6381.conf 
$ 127.0.0.1:6381> auth cyan001

$ ./src/redis-cli -p 6379
# 查看主/从复制信息
127.0.0.1:6379> info replication
$ ./src/redis-cli -p 6380
# 查看主/从复制信息
127.0.0.1:6380> info replication
$ ./src/redis-cli -p 6381
# 查看主/从复制信息
127.0.0.1:6381> info replication

主节点配置文件:

# bind 127.0.0.1
protected-mode no
port 6379
daemonize yes
pidfile /var/run/redis_6379.pid
logfile "6379.log"
dir /usr/local/redis/data/6379
# 设置密码
requirepass cyan001
masterauth cyan001

appendonly yes
appendfilename "appendonly-6379.aof"

从节点1配置文件:

# bind 127.0.0.1
protected-mode no
port 6380
daemonize yes
pidfile /var/run/redis_6380.pid
logfile "6380.log"
dir /usr/local/redis/data/6380
# 设置密码
requirepass cyan001

appendonly yes
appendfilename "appendonly-6380.aof"

# 从本机6379的redis实例复制数据
replicaof 106.13.125.89 6379
masterauth cyan001
replica-read-only yes

从节点2配置文件:

# bind 127.0.0.1
protected-mode no
port 6381
daemonize yes
pidfile /var/run/redis_6381.pid
logfile "6381.log"
dir /usr/local/redis/data/6381

# 设置密码
requirepass cyan001

appendonly yes
appendfilename "appendonly-6381.aof"

# 从本机6379的redis实例复制数据
replicaof 106.13.125.89 6379
masterauth cyan001
replica-read-only yes

测试在6379实例上写数据,6380实例和6381实例是否能及时同步新修改的数据

主节点挂机后,从节点如何转换为主节点?

# 模拟6379挂了
127.0.0.1:6379> shutdown
# 手动将6380设为主节点
127.0.0.1:6380> slaveof no one
# 手动修改6381为6380的从节点
127.0.0.1:6381> slaveof 127.0.0.1 6380

3、Java操作redis

三、Redis哨兵高可用架构


sentinel哨兵是特殊的redis服务,不提供读写服务,主要用来监控redis实例节点。

哨兵架构下client端第一次从哨兵找出redis的主节点,后续就直接访问redis的主节点,不会每次都通过sentinel代理访问redis的主节点,当redis的主节点发生变化,哨兵会第一时间感知到,并且将新的redis主节点通知给client端(这里面redis的client端一般都实现了订阅功能,订阅sentinel发布的节点变动消息)

1、redis哨兵搭建

redis哨兵架构搭建步骤:

$ cd /usr/local/redis/

$ mkdir -p data/26379
$ mkdir -p data/26380
$ mkdir -p data/26381

$ cp sentinel.conf ./config/sentinel-26379.conf
$ cp sentinel.conf ./config/sentinel-26380.conf
$ cp sentinel.conf ./config/sentinel-26381.conf

$ ./src/redis-sentinel ./config/sentinel-26379.conf 
$ ./src/redis-sentinel ./config/sentinel-26380.conf 
$ ./src/redis-sentinel ./config/sentinel-26381.conf 

$ ./src/redis-cli -p 26379
127.0.0.1:26379> info sentinel
$ ./src/redis-cli -p 26380
127.0.0.1:26380> info sentinel
$ ./src/redis-cli -p 26381
127.0.0.1:26381> info sentinel

# 可以看到Sentinel的info里已经识别出了redis的主从
127.0.0.1:26379>info

redis哨兵1配置文件:

port 26379
daemonize yes
pidfile /var/run/redis-sentinel-26379.pid
logfile "26379.log"
dir /usr/local/redis/data/26379

# sentinel monitor <master-name> <ip> <redis-port> <quorum>
# quorum是一个数字,指明当有多少个sentinel认为一个master失效时(值一般为:sentinel总数/2 + 1),master才算真正失效
sentinel monitor mymaster 106.13.125.89 6379 2
sentinel auth-pass mymaster cyan001

redis哨兵2配置文件:

port 26380
daemonize yes
pidfile /var/run/redis-sentinel-26380.pid
logfile "26380.log"
dir /usr/local/redis/data/26380

# sentinel monitor <master-name> <ip> <redis-port> <quorum>
# quorum是一个数字,指明当有多少个sentinel认为一个master失效时(值一般为:sentinel总数/2 + 1),master才算真正失效
sentinel monitor mymaster 106.13.125.89 6379 2
sentinel auth-pass mymaster cyan001

redis哨兵3配置文件:

port 26381
daemonize yes
pidfile /var/run/redis-sentinel-26381.pid
logfile "26381.log"
dir /usr/local/redis/data/26381

# sentinel monitor <master-name> <ip> <redis-port> <quorum>
# quorum是一个数字,指明当有多少个sentinel认为一个master失效时(值一般为:sentinel总数/2 + 1),master才算真正失效
sentinel monitor mymaster 106.13.125.89 6379 2
sentinel auth-pass mymaster cyan001

测试在6379实例上写数据,6380实例和6381实例是否能及时同步新修改的数据

2、Java操作redis哨兵

四、redis高可用集群


1、redis集群方案比较

1)哨兵模式

在redis3.0以前的版本要实现集群一般是借助哨兵sentinel工具来监控master节点的状态,如果master节点异常,则会做主从切换,将某一台slave作为master,哨兵的配置略微复杂,并且性能和高可用性等各方面表现一般,特别是在主从切换的瞬间存在访问瞬断的情况,而且哨兵模式只有一个主节点对外提供服务,没法支持很高的并发,且单个主节点内存也不宜设置得过大,否则会导致持久化文件过大,影响数据恢复或主从同步的效率

2)高可用集群模式

redis集群是一个由多个主从节点群组成的分布式服务器群,它具有复制、高可用和分片特性。Redis集群不需要sentinel哨兵也能完成节点移除和故障转移的功能。需要将每个节点设置成集群模式,这种集群模式没有中心节点,可水平扩展,据官方文档称可以线性扩展到上万个节点(官方推荐不超过1000个节点)。redis集群的性能和高可用性均优于之前版本的哨兵模式,且集群配置非常简单

2、Redis高可用集群搭建

redis集群需要至少要三个master节点,这里搭建三个master节点,并且给每个master再搭建一个slave节点,总共6个redis节点,搭建集群的步骤如下:

$ cd /usr/local/redis/

$ mkdir -p data/8001
$ mkdir -p data/8002
$ mkdir -p data/8003
$ mkdir -p data/8004
$ mkdir -p data/8005
$ mkdir -p data/8006

$ cp redis.conf config/redis-8001.conf
$ cp redis.conf config/redis-8002.conf
$ cp redis.conf config/redis-8003.conf
$ cp redis.conf config/redis-8004.conf
$ cp redis.conf config/redis-8005.conf
$ cp redis.conf config/redis-8006.conf

$ ./src/redis-server ./config/redis-8001.conf
$ ./src/redis-server ./config/redis-8002.conf
$ ./src/redis-server ./config/redis-8003.conf
$ ./src/redis-server ./config/redis-8004.conf
$ ./src/redis-server ./config/redis-8005.conf
$ ./src/redis-server ./config/redis-8006.conf

# 创建redis集群
$ ./src/redis-cli -a cyan001 --cluster create --cluster-replicas 1 106.13.125.89:8001 106.13.125.89:8002 106.13.125.89:8003 106.13.125.89:8004 106.13.125.89:8005 106.13.125.89:8006

$ ./src/redis-cli -a cyan001 -c -h 106.13.125.89 -p 8001

# 查看集群信息
106.13.125.89:8001> cluster info
# 查看节点列表
106.13.125.89:8001> cluster nodes

修改配置文件:

# bind 127.0.0.1
protected-mode no
port 8001
daemonize yes
pidfile /var/run/redis_8001.pid
logfile "8001.log"
dir /usr/local/redis/data/8001

# 启动集群模式
cluster-enabled yes
cluster-config-file nodes-8001.conf
cluster-node-timeout 15000

# 设置密码
requirepass cyan001
masterauth cyan001
appendonly yes
appendfilename "appendonly-8001.aof"

其余五个配置文件内容类似,只需把上述修改过的文件内容中的8001依次替换为8002、8003、8004、8005、8006即可(%s/源字符串/目标字符串/g)

3、Java操作redis集群

4、Redis集群原理分析