该文章为知识总结的文章,如果是初学者,建议先从专栏学习:Redis专栏

一、容灾策略

1. 如何配置RDB持久化机制

redis.conf文件,也就是/etc/redis/6379.conf,去配置持久化

save 900 1
save 300 10
save 60 10000

每隔60s,如果有超过1000个key发生了变更,那么就生成一个新的dump.rdb文件,就是当前redis内存中完整的数据快照,这个操作也被称之为snapshotting,快照

也可以手动调用save或者bgsave命令,同步或异步执行rdb快照生成

save可以设置多个,就是多个snapshotting检查点,每到一个检查点,就会去check一下,是否有指定的key数量发生了变更,如果有,就生成一个新的dump.rdb文件

2. RDB持久化机制的工作流程

(1)redis根据配置自己尝试去生成rdb快照文件

(2)fork一个子进程出来

(3)子进程尝试将数据dump到临时的rdb快照文件中

(4)完成rdb快照文件的生成之后,就替换之前的旧的快照文件

dump.rdb,每次生成一个新的快照,都会覆盖之前的老快照

3. AOF持久化的配置

AOF持久化,默认是关闭的,默认是打开RDB持久化

appendonly yes,可以打开AOF持久化机制,在生产环境里面,一般来说AOF都是要打开的,除非你说随便丢个几分钟的数据也无所谓

打开AOF持久化机制之后,redis每次接收到一条写命令,就会写入日志文件中,当然是先写入os cache的,然后每隔一定时间再fsync一下

而且即使AOF和RDB都开启了,redis重启的时候,也是优先通过AOF进行数据恢复的,因为aof数据比较完整

可以配置AOF的fsync策略,有三种策略可以选择,一种是每次写入一条数据就执行一次fsync; 一种是每隔一秒执行一次fsync; 一种是不主动执行fsync

  • always: 每次写入一条数据,立即将这个数据对应的写日志fsync到磁盘上去,性能非常非常差,吞吐量很低; 确保说redis里的数据一条都不丢,那就只能这样了

  • everysec: 每秒将os cache中的数据fsync到磁盘,这个最常用的,生产环境一般都这么配置,性能很高,QPS还是可以上万的,但是可能会失去一秒的数据

  • no: 仅仅redis负责将数据写入os cache就撒手不管了,然后后面os自己会时不时有自己的策略将数据刷入磁盘,不可控了

# appendfsync always
appendfsync everysec
# appendfsync no

4. AOF rewrite

redis中的数据其实有限的,很多数据可能会自动过期,可能会被用户删除,可能会被redis用缓存清除的算法清理掉。

redis中的数据会不断淘汰掉旧的,就一部分常用的数据会被自动保留在redis内存中

所以可能很多之前的已经被清理掉的数据,对应的写日志还停留在AOF中,AOF日志文件就一个,会不断的膨胀,到很大很大。

所以AOF会自动在后台每隔一定时间做rewrite操作,比如日志里已经存放了针对100w数据的写日志了; redis内存只剩下10万; 基于内存中当前的10万数据构建一套最新的日志,到AOF中; 覆盖之前的老日志; 确保AOF日志文件不会过大,保持跟redis内存数据量一致

no-appendfsync-on-rewrite no

当进行rewrite操作时,涉及大量磁盘操作,这样就会造成主进程在写aof文件的时候出现阻塞的情形,设置成no意思就是接受阻塞,而设置成yes则相当于将appendfsync设置为no,将可能丢失30s的数据

如果应用系统无法忍受延迟,而可以容忍少量的数据丢失,则设置为yes;如果应用系统无法忍受数据丢失,则设置为no。

在redis.conf中,可以配置rewrite策略

auto-aof-rewrite-percentage 100  # 增长超过100%的比例

auto-aof-rewrite-min-size 64mb   # 最小的重写大小

每一次rewrite之后会记住当前文件的大小,当文件大小超过一定比例时就会进行rewrite

比如说上一次AOF rewrite之后,是128mb

然后就会接着128mb继续写AOF的日志,如果发现增长的比例,超过了之前的100%,256mb,就可能会去触发一次rewrite

但是此时还要去跟min-size,64mb去比较,256mb > 64mb,才会去触发rewrite

