小码今天去面试。
面试官:给我介绍一下Redis
集群,
小码:啊,平时开发用的都是单机Redis
,没怎么用过集群了。
面试官:好的,出门右转不谢。
小码内心困惑:在小公司业务量也不大,单机的 Redis
完全够用,也不会发生宕机问题啊。面试要问到 Redis 集群该怎么办呢?
很多小伙伴也有类似的困惑,自己的公司并不大。并发量、访问量要求不高,使用单机 Redis 就能很好的解决数据请求的问题。为啥要使用集群呢?
对于访问量小、并发低的系统,对数据的高可用要求不高的数据,单机部署都能满足需求。但是在大公司,随便一个系统的QPS
都是成千上万,对系统的高可用、高并发要求比较高,这个时候就需要要使用Redis
集群模式了,Redis
有三种集群模式:
Redis
想要不丢失数据,就需要开始持久化,数据会写入到磁盘中,这样即使服务关闭再重启服务器后,数据也能从硬盘加载到内存中。但是如果服务器的硬盘出现故障,也会导致数据丢失。
为了避免硬盘故障问题,需要将数据复制的多个副本上,这样即使一个服务出现故障,其他服务器也能提供数据服务。
Redis
提供了主从模式,一个主数据库master
绑定多个从数据库slave
:
主数据可以读和写,从数据库一般是只读,主从库之间采用读写分离,主数据库数据更新后同步复制给绑定的从数据库,主从数据库的数据保持一致:
SYNC
命令。BGSAVE
命令生成RDB
文件并使用缓冲区记录后面执行的所有写命令。BGSAVE
命令之后,向所有从数据库发送RDB
文件。从数据库加载RDB
文件。
SYNC
每次从服务重启,都会请求所以的数据。如果服务宕机再重启还是同步所有的数据,就会造成资源的浪费,所以有了PSYNC
命令,PSYNC
有完整同步和部分同步,其中完整同步和SYNC
一致,而部分同步是根据数据偏移量复制数据。
Redis
单机搭建可以查看前面写的的教程
首先创建三个文件夹6380、6381、6382
:
mkdir 6380
mkdir 6381
mkdir 6382
复制redis.conf
到这三个文件夹里:
cp redis.conf 6380/
cp redis.conf 6381/
cp redis.conf 6382/
配置一主两从,6380
为主,6381、6382
为从。然后修改redis.conf
文件:
参数 | maser (6380) | slave1 (6381) | slave2 (6382) |
---|---|---|---|
port | 6380 | 6381 | 6382 |
requirepass | requirepass “xxxx” | requirepass “xxxx” | requirepass “xxxx” |
slaveof | slaveof 本机ip 6380 | slaveof 本机ip 6380 | |
masterauth | masterauth ”xxx“ | masterauth ”xxx“ | |
pidfile | pidfile /redis_6380.pid | pidfile /redis_6381.pid | pidfile /redis_6382.pid |
logfile | logfile “redis_6380.log” | logfile “redis_6381.log” | logfile “redis_6382.log” |
设置了requirepass
,就需要设置masterauth
,三台服务器的密码需要一致。
启动服务器:
[root@instance-3 redis]# bin/redis-server 6380/redis.conf
[root@instance-3 redis]# bin/redis-server 6381/redis.conf
[root@instance-3 redis]# bin/redis-server 6382/redis.conf
然后查看进程,如果有以下的显示,说明启动成功了:
[root@instance-3 redis]# ps -ef |grep redis
root 6652 1 0 16:28 ? 00:00:00 bin/redis-server *:6380
root 6665 1 0 16:28 ? 00:00:00 bin/redis-server *:6381
root 6682 1 0 16:28 ? 00:00:00 bin/redis-server *:6382
root 7188 4291 0 16:30 pts/0 00:00:00 grep --color=auto redis
进入Redis
客户端,使用info replication
命令查看数据库的信息。
master 6380:
[root@instance-3 redis]# bin/redis-cli -p 6380
127.0.0.1:6380> auth xxxx
OK
127.0.0.1:6380> info replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6381,state=online,offset=42,lag=0
slave1:ip=127.0.0.1,port=6382,state=online,offset=42,lag=1
master_replid:19ca382e3c05014988002a295078687dae9bb92e
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:42
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:42
role:master
表示 6380 是主服务器,slave0
和salve1
表示绑定的从服务器。
slave 6381:
role:slave
master_host:127.0.0.1
master_port:6380
master_link_status:up
master_last_io_seconds_ago:4
master_sync_in_progress:0
slave_repl_offset:126
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:19ca382e3c05014988002a295078687dae9bb92e
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:126
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:126
role:slave
表示 6381 是从服务器,master_host
和master_port
表示绑定对应的主服务器。
slave 6382:
role:slave
master_host:127.0.0.1
master_port:6380
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:476
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:19ca382e3c05014988002a295078687dae9bb92e
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:476
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:476
role:slave
表示 6382 是从服务器,master_host
和master_port
表示绑定对应的主服务器。
主服务器添加数据,再从从服务器获取数据。
6380
服务器添加数据:
127.0.0.1:6380> set name jeremy
OK
6381
服务器获取数据:
127.0.0.1:6381> get name
"jeremy"
经过以上测试,说明主服务器的数据,从服务器也能同步获取。主从服务都搭建成功。
优点
缺点
Spring Boot
也不支持集成Redis
主从模式。主从模式最大的问题就是无法处理主数据库宕机问题,也就无法保证
Redis
的高可用性。这就需要有一个自动的机制处理主数据库宕机问题,这就延伸出下面的模式 —— 哨兵模式。
当主数据库挂了之后,需要手动设置新的主数据库,其他从数据库都需要重新设置新的主数据。手动切换的成本比较大,还会导致一段时间的服务不可用。这就需要讲上面的手动设置改成自动设置,也就是使用哨兵来配置。
哨兵Redis
的高可用解决方案,哨兵监控Redis
主服务器和绑定的从服务器,如果主服务器宕机了,自动将某个从服务器升级为新的服务器,然后发送通知给其他从服务器。
哨兵是一个独立的进程,和Redis一样,它也运行一个实例。主要有三个任务:
PING
命令,检查主从数据库运行是否正常,在设置down-after-milliseconds
毫秒,没有服务响应,就会标记主观下线,当其他哨兵也判断主观下线,判断主观下线的数量达到设置的值后,哨兵之间会进行投票,投票同意后,进行数据库升级。redis.conf
配置文件。replicaof
命令,和新数据库建立连接,并进行数据复制。在上面的主从模式的基础上添加哨兵,首先从解压文件夹复制sentinel.conf
到usr/local/redis
文件夹中:
cp sentinel.conf /usr/local/redis/
修改sentinel.conf
文件,需要修改的部分:
daemonize no
改成 daemonize yes
logfile ""
改成 logfile "redis_26379.log"
sentinel monitor
,设置成 sentinel monitor mymaster 127.0.0.1 6382 1
ip
主数据库IPredis-port
主数据库端口quorum
主从切换需要达到主动下线个数sentinel auth-pass mymaster 123456
,表示验证密码
mymaster
哨兵的名称,需要唯一123456
数据库密码,所有主从数据库密码需要设置成一致。启动服务器:
[root@instance-3 redis]# bin/redis-sentinel sentinel.conf
查看logfile
启动日志:
看最后标记的三行,表明哨兵分别监控了主数据库
6380
、两个从数据库6381
、6382
。有上面的日志输出表明哨兵已经成功启动。
使用SHUTDOWN
命令关闭6380
主数据库服务:
[root@instance-3 redis]# bin/redis-cli -p 6380
127.0.0.1:6380> shutdown
(error) NOAUTH Authentication required.
127.0.0.1:6380> a
[root@instance-3 redis]# bin/redis-cli -p 6380
127.0.0.1:6380> auth xxx
OK
127.0.0.1:6380> SHUTDOWN
通过ps -ef |grep redis
查看6380
已经关闭:
[root@instance-3 redis]# ps -ef |grep redis
root 8822 1 0 Nov21 ? 00:00:58 /usr/local/redis/bin/redis-server *:6379
root 24707 1 0 10:35 ? 00:00:02 bin/redis-server *:6381
root 27500 1 0 10:47 ? 00:00:01 bin/redis-server *:6382
root 29247 1 0 10:54 ? 00:00:03 bin/redis-sentinel *:26379 [sentinel]
root 34131 17210 0 11:16 pts/1 00:00:00 grep --color=auto redis
查看哨兵日志logfile
:
表明主服务器从
6380
成功切换到了6382
,sentinel.conf
配置文件也修改了主从数据库配置。如果没有切换成功,日志报错-failover-abort-no-good-slave
,可能是没有设置验证密码sentinel auth-pass
。
主从模式和哨兵模式数据库都存储了相同的数据,比较浪费内存。而且当数据量增加时,在单个数据库上很难实现在线扩容。Redis Cluster将数据分布存储在不同的节点上,每个节点存储不同的数据。添加节点就能解决扩容问题。
Redis Cluster
翻译就是Redis
集群,Redis
集群提供分布式解决方案,通过分片将数据拆分到不同的节点上,并提供复制和故障转移功能。使用了水平扩展的方法,将数据分发到不同的数据库中。
每个虚线圆都表示一个节点,每个节点都有一个主数据库和多个从数据库。任意两个节点都是相同的(三个节点画图容易误以为是一个环,四个节点容易理解),节点之间都共享数据。
Redis
分片集群,使用了一种类似于一致性哈希的分片技术——哈希槽,每个键都有一个哈希槽的值,Redis 集群有16384
个哈希嘈,对键的CRC16
取模16384
计算出哈希槽,以此决定放置的哈希嘈的位置。
Redis
集群中每个节点都负责一部分哈希嘈,比如,集群有三个节点,其中:
数据根据哈希嘈分配到不同的数据库中,实现数据的分片。这里添加或者减少一个节点就比较容易了。比如,我想添加一个新的节点D
,需要将节点A、B、C
一部分数据移动到节点D
中。而删除一个节点A
,就将原来A节点
的数据分发到其它节点上。
为了保证高可用,Redis Cluster
也使用了主从模式。节点(上图虚线圆)宕机了,就无法提供继续数据服务了。当节点引入主从模式后,主服务宕机之后,从服务器升级成主服务。但是如果一个节点的所有主从服务服务都宕机了,该节点就无法提供数据服务了。
最小集群必须最少包含三个节点,这里部署使用三个主节点,三个从节点。一共有六个配置文件,端口分别是7001、7002、7003、7004、7005、7006
。
复制redis.conf
配置文件命名redis_7001.conf
,修改以下字段:
# 端口
port 7001
# 启用集群模式
cluster-enabled yes
# 保存其他节点的名称、状态等信息,命名和端口保持一致
cluster-config-file nodes_7001.conf
logfile "redis_7001.log"
daemonize yes
protected-mode no
其他五个文件分别复制redis_7001.conf
文件,文件名分别是:
redis_7002.conf
redis_7003.conf
redis_7004.conf
redis_7005.conf
redis_7006.conf
根据文件名修改修改port
、cluster-config-file
、logfile
三个属性,比如redis_7002.conf
的配置修改以下字段:
port 7001
cluster-config-file nodes_7002.conf
logfile "redis_7002.log"
其他配置文件也修改成对应文件名的字段。
启动redis节点:
bin/redis-server redis_7001.conf &
bin/redis-server redis_7002.conf &
bin/redis-server redis_7003.conf &
bin/redis-server redis_7004.conf &
bin/redis-server redis_7005.conf &
bin/redis-server redis_7006.conf
然后查看redis进程:
[root@localhost redis]# ps -ef|grep redis
root 24783 1 0 Nov15 ? 00:07:53 bin/redis-server 0.0.0.0:7001 [cluster]
root 24792 1 0 Nov15 ? 00:07:50 bin/redis-server 0.0.0.0:7002 [cluster]
root 24805 1 0 Nov15 ? 00:07:53 bin/redis-server 0.0.0.0:7003 [cluster]
root 24816 1 0 Nov15 ? 00:07:49 bin/redis-server 0.0.0.0:7004 [cluster]
root 24821 1 0 Nov15 ? 00:07:53 bin/redis-server 0.0.0.0:7005 [cluster]
root 24830 1 0 Nov15 ? 00:07:50 bin/redis-server 0.0.0.0:7006 [cluster]
--cluster-replicas 1
参数表示创建一个主节点同时也创建一个从节点。
创建redis集群:
redis-cli --cluster 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006 --cluster-replicas 1
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 127.0.0.1:7004 to 127.0.0.1:7001
Adding replica 127.0.0.1:7005 to 127.0.0.1:7002
Adding replica 127.0.0.1:7006 to 127.0.0.1:7003
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 9f8616c497aeb89e065c9ed7e260a13a499078eb 127.0.0.1:7001
slots:[0-5460] (5461 slots) master
M: 1064be46f6001390b47308fcb90832cb5eff3256 127.0.0.1:7002
slots:[5461-10922] (5462 slots) master
M: c862b3f74904891972debe055edee66d08563f6c 127.0.0.1:7003
slots:[10923-16383] (5461 slots) master
S: 51fa3d61cd6075d8a179ec5402c3d6771592d524 127.0.0.1:7004
replicates c862b3f74904891972debe055edee66d08563f6c
S: f2a18a3fd5f7097888f31cbbc3878f26699ecd09 127.0.0.1:7005
replicates 9f8616c497aeb89e065c9ed7e260a13a499078eb
S: 004d9acf71c448d93c8b3211f1fd132dd47cd5e9 127.0.0.1:7006
replicates 1064be46f6001390b47308fcb90832cb5eff3256
Can I set the above configuration? (type 'yes' to accept):
可以看到启动六个节点,三个主节点
Master
,三个从节点Slave
,以及他们之间的主从关系。六个节点,每个节点都生成一个唯一的编码。
输入yes
最后有以下输出,表示集群搭建成功:
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
登录客户端需要带上参数-c
表示集群环境,否则只能获取单个节点的数据。
先在7001
添加数据
bin/redis-cli -p 7001
redis 127.0.0.1:7001> set name jeremy
OK
然后在7002
获取数据:
bin/redis-cli -p 7002
redis 127.0.0.1:7002> get name
-> Redirected to slot [5798] located at 127.0.0.1:7001
OK
127.0.0.1:7001> get name
"jeremy"
在
7002
获取数据,redis集群会根据key计算哈希槽的位置,算出数据在7001
节点,重定向到7001
节点获取数据。
添加一个新节点,一般是添加一个空节点,将其他节点数据移动该节点数据库中。实现数据库的扩容。
bin/redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7001
将新节点地址为第一个参数,集群中随机一个节点地址为第二个参数。上面的命令的表示将新节点127.0.0.1:7006
添加到127.0.0.1:7001
所在的集群中。
bin/redis-cli --cluster del-node 127.0.0.1:7001
SYNC
同步命令给主数据库,主数据库接收到命令之后,生成RDB
文件。并且使用缓冲区记录所有写命令。写完毕后发送RDB
文件给每个从数据库解析,以及发送缓存写命名给所以从数据库执行。Cluster
模式
Redis
集群有16384
个哈希嘈,对键的CRC16
取模16384
计算出哈希槽。