现在假设你有一台Redis来对外提供服务,架构如下
Redis负责缓存后端数据库的数据以及用户的写数据,由于Redis的数据都是运行在内存中,所以大大提高了业务的性能
刚开始你的业务流量不算很大,随着时间的推移,你的Redis里的数据越来越多,突然有一天,你的Redis宕机了,这时前端所有的流量都会打到后端的数据库上,这会导致你的数据库压力剧增,严重的话还对压垮数据库
这时你该怎么办,可能你想的是重启Redis不就行了,但是Redis的数据都是在内存中的,重启之后就什么都没了;这时你可能想到文章上篇介绍到的数据持久化机制,通过将内存中的数据持久化到磁盘里,避免了Redis因某些故障导致的数据丢失
但是在恢复数据这段时间里,你的业务很有可能还是会收到影响,有没有更好的方法呢?
一台Redis宕机,只能通过恢复数据来解决,那我们是不是可以部署多个Redis,然后让它们保持数据同步,这样当其中一台Redis宕机时,我们再重新挑选一台Redis继续提供服务就好了
这个方法就是下面我要将的Redis第一个架构——主从复制
1.主从复制
- 在分布式系统中为了解决单点问题,通常会把数据复制多个副本部署到其他机器,满足故障恢复和负载均衡等需求。Redis也是如此,它为我们提供了复制功能,实现了相同数据的多个Redis副本。复制功能是高可用Redis 的基础,后面章节的哨兵和集群都是在复制的基础上实现高可用的
- 优点:保障数据安全,实现高可用
缺点:
- 主节点出现问题,不能提供服务。需要人工修改配置
- 主节点的写能力有限
- 单机节点存储能力有限
数据同步方式
- 全量同步
一般发生再slave初始化阶段,这时slave需要将master上的所有数据都复制一份
步骤如下:
1.从服务器连接主服务器,发送PSYNC命令
2.主服务器收到PSYNC命令之后开始执行BGSAVE命令生成RDB快照,在生成过程中使用缓冲区来记录此后执行的所有写命令
3.主服务器执行完BGSAVE后,向从服务器发送快照文件,并在发送期间继续记录被执行的写命令
4.从服务器收到快照文件后丢弃所有旧数据,载入收到的快照
5.主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令
6.从服务器载入快照后,开始接收命令请求,并执行来自主服务器缓冲区的写命令
- 增量同步
slave初始化后开始正常工作时主服务器将发生的写操作同步到从服务器的过程
增量复制的过程主要是主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令
- 同步策略
主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,slave在任何时候都可以发起全量同步
redis 策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步
增量同步其实是以全量同步为基础(得到复制ID),用复制积压缓冲区中的缓存命令做命令重放的增量同步逻辑,不过受制于复制积压缓冲区的容量,它可容忍的范围是有限的。这与持久化机制的AOF混合持久化如出一辙,也与mysql中主从复制的Binlog思路不谋而合
部署
- master配置
vim /etc/redis/redis.conf
bind 0.0.0.0
protected-mode no
port 6379
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize yes
supervised systemd
pidfile "/var/run/redis.pid"
loglevel notice
logfile "/var/log/redis/redis.log"
databases 16
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename "dump.rdb"
dir "/var/lib/redis"
slave-serve-stale-data yes
slave-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
repl-disable-tcp-nodelay no
slave-priority 100
- slave1配置
vim /etc/redis/redis.conf
bind 0.0.0.0
protected-mode no
port 6379
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize yes
supervised systemd
pidfile "/var/run/redis.pid"
loglevel notice
logfile "/var/log/redis/redis.log"
databases 16
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename "dump.rdb"
dir "/var/lib/redis"
slave-serve-stale-data yes
slave-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
repl-disable-tcp-nodelay no
slave-priority 100
appendonly no
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
lua-time-limit 5000
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
aof-rewrite-incremental-fsync yes
slaveof 192.168.149.128 6379
- slave2配置
vim /etc/redis/redis.conf
bind 0.0.0.0
protected-mode no
port 6379
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize yes
supervised systemd
pidfile "/var/run/redis.pid"
loglevel notice
logfile "/var/log/redis/redis.log"
databases 16
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename "dump.rdb"
dir "/var/lib/redis"
slave-serve-stale-data yes
slave-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
repl-disable-tcp-nodelay no
slave-priority 100
appendonly no
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
lua-time-limit 5000
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
aof-rewrite-incremental-fsync yes
slaveof 192.168.149.128 6379
- 启动则完成主从复制架构的部署
systemctl start redis-server
- 验证
[root@master ~]# redis-cli -p 6379
127.0.0.1:6379> info Replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.149.129,port=6379,state=online,offset=169,lag=0
slave1:ip=192.168.149.130,port=6379,state=online,offset=169,lag=0
master_repl_offset:169
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:168
#slave1
[root@slave1 ~]# redis-cli -p 6379
127.0.0.1:6379> info replication
# Replication
role:slave
master_host:192.168.149.128
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:267
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
#salve2
[root@slave2 ~]# redis-cli -p 6379
127.0.0.1:6379> info Replication
# Replication
role:slave
master_host:192.168.149.128
master_port:6379
master_link_status:up
master_last_io_seconds_ago:3
master_sync_in_progress:0
slave_repl_offset:379
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
- 测试
现在master上写入,看slave有没有同步数据
127.0.0.1:6379> set name python
OK
127.0.0.1:6379> get name
"python"
在slave上查看
127.0.0.1:6379> get name
"python"
127.0.0.1:6379> get name
"python"
ps:slave只能读数据,不能写数据
127.0.0.1:6379> set name1 java
(error) READONLY You can't write against a read only slave.