重写的过程

  1. redis fork一个子进程

  2. 子进程基于当前内存中的数据,构建日志,开始往一个新的临时的AOF文件中写入日志

  3. redis主进程,接收到client新的写操作之后,在内存中写入日志,同时新的日志也继续写入旧的AOF文件

  4. 子进程写完新的日志文件之后,redis主进程将内存中的新日志再次追加到新的AOF文件中

  5. 用新的日志文件替换掉旧的日志文件

5. AOF破损文件的修复

如果redis在append数据到AOF文件时,机器宕机了,可能会导致AOF文件破损

用redis-check-aof --fix命令来修复破损的AOF文件,就是删除那些破损的命令

6. AOF和RDB同时工作

  1. RDB和AOF 重写同一时间只会执行一个
  2. 同时有RDB snapshot文件和AOF日志文件,那么redis重启的时候,会优先使用AOF进行数据恢复,因为其中的日志更完整

7. 企业级的备份策略

RDB中每隔一分钟更改的数据量为多少需要根据业务需求改变

博主设置的为save 60 1000

AOF一定要打开,fsync磁盘刷新策略使用everysec,重写策略采用就是超过100%,最小大小设置为16mb

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 16mb

然后设置定时任务每天和每个小时都做一个备份,然后每天都将备份上传到云服务器上

备份也可以避免上线出现的BUG,比如12点上线了代码,发现代码有bug,导致代码生成的所有的缓存数据,写入redis,全部错了找到一份11点的rdb的冷备,然后按照上面的步骤,去恢复到11点的数据

二、读写分离

单机Redis最高不超过10万QPS,一般情况下,大量的请求都是读请求

1. redis replication主从复制过程

千万不能关闭主节点的持久化,否则一旦重启主节点,数据将是空,然后将全部的从节点也变成空

当启动一个slave node的时候,它会发送一个PSYNC命令给master node

如果这是slave node重新连接master node,那么master node仅仅会复制给slave部分缺少的数据; 否则如果是slave node第一次连接master node,那么会触发一次全量复制

全量复制

  • master会启动一个后台线程,开始生成一份RDB快照文件,同时还会将从客户端收到的所有写命令缓存在内存中。RDB文件生成完毕之后,master会将这个RDB发送给slave,slave会先写入本地磁盘,然后再从本地磁盘加载到内存中。然后master会将内存中缓存的写命令发送给slave,slave也会同步这些数据。
  • 如果发现有多个slave node都来重新连接,仅仅会启动一个rdb save操作,用一份数据服务所有slave node。

异步同步

  • 当给一个主节点写一条数据的时候,会直接返回给客户端写入成功,然后在异步的把这个命令同步给从节点

断点续传

  • 如果主从复制过程中,网络连接断掉了,那么可以接着上次复制的地方,继续复制下去,而不是从头开始复制一份

无磁盘化同步

  • master在内存中直接创建rdb,然后发送给slave,不会在自己本地落地磁盘了
repl-diskless-sync yes      # 开启无磁盘化,默认是false
repl-diskless-sync-delay 5  # 等待一定时长再开始复制,因为要等更多slave重新连接过来,默认等待5秒

过期key处理

  • slave不会过期key,只会等待master过期key。如果master过期了一个key,或者通过LRU淘汰了一个key,那么会模拟一条del命令发送给slave。

2. 主从搭建

从节点

修改绑定的IP地址

bind 0.0.0.0

配置从节点

slaveof  192.168.xxx.xxx 6379

强制读写分离

slave-read-only yes
  • 开启了只读的redis slave node,会拒绝所有的写操作,这样可以强制搭建成读写分离的架构

集群安全认证

masterauth redis-pass # master连接口令

主节点

修改绑定的IP地址

bind 0.0.0.0

集群安全认证

requirepass redis-pass  # master上启用安全认证

读写分离架构的测试

  • 先启动主节点,eshop-cache01上的redis实例
  • 再启动从节点,eshop-cache02上的redis实例

使用命令查看各个节点状态

redis-cli -a redis-pass
info replication

3. 快速压测

redis-3.2.8/src

./redis-benchmark -h eshop-cache01

