现如今,Redis变得越来越流行,在很多业务场景都使用到了Redis。那么在一些高并发、大流量的业务场景里,Redis是如何高效、稳定地提供服务呢?
现在假设你有一台Redis来对外提供服务,架构如下
Redis负责缓存后端数据库的数据以及用户的写数据,由于Redis的数据都是运行在内存中,所以大大提高了业务的性能
刚开始你的业务流量不算很大,随着时间的推移,你的Redis里的数据越来越多,突然有一天,你的Redis宕机了,这时前端所有的流量都会打到后端的数据库上,这会导致你的数据库压力剧增,严重的话还对压垮数据库
这时你该怎么办,可能你想的是重启Redis不就行了,但是Redis的数据都是在内存中的,重启之后就什么都没了;这时你可能想到文章上篇介绍到的数据持久化机制,通过将内存中的数据持久化到磁盘里,避免了Redis因某些故障导致的数据丢失
但是在恢复数据这段时间里,你的业务很有可能还是会收到影响,有没有更好的方法呢?
一台Redis宕机,只能通过恢复数据来解决,那我们是不是可以部署多个Redis,然后让它们保持数据同步,这样当其中一台Redis宕机时,我们再重新挑选一台Redis继续提供服务就好了
这个方法就是下面我要将的Redis第一个架构——主从复制
数据同步方式
一般发生再slave初始化阶段,这时slave需要将master上的所有数据都复制一份
步骤如下:
1.从服务器连接主服务器,发送PSYNC命令
2.主服务器收到PSYNC命令之后开始执行BGSAVE命令生成RDB快照,在生成过程中使用缓冲区来记录此后执行的所有写命令
3.主服务器执行完BGSAVE后,向从服务器发送快照文件,并在发送期间继续记录被执行的写命令
4.从服务器收到快照文件后丢弃所有旧数据,载入收到的快照
5.主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令
6.从服务器载入快照后,开始接收命令请求,并执行来自主服务器缓冲区的写命令
slave初始化后开始正常工作时主服务器将发生的写操作同步到从服务器的过程
增量复制的过程主要是主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令
主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,slave在任何时候都可 以发起全量同步
redis 策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步
增量同步其实是以全量同步为基础(得到复制ID),
用复制积压缓冲区中的缓存命令做命令重放的增量同步逻辑,
不过受制于复制积压缓冲区的容量,它可容忍的范围是有限的。
这与持久化机制的AOF混合持久化如出一辙,也与mysql中主从复制的Binlog思路不谋而合
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
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
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
## 默认yes,开启之后保护你的redis实例,只能本地使用,外网不允许连接和操作
protected-mode no
## 是否以守护进程方式启动
daemonize yes
## no:开机不会自启动;systemed:开机自启动
supervised systemd
## RDB持久化配置
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes ## 当启用了RDB且最后一次后台保存数据失败,Redis是否停止接
收数据
rdbcompression yes ## 对于存储到磁盘中的快照,可以设置是否进行压缩存储
rdbchecksum yes ## 在存储快照后,我们还可以让redis使用CRC64算法来进行数据校验
## salve配置,指定哪台服务器是master
slaveof 192.168.149.128 6379
## 默认redis使用的是rdb方式持久化
appendonly no
systemctl start redis-server
可以看到master下有两台slave,分别是192.168.149.129、192.168.149.130
[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
我们随便登上一台slave的Redis查看一下
#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有没有同步数据
# master操作
192.168.149.128:6379> set name python
OK
192.168.149.128:6379> get name
"python"
# slave同步数据
192.168.149.129:6379> get name
"python"
192.168.149.130:6379> get name
"python""
ps:slave只能读数据,不能写数据
192.168.149.129:6379> set name1 java
(error) READONLY You can't write against a read only slave.