Redis集群技术演变

按照需求来分,将实例区分为单机和集群两种类型。

  • 单机适合容量和性能要求不高的小型存储
  • 集群应对性能和容量要求较高的场景

单机

原生主从(master-slave)

实现高可用,暴露master节点。支持Redis所有指令。
使用Redis 自带的哨兵(Sentinel)集群对实例进行状态监控与 Failover。Sentinel 是 Redis 自带的高可用组件,将 Redis 注册到由多个 Sentinel 组成的 Sentinel 集群后,Sentinel 会对 Redis 实例进行健康检查,当 Redis 发生故障后,Sentinel 会通过 Gossip 协议进行故障检测,确认宕机后会通过一个简化的 Raft 协议来提升 Slave 成为新的 Master。

通常情况仅使用1 个 Slave 节点进行冷备,如果有读写分离请求,可以建立多个Read only slave 来进行读写分离。

  • 只读Slave 节点可以按照需求设置 slave-priority 参数为0,防止故障切换时选择了只读节点而不是热备 Slave 节点;
  • Sentinel 进行故障切换后会执行 CONFIG REWRITE 命令将SLAVEOF 配置落地,如果 Redis 配置中禁用了 CONFIG 命令,切换时会发生错误,可以通过修改 Sentinel 代码来替换 CONFIG 命令;
  • Sentinel Group 监控的节点不宜过多,实测超过 500 个切换过程偶尔会进入 TILT 模式,导致Sentinel 工作不正常,推荐部署多个 Sentinel 集群并保证每个集群监控的实例数量小于 300 个;
  • Master 节点应与 Slave 节点跨机器部署,有能力的使用方可以跨机架部署,不推荐跨机房部署 Redis 主从实例;
  • Sentinel 切换功能主要依赖 down-after-milliseconds 和failover-timeout 两个参数,down-after-milliseconds 决定了Sentinel 判断 Redis 节点宕机的超时,知乎使用 30000 作为阈值。而 failover-timeout 则决定了两次切换之间的最短等待时间,如果对于切换成功率要求较高,可以适当缩短failover-timeout 到秒级保证切换成功,具体详见Redis 官方文档[2];
  • 单机网络故障等同于机器宕机,但如果机房全网发生大规模故障会造成主从多次切换,此时资源发现服务可能更新不够及时,需要人工介入。

Sentinel

Redis 的 Sentinel 系统用于管理多个 Redis 服务器(instance), 该系统执行以下三个任务:

  • 监控(Monitoring): Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。
  • 提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
  • 自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。

优点:

  • 基于主从模式,所有主从的优点,sentinel都有
  • 主从可以切换,故障可以转移,系统可用性好
  • 是主从模式的升级,系统更健壮,可用性更高

缺点:

  • 较难支持在线扩容
  • 在集群容量达到上限时只能升级机器内存,而不能实现水平扩容

主从同步可能会遇到的问题:

  • 端口不通
  • 认证失败
角色 IP 端口 其他
Master,Sentinel1 192.168.1.233 6379,26379
slave01,Sentinel2 192.168.1.234 6379,26379
slave02,Sentinel3 192.168.1.235 6379,26379

配置redis文件

  • 在3台服务器上分别安装Redis。需要注意的是,如果要给Redis设置密码,需要在3个Redis的配置文件中设置相同的密码。
1
2
3
4
#master
requirepass "Redis-Password"
#slave
masterauth "Redis-Password"
  • 在2个SlaveRedis的配置文件中声明所从属的主数据库。slaveof 192.168.1.233 6379
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
daemonize                   yes    
pidfile /var/run/redis.pid
port 6379
bind 192.168.1.233
timeout 120
tcp-keepalive 60
loglevel notice
logfile /var/log/redis/redis.log
syslog-enabled no
databases 16
stop-writes-on-bgsave-error no
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir /var/lib/redis
save 7200 100000
maxclients 10000
maxmemory 1GB
maxmemory-policy volatile-lru
maxmemory-samples 5
appendonly no
appendfilename appendonly.aof
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-entries 512
list-max-ziplist-value 64
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 256mb 64b 120
hz 10
requirepass "alkq2caiAmq82s"
masterauth "alkq2caiAmq82s"
rename-command flushall ""
rename-command flushdb ""

三台机器启动后检查主从状态,数量和状态对的上就没啥问题了。

配置sentinel