-c <clients>       Number of parallel connections (default 50)
-n <requests>      Total number of requests (default 100000)
-d <size>          Data size of SET/GET value in bytes (default 2)

根据你自己的高峰期的访问量,在高峰期,瞬时最大用户量会达到10万+,-c 100000,-n 10000000,-d 50

三、哨兵配置

1. 解决异步复制和脑裂导致的数据丢失

min-slaves-to-write 1 # 从服务器的数量少于1个,或者小于1个从服务器的延迟(lag)值都小于等于10秒时
min-slaves-max-lag 10 # 允许丢失多长时间的数据量
  • 要求至少有一个slave数据复制和同步的延迟不能超过10秒

  • 如果说一旦所有的slaves,数据复制和同步的延迟都超过了10秒钟,或者当前连接的slave数少于1,那么这个时候,master将会变成只读

上面两个配置可以减少异步复制和脑裂导致的数据丢失

(1)减少异步复制的数据丢失

有了min-slaves-max-lag这个配置,就可以确保说,一旦slave复制数据和ack延时太长,就认为可能master宕机后损失的数据太多了,那么就拒绝写请求,这样可以把master宕机时由于部分数据未同步到slave导致的数据丢失降低的可控范围内

(2)减少脑裂的数据丢失

如果一个master出现了脑裂,跟其他slave丢了连接,那么上面两个配置可以确保说,如果不能继续给指定数量的slave发送数据,而且slave超过10秒没有给自己ack消息,那么就直接拒绝客户端的写请求

这样脑裂后的旧master就不会接受client的新数据,也就避免了数据丢失

上面的配置就确保了,如果跟所有的slave丢了连接,在10秒后发现没有slave给自己ack,那么就拒绝新的写请求

因此在脑裂场景下,最多就丢失10秒的数据

2. 配置经典三节点哨兵

哨兵的配置文件在sentinel.conf

  • 每一个哨兵都可以去监控多个maser-slaves的主从架构

  • 因为可能你的公司里,为不同的项目,部署了多个master-slaves的redis主从集群

  • 相同的一套哨兵集群,就可以去监控不同的多个redis主从集群

  • 你自己给每个redis主从集群分配一个逻辑的名称

类似这种配置,来指定对一个master的监控,给监控的master指定的一个名称,因为后面分布式集群架构里会讲解,可以配置多个master做数据拆分

核心配置

sentinel down-after-milliseconds mymaster 60000 # 哨兵主管认为的宕机时间(60s)
sentinel failover-timeout mymaster 180000 # 一台机器故障转移超时时间(180s)
sentinel parallel-syncs mymaster 1 # 故障转移每次转移几台机器

上面的三个配置,都是针对某个监控的master配置的,给其指定上面分配的名称即可

上面这段配置,就监控了两个master node

这是最小的哨兵配置,如果发生了master-slave故障转移,或者新的哨兵进程加入哨兵集群,那么哨兵会自动更新自己的配置文件

sentinel monitor master-group-name hostname port quorum

quorum的解释如下

  1. 至少多少个哨兵要一致同意,master进程挂掉了,或者slave进程挂掉了,或者要启动一个故障转移操作
  2. quorum是用来识别故障的,真正执行故障转移的时候,还是要在哨兵集群执行选举,选举一个哨兵进程出来执行故障转移操作
  3. 假设有5个哨兵,quorum设置了2,那么如果5个哨兵中的2个都认为master挂掉了; 2个哨兵中的一个就会做一个选举,选举一个哨兵出来,执行故障转移; 如果5个哨兵中有3个哨兵都是运行的,那么故障转移就会被允许执行

down-after-milliseconds

  • 超过多少毫秒跟一个redis实例断了连接,哨兵就可能认为这个redis实例挂了

parallel-syncs

  • 新的master别切换之后,同时有多少个slave被切换到去连接新master,重新做同步,数字越低,花费的时间越多

  • 假设你的redis是1个master,4个slave

  • 然后master宕机了,4个slave中有1个切换成了master,剩下3个slave就要挂到新的master上面去

  • 这个时候,如果parallel-syncs是1,那么3个slave,一个一个地挂接到新的master上面去,1个挂接完,而且从新的master sync完数据之后,再挂接下一个

  • 如果parallel-syncs是3,那么一次性就会把所有slave挂接到新的master上去

