首页 > 数据库 > 数据库开发技术 > Redis—听说你速度跟甲斗一样快?——主从复制
现如今,Redis变得越来越流行,在很多业务场景都使用到了Redis。那么在一些高并发、大流量的业务场景里,Redis是如何高效、稳定地提供服务呢?
现在假设你有一台Redis来对外提供服务,架构如下:

Redis负责缓存后端数据库的数据以及用户的写数据,由于Redis的数据都是运行在内存中,所以大大提高了业务的性能
刚开始你的业务流量不算很大,随着时间的推移,你的Redis里的数据越来越多,突然有一天,你的Redis宕机了,这时前端所有的流量都会打到后端的数据库上,这会导致你的数据库压力剧增,严重的话还对压垮数据库
这时你该怎么办,可能你想的是重启Redis不就行了,但是Redis的数据都是在内存中的,重启之后就什么都没了;这时你可能想到文章上篇介绍到的数据持久化机制,通过将内存中的数据持久化到磁盘里,避免了Redis因某些故障导致的数据丢失
但是在恢复数据这段时间里,你的业务很有可能还是会收到影响,有没有更好的方法呢?
一台Redis宕机,只能通过恢复数据来解决,那我们是不是可以部署多个Redis,然后让它们保持数据同步,这样当其中一台Redis宕机时,我们再重新挑选一台Redis继续提供服务就好了
这个方法就是下面我要将的Redis第一个架构——主从复制
在分布式系统中为了解决单点问题,通常会把数据复制多个副本部署到其他机器,满足故障恢复和负载均衡等需求。
Redis也是如此,它为我们提供了复制功能,实现了相同数据的多个Redis副本。
复制功能是高可用Redis 的基础,后面章节的哨兵和集群都是在复制的基础上实现高可用的
优点
保障数据安全,实现高可用
防止单点故障的问题出现
适用于高并发的业务场景
缺点
数据的复制是单向的,只能由主节点(master)到从节点(slave),简单理解就是从节点(slave)只支持读操作,不允许写操作
当主节点(master)出现问题时,Redis便不能提供服务,需要人工干预,将从节点(slave)提升为主节点(master)
由于只有一台master提供服务,当有大量的写操作发生时,容易出现性能瓶颈,而且master的存储能力也是有限的
主从节点数据复制同步方式
全量同步
一般在slave初始化阶段,这时slave需要将master上的所有数据都复制一份
步骤如下:
1.slave连接master,发送PSYNC命令
2.master收到PSYNC命令之后开始执行BGSAVE命令生成RDB快照,在生成过程中使用缓冲区来记录此后执行 的所有写命令
3.master执行完BGSAVE后,向slave发送快照文件,并在发送期间继续记录被执行的写命令
4.slave收到快照文件后丢弃所有旧数据,载入收到的快照
5.master快照发送完毕后开始向slave发送缓冲区中的写命令
6.slave载入快照后,开始接收命令请求,并执行来自master缓冲区的写命令
增量同步
一般在slave初始化后开始正常工作时master将发生的写操作同步到从服务器的过程
增量复制的过程主要是master每执行一个写命令就会向slave发送相同的写命令,slave接收并执行收到的写命令
一般同步策略
主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。
当然,如果有需要,slave在任何时候都可以发起全量同步
redis 策略是,无论如何,首先会尝试进行增量同步,如不成功,要求slave进行全量同步
增量同步其实是以全量同步为基础(得到复制ID),用复制积压缓冲区中的缓存命令做命令重放的增量同步逻辑,不过受制于复制积压缓冲区的容量,它可容忍的范围是有限的。这与持久化机制的AOF混合持久化如出一辙,也与mysql中主从复制的Binlog思路不谋而合
部署