注意加上认证部分。
配置项解释:

  • sentinel monitor mymaster 192.168.1.233 6379 2给master取名叫mymaster,2代表集群中有两个sentinel认为master死了的时候,才能真正认为master死了。sentinel集群中通过gossip协互相通信。
  • down-after-milliseconds sentinel会向master发送心跳PING来确认master是否存活,如果master在“一定时间范围”内不回应PONG 或者是回复了一个错误消息,那么这个sentinel会主观地(单方面地)认为这个master已经不可用了(subjectively down, 也简称为SDOWN)。而这个down-after-milliseconds就是用来指定这个“一定时间范围”的,单位是毫秒。
  • parallel-syncs不过需要注意的是,这个时候sentinel并不会马上进行failover主备切换,这个sentinel还需要参考sentinel集群中其他sentinel的意见,如果超过某个数量的sentinel也主观地认为该master死了,那么这个master就会被客观地(注意哦,这次不是主观,是客观,与刚才的subjectively down相对,这次是objectively down,简称为ODOWN)认为已经死了。需要一起做出决定的sentinel数量在上一条配置中进行配置。
  • 在发生failover主备切换时,这个选项指定了最多可以有多少个slave同时对新的master进行同步,这个数字越小,完成failover所需的时间就越长,但是如果这个数字越大,就意味着越多的slave因为replication而不可用。可以通过将这个值设为 1 来保证每次只有一个slave处于不能处理命令请求的状态。
配置文件
1
2
3
4
5
6
7
8
9
10
port 26379
daemonize yes
bind 192.168.1.233
logfile "/var/log/redis/sentinel.log"
dir "/var/lib/redis"
sentinel monitor mymaster 192.168.1.233 6379 2
sentinel auth-pass mymaster alkq2caiAmq82s
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
1
2
3
4
5
6
7
8
9
10
port 26379
daemonize yes
bind 192.168.1.234
logfile "/var/log/redis/sentinel.log"
dir "/var/lib/redis"
sentinel monitor mymaster 192.168.1.233 6379 2
sentinel auth-pass mymaster alkq2caiAmq82s
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
1
2
3
4
5
6
7
8
9
10
port 26379
daemonize yes
bind 192.168.1.235
logfile "/var/log/redis/sentinel.log"
dir "/var/lib/redis"
sentinel monitor mymaster 192.168.1.233 6379 2
sentinel auth-pass mymaster alkq2caiAmq82s
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
启动

然后依次启动,之后检查状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
redis-sentinel sentinel.conf

or

redis-server sentinel.conf --sentinel

[root@base_233 ~]# redis-cli -h 192.168.1.233 -p 26379
192.168.1.233:26379> info Sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=192.168.1.233:6379,slaves=2,sentinels=3

检查配置文件的变化


发现slave机器上会自动增加其他两个节点的信息。

启动日志

基本命令

  • PING :返回 PONG 。
  • SENTINEL masters :列出所有被监视的主服务器,以及这些主服务器的当前状态。
  • SENTINEL slaves :列出给定主服务器的所有从服务器,以及这些从服务器的当前状态。
  • SENTINEL get-master-addr-by-name : 返回给定名字的主服务器的 IP 地址和端口号。 如果这个主服务器正在执行故障转移操作, 或者针对这个主服务器的故障转移操作已经完成, 那么这个命令返回新的主服务器的 IP 地址和端口号。
  • SENTINEL reset : 重置所有名字和给定模式 pattern 相匹配的主服务器。 pattern 参数是一个 Glob 风格的模式。 重置操作清楚主服务器目前的所有状态, 包括正在执行中的故障转移, 并移除目前已经发现和关联的, 主服务器的所有从服务器和 Sentinel 。
  • SENTINEL failover : 当主服务器失效时, 在不询问其他 Sentinel 意见的情况下, 强制开始一次自动故障迁移 (不过发起故障转移的 Sentinel 会向其他 Sentinel 发送一个新的配置,其他 Sentinel 会根据这个配置进行相应的更新)。

故障转移

一次故障转移操作由以下步骤组成:

  • 发现主服务器已经进入客观下线状态。
  • 对我们的当前纪元进行自增(详情请参考 Raft leader election ), 并尝试在这个纪元中当选。
  • 如果当选失败, 那么在设定的故障迁移超时时间的两倍之后, 重新尝试当选。 如果当选成功, 那么执行以下步骤。
  • 选出一个从服务器,并将它升级为主服务器。
  • 向被选中的从服务器发送 SLAVEOF NO ONE 命令,让它转变为主服务器。
  • 通过发布与订阅功能, 将更新后的配置传播给所有其他 Sentinel , 其他 Sentinel 对它们自己的配置进行更新。
  • 向已下线主服务器的从服务器发送 SLAVEOF 命令, 让它们去复制新的主服务器。
  • 当所有从服务器都已经开始复制新的主服务器时, 领头 Sentinel 终止这次故障迁移操作。