failover-timeout

  • 执行故障转移的timeout超时时长

配置sentinal

mkdir /etc/sentinal
mkdir -p /var/sentinal/5000
mkdir -p /var/log/sentinal/5000
cp /var/sentinal/5000/26379.log  /var/log/sentinal/5000/5000.log
cp /usr/local/redis/redis-3.2.8/sentinel.conf /etc/sentinal/5000.conf
vi /etc/sentinal/5000.conf

详细配置

port 5000
bind 0.0.0.0
dir /var/sentinal/5000
sentinel monitor mymaster 192.168.31.187 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1
daemonize yes  # 配置成后台进程
logfile /var/log/sentinal/5000/5000.log
sentinel auth-pass mymaster redis-pass # 配置主节点的密码

启动哨兵

redis-sentinel /etc/sentinal/5000.conf

连接哨兵查看状态

redis-cli -h 127.0.0.1 -p 5000 
info sentinel # 展示基本信息

日志里会显示出来,每个哨兵都能去监控到对应的redis master,并能够自动发现对应的slave

哨兵之间,互相会自动进行发现,用的就是之前说的pub/sub,消息发布和订阅channel消息系统和机制

3. 常用命令

增加sentinal,会自动发现

删除sentinal的步骤

  • 停止sentinal进程
SENTINEL RESET * # 在所有sentinal上执行,清理所有的master状态

slave的永久下线

 SENTINEL RESET mymaster # 让master摘除某个已经下线的slave

四、Redis-cluster集群配置

1. redis cluster的重要配置

  • cluster-enabled <yes/no>:开启集群

  • cluster-config-file <filename>:这是指定一个文件,供cluster模式下的redis实例将集群状态保存在那里,包括集群中其他机器的信息,比如节点的上线和下限,故障转移,不是我们去维护的,给它指定一个文件,让redis自己去维护的

  • cluster-node-timeout <milliseconds>:节点存活超时时长,超过一定时长,认为节点宕机,master宕机的话就会触发主备切换,slave宕机就不会提供服务

2. 编写配置文件

port 7001
cluster-enabled yes
cluster-config-file /etc/redis-cluster/node-7001.conf
cluster-node-timeout 15000
daemonize	yes							
pidfile		/var/run/redis_7001.pid 						
dir 		/var/redis/7001		
logfile /var/log/redis/7001.log
bind 192.168.31.187		
appendonly yes

3. 准备环境

准备启动脚本并启动

mkdir -p /etc/redis-cluster
mkdir -p /var/log/redis
mkdir -p /var/redis/7001
mkdir -p /var/redis/7002
cd /etc/init.d
cp redis_6379 7001_redis
cp redis_6379 7002_redis
vi 7001_redis
vi 7002_redis

./7002_redis start
./7002_redis start

4. 创建集群

wget https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.1.tar.gz
tar -zxvf ruby-2.3.1.tar.gz
./configure -prefix=/usr/local/ruby
make && make install
cd /usr/local/ruby/ruby-2.3.1
cp bin/ruby /usr/local/bin
cp bin/gem /usr/local/bin

wget http://rubygems.org/downloads/redis-3.3.0.gem
gem install -l ./redis-3.3.0.gem
gem list --check redis gem

cp /usr/local/redis-3.2.8/src/redis-trib.rb /usr/local/bin

redis-trib.rb create --replicas 1 192.168.31.187:7001 192.168.31.187:7002 192.168.31.19:7003 192.168.31.19:7004 192.168.31.227:7005 192.168.31.227:7006


redis-trib.rb check 192.168.31.187:7001

5. 添加删除节点

添加

redis-trib.rb add-node 192.168.31.227:7007 192.168.31.187:7001
redis-trib.rb reshard 192.168.31.187:7001

# 迁移slot到一个node的id

删除

先用resharding将数据都移除到其他节点,确保node为空之后,才能执行remove操作

redis-trib.rb del-node 192.168.31.187:7001 bd5a40a6ddccbd46a0f4a2208eb25d2453c2a8db