master配置
vim /etc/redis/redis.confbind 0.0.0.0protected-mode noport 6379tcp-backlog 511timeout 0tcp-keepalive 300daemonize yessupervised systemdpidfile "/var/run/redis.pid"loglevel noticelogfile "/var/log/redis/redis.log"databases 16save 900 1save 300 10save 60 10000stop-writes-on-bgsave-error yesrdbcompression yesrdbchecksum yesdbfilename "dump.rdb"dir "/var/lib/redis"slave-serve-stale-data yesslave-read-only yesrepl-diskless-sync norepl-diskless-sync-delay 5repl-disable-tcp-nodelay noslave-priority 100
slave1配置
vim /etc/redis/redis.confbind 0.0.0.0protected-mode noport 6379tcp-backlog 511timeout 0tcp-keepalive 300daemonize yessupervised systemdpidfile "/var/run/redis.pid"loglevel noticelogfile "/var/log/redis/redis.log"databases 16save 900 1save 300 10save 60 10000stop-writes-on-bgsave-error yesrdbcompression yesrdbchecksum yesdbfilename "dump.rdb"dir "/var/lib/redis"slave-serve-stale-data yesslave-read-only yesrepl-diskless-sync norepl-diskless-sync-delay 5repl-disable-tcp-nodelay noslave-priority 100appendonly noappendfilename "appendonly.aof"appendfsync everysecno-appendfsync-on-rewrite noauto-aof-rewrite-percentage 100auto-aof-rewrite-min-size 64mbaof-load-truncated yeslua-time-limit 5000slowlog-log-slower-than 10000slowlog-max-len 128latency-monitor-threshold 0notify-keyspace-events ""hash-max-ziplist-entries 512hash-max-ziplist-value 64list-max-ziplist-size -2list-compress-depth 0set-max-intset-entries 512zset-max-ziplist-entries 128zset-max-ziplist-value 64hll-sparse-max-bytes 3000activerehashing yesclient-output-buffer-limit normal 0 0 0client-output-buffer-limit slave 256mb 64mb 60client-output-buffer-limit pubsub 32mb 8mb 60hz 10aof-rewrite-incremental-fsync yesslaveof 192.168.149.128 6379
slave2配置
vim /etc/redis/redis.confbind 0.0.0.0protected-mode noport 6379tcp-backlog 511timeout 0tcp-keepalive 300daemonize yessupervised systemdpidfile "/var/run/redis.pid"loglevel noticelogfile "/var/log/redis/redis.log"databases 16save 900 1save 300 10save 60 10000stop-writes-on-bgsave-error yesrdbcompression yesrdbchecksum yesdbfilename "dump.rdb"dir "/var/lib/redis"slave-serve-stale-data yesslave-read-only yesrepl-diskless-sync norepl-diskless-sync-delay 5repl-disable-tcp-nodelay noslave-priority 100appendonly noappendfilename "appendonly.aof"appendfsync everysecno-appendfsync-on-rewrite noauto-aof-rewrite-percentage 100auto-aof-rewrite-min-size 64mbaof-load-truncated yeslua-time-limit 5000slowlog-log-slower-than 10000slowlog-max-len 128latency-monitor-threshold 0notify-keyspace-events ""hash-max-ziplist-entries 512hash-max-ziplist-value 64list-max-ziplist-size -2list-compress-depth 0set-max-intset-entries 512zset-max-ziplist-entries 128zset-max-ziplist-value 64hll-sparse-max-bytes 3000activerehashing yesclient-output-buffer-limit normal 0 0 0client-output-buffer-limit slave 256mb 64mb 60client-output-buffer-limit pubsub 32mb 8mb 60hz 10aof-rewrite-incremental-fsync yesslaveof 192.168.149.128 6379
配置文件字段解读(挑重点)
## 默认yes,开启之后保护你的redis实例,只能本地使用,外网不允许连接和操作protected-mode no## 是否以守护进程方式启动daemonize yes## no:开机不会自启动;systemed:开机自启动supervised systemd## RDB持久化配置save 900 1save 300 10save 60 10000stop-writes-on-bgsave-error yes ## 当启用了RDB且最后一次后台保存数据失败,Redis是否停止接收数据rdbcompression yes ## 对于存储到磁盘中的快照,可以设置是否进行压缩存储rdbchecksum yes ## 在存储快照后,我们还可以让redis使用CRC64算法来进行数据校验## salve配置,指定哪台服务器是masterslaveof 192.168.149.128 6379## 默认redis使用的是rdb方式持久化appendonly no
三台服务器配置之后我们开启redis服务
systemctl start redis-server我们验证一下,我们在master上登陆redis查看下主从信息
[root@master ~]# redis-cli -p 6379127.0.0.1:6379> info Replication# Replicationrole:masterconnected_slaves:2slave0:ip=192.168.149.129,port=6379,state=online,offset=169,lag=0slave1:ip=192.168.149.130,port=6379,state=online,offset=169,lag=0master_repl_offset:169repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:2repl_backlog_histlen:168
可以看到master下有两台slave,分别是192.168.149.129、192.168.149.130
我们随便登上一台slave的Redis查看一下
[root@slave1 ~]# redis-cli -p 6379127.0.0.1:6379> info replication# Replicationrole:slavemaster_host:192.168.149.128master_port:6379master_link_status:upmaster_last_io_seconds_ago:1master_sync_in_progress:0slave_repl_offset:267slave_priority:100slave_read_only:1connected_slaves:0master_repl_offset:0repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:0repl_backlog_histlen:0
我们在master上进行写操作,观察slave有没有同步数据
# master操作192.168.149.128:6379> set name pythonOK192.168.149.128:6379> get name"python"# slave同步数据192.168.149.129:6379> get name"python"192.168.149.130:6379> get name"python"
我们需要注意的是,slave只能读数据,不能写数据
192.168.149.130:6379> set name1 java(error) READONLY You can't write against a read only slave.
原文作者:咸鱼运维杂谈;如有侵权,请联系管理员删除。