每当一个 Redis 实例被重新配置(reconfigured) —— 无论是被设置成主服务器、从服务器、又或者被设置成其他主服务器的从服务器 —— Sentinel 都会向被重新配置的实例发送一个 CONFIG REWRITE 命令, 从而确保这些配置会持久化在硬盘里。

日志说明:

  • +sdown 表示哨兵主观认为数据库下线
  • +odown 表示哨兵客观认为数据库下线
  • +try-failover 表示哨兵开始进行故障恢复
  • +failover-end 表示哨兵完成故障修复,其中包括了领头哨兵的选举、备选从数据库的选择等等较为复杂的过程
  • +switch-master表示主数据库从51服务器迁移到52服务器
  • +slave列出了新的主数据库的2个从数据库,而哨兵并没有彻底清除51服务器的实力信息,这是因为停止的实例有可能会在将来恢复,哨兵会让其重新加入进来

故障转移过程:

  • 观察slave节点日志:master故障了
    • 发现master故障,
    • 选举出来233为master
    • 切换master
    • 更新sentinel配置文件
    • 将原来的master切换为slave,并且定义为down
  • 观察sentinel配置文件,master已经切换为新的地址

之前一直不成功主要的原因就是配置了rename-command "RENAMECONFIG",导致失败的,这个要多注意一下。

常见问题

  • slave不能写

访问sentinel集群

以Python为例,程序通过sentinel提供的端口进行访问,获取master地址进行写操作,读操作默认是轮询。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Python 2.6.6 (r266:84292, Jan 22 2014, 09:42:36) 
[GCC 4.4.7 20120313 (Red Hat 4.4.7-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from redis.sentinel import Sentinel #加载redis模块
>>> sentinel = Sentinel([('192.168.221.160', 26379),
... ('192.168.221.161', 26379)],
... socket_timeout=0.1) #连接哨兵服务器
>>> sentinel.discover_master('mymaster') #获取主redis服务器地址
('192.168.221.161', 6379)
>>> sentinel.discover_slaves('mymaster')#获取从redis服务区地址
[('192.168.221.160', 6379)]
>>> master = sentinel.master_for('mymaster', socket_timeout=0.1)
>>> master.set('foo','bar') #获取主redis服务器并进行写入
True
>>> slave = sentinel.slave_for('mymaster', socket_timeout=0.1)
>>> slave.get('foo')#获取从redis服务器进行获取
'bar'
>>>

集群(Cluster)

cluster的出现是为了解决单机Redis容量有限的问题,将Redis的数据根据一定的规则分配到多台机器。对cluster的一些理解:

  • cluster可以说是sentinel和主从模式的结合体,通过cluster可以实现主从和master重选功能,所以如果配置两个副本三个分片的话,就需要六个Redis实例。
  • 因为Redis的数据是根据一定规则分配到cluster的不同机器的,当数据量过大时,可以新增机器进行扩容。这种模式适合数据量巨大的缓存要求,当数据量不是很大使用sentinel即可。

简介

Redis集群是一个可以在多个Redis节点之间进行数据共享的设施。

Redis集群不支持需要同时处理多个键的命令,因为在执行这些命令的时候需要在多个Redis节点之间移动数据,会降低集群性能。
Redis集群通过分区来提供一定程度的可用性:即使集群中有一部分节点失效,集群也可以继续处理命令请求。

优点:

  • 将数据自动切分到多个节点的能力
  • 当部分节点失效的时候,集群仍然可以继续处理命令请求

数据共享

一个集群包含16384个哈希槽,数据库中的每个键都属于这16384个哈希槽的其中一个。
每个节点负责处理一部分哈希槽,比如一个集群有三个节点,就均分为3大块哈希槽。

这种哈希槽的处理方式使用处更容易或者更快的添加删除节点。比如

  • 增加节点。ABC只需要将部分哈希槽移动到新的D就可以了
  • 删除节点,ABCD只需要将要删除的D节点的哈希槽移动到ABC就可以了

集群中的主从复制

集群中的主从复制功能:集群中的每个节点都有N个复制品,其中一个复制品为主节点,其余的为从节点。
比如之前的B节点下线了,那么集群中会丢失部分哈希槽。但是如果创建集群的时候为B节点添加了从节点B1,那么在B下线的时候,B1就会为新的主节点,取代处理之前对应的哈希槽。这样集群就会正常运作了。

集群一致性保证

集群不保证数据的强一致性,在特定条件下,集群可能会丢失已经被执行过的写命令。

使用异步复制是丢失写命令的一个原因,示例如下:

  • 客户端向主节点B发送写的命令
  • 主节点B执行写命令,并向客户端放回命令回复
  • 主机点B

部署

配置文件

创建三个目录,然后依次修改端口,之后发送到其他节点机器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
daemonize                   yes    
pidfile /var/run/redis7000.pid
port 6379
bind 192.168.1.233
timeout 120
tcp-keepalive 60
loglevel notice
logfile /var/log/redis/redis7000.log
syslog-enabled no
databases 16
stop-writes-on-bgsave-error no
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir /usr/local/redis_cluster/7000
save 7200 100000
maxclients 10000
maxmemory 1GB
maxmemory-policy volatile-lru
maxmemory-samples 5
appendonly no
appendfilename appendonly.aof
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-entries 512
list-max-ziplist-value 64
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 256mb 64b 120
hz 10

cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000

启动命令

1
2
3
7000/bin/redis-server 7000/cluster.conf
7001/bin/redis-server 7001/cluster.conf
7002/bin/redis-server 7002/cluster.conf

1 创建集群

对于5.0以前的版本:

1
/usr/local/redis_cluster/7000/bin/redis-trib.rb create --replicas 2 192.168.1.233:7000 192.168.1.233:7001 192.168.1.233:7002 192.168.1.234:7000 192.168.1.234:7001 192.168.1.234:7002 192.168.1.235:7000  192.168.1.235:7001 192.168.1.235:7002

对于5.0的版本:

1
/usr/local/redis_cluster/7000/bin/redis-cli --cluster  create --cluster-replicas 2   192.168.1.233:7000 192.168.1.233:7001 192.168.1.233:7002 192.168.1.234:7000 192.168.1.234:7001 192.168.1.234:7002 192.168.1.235:7000  192.168.1.235:7001 192.168.1.235:7002

如果使用了错误的版本命令,会有下面的警告信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@base_233 redis_cluster]# /usr/local/redis_cluster/7000/bin/redis-trib.rb create --replicas 2 192.168.1.233:7000 192.168.1.233:7001 192.168.1.233:7002 192.168.1.234:7000 192.168.1.234:7001 192.168.1.234:7002 192.168.1.235:7000  192.168.1.235:7001 192.168.1.235:7002
WARNING: redis-trib.rb is not longer available!
You should use redis-cli instead.

All commands and features belonging to redis-trib.rb have been moved
to redis-cli.
In order to use them you should call redis-cli with the --cluster
option followed by the subcommand name, arguments and options.

Use the following syntax:
redis-cli --cluster SUBCOMMAND [ARGUMENTS] [OPTIONS]

Example:
redis-cli --cluster create 192.168.1.233:7000 192.168.1.233:7001 192.168.1.233:7002 192.168.1.234:7000 192.168.1.234:7001 192.168.1.234:7002 192.168.1.235:7000 192.168.1.235:7001 192.168.1.235:7002 --cluster-replicas 2

To get help about all subcommands, type:
redis-cli --cluster help

redis 5.0 放弃redis-trib.rb,使用redis-cli --cluster create创建集群。也就是说5.0以后所有的集群操作都是通过redis-cli --cluster 来执行的。

创建集群后会提示是否能更新以上的配置?选择yes

修复集群

1
/usr/local/redis_cluster/7000/bin/redis-cli --cluster check  192.168.1.233:7001

监控

https://github.com/ZhuoRoger/redismon
or
https://github.com/iambocai/falcon-monit-scripts

备份机制

AOF

每秒或者每次写操作,安全,速度不够快,也存在丢失1秒的可能。可以在从机器上开启。

RDB

快照,定时,或者数量改变时,存储到一个二进制文件。容易恢复,文件小,但是存在不完整的可能。

主库开启RDB,每天
从库开启AOF,

如果只配置AOF,重启时加载AOF文件恢复数据;
如果同时 配置了RBD和AOF,启动是只加载AOF文件恢复数据;
如果只配置RBD,启动是讲加载dump文件恢复数据。

参考资料

http://doc.redisfans.com/topic/sentinel.html
sentinel客户端使用参考
sentinel和cluster的对比

------ 本文结束 ------

版权声明

Medivh's Notes by Medivh is licensed under a Creative Commons BY-NC-ND 4.0 International License.
Medivh创作并维护的Medivh's Notes博客采用创作共用保留署名-非商业-禁止演绎4.0国际许可证
本文首发于Medivh 博客( http://www.mknight.cn ),版权所有,侵权必究。