# 029. 对项目的 redis cluster 实验多 master 写入、读写分离、高可用性

上一章节 将 redis cluster 搭建起来了

redis cluster 提供了多个 master,数据可以分布式存储在多个 master 上; 每个 master 都带着 slave,自动就做读写分离; 每个 master 如果故障,那么就会自动将 slave 切换成 master,高可用

redis cluster 的基本功能,来测试一下

# 实验多 master 写入 -> 海量数据的分布式存储

[root@eshop-cache01 ~]# redis-cli -h eshop-cache01 -p 7001
eshop-cache01:7001> set mykey1 v1
OK
eshop-cache01:7001> set mykey2 v2
(error) MOVED 14119 192.168.99.172:7005
eshop-cache01:7001>
1
2
3
4
5
6

先登录其中一台机器,然后写入数据,会发现 mykey2 error 了,这是告诉你需要到 172 上面去执行写入。

[root@eshop-cache02 ~]# redis-cli -h 192.168.99.172 -p 7005
192.168.99.172:7005> set mykey2 v2
OK
192.168.99.172:7005> get mykey2
"v2"
192.168.99.172:7005> get mykey1
(error) MOVED 1860 192.168.99.170:7001
192.168.99.172:7005>
1
2
3
4
5
6
7
8

在提示的上面执行写入数据,能看到成功了,但是读取 myke1 又提示需要到他对应的 master 上去读取

原因是:每个 master 都会计算这个 key 对应的 CRC16 值,然后对 16384个 hashslot 取模,找到 key 对应的 hashslot,找到 hashslot 对应的 master

如果对应的 master 就在自己本地的话,自己就处理掉了

但是如果计算出来的 hashslot 在其他 master 上,那么就会给客户端返回一个 moved error,告诉你,你得到哪个 master 上去执行这条写入的命令

什么叫做多 master 的写入?就是每条数据只能存在于一个 master 上,不同的 master 负责存储不同的数据,这个就是分布式的数据存储

比如:100w 条数据,5 个 master,每个 master 就负责存储 20w 条数据,分布式数据存储

以下内容是讲师独白 + 扩展知识角度

我除了大型的 java 系统架构,还专注在大数据系统架构、分布式、分布式存储 hadoop hdfs、分布式资源调度 hadoop yarn、分布式计算 hadoop mapreduce/hive、分布式的 nosql 数据库 hbase、分布式的协调 zookeeper、分布式通用计算引擎 spark、分布式的实时计算引擎 storm 这些方面。讲解的角度不一样

如果你要处理海量数据,就涉及到了一个名词,叫做大数据,只要涉及到大数据,那么其实就会涉及到分布式

如 redis cluster 就是分布式的

因为我来讲 java 系统的架构,有时候跟其他人不一样,不是纯搞 java,但是我因为工作时间很长,早期专注做 java 架构好多年,大数据兴起,就一直专注大数据系统架构

大数据相关的系统,也涉及很多的 java 系统架构,高并发、高可用、高性能、可扩展、分布式系统等很复杂的架构

所以会给大家稍微拓展一下知识面,从不同的角度去讲解一块知识

redis 高并发、高性能、每日上亿流量的大型电商网站的商品详情页系统的缓存架构,面对这个需求来讲解的,redis 是作为大规模缓存架构中的底层的核心存储的支持

高并发、高性能、每日上亿流量,对 redis 深入讲解一些原理:

  • redis 持久化 -> 灾难的时候,做数据恢复,
  • 复制 -> 读写分离,扩容 slave,支撑更高的读吞吐,redis 怎么支撑读 QPS 超过 10 万,几十万;
  • 哨兵,在 redis 主从,一主多从,怎么保证 99.99% 可用性; redis cluster,海量数据

java 架构课,架构思路和设计是很重要的,但是另外一点,我希望能够带着大家用真正 java 架构师的角度去看待一些技术,而不是仅仅停留在技术的一些细节的点

给大家从一些大数据的角度,去分析一下我们 java 架构领域中的一些技术

天下武功,都出自一脉,研究过各种大数据的系统,比如 redis cluster 讲解了很多原理,它跟 elasticsearch 很多底层的分布式原理,都是类似的

如:redis AOF,fsync

elasticsearch 建立索引的时候,先写内存缓存,每秒钟把数据刷入 os cache,接下来再每隔一定时间 fsync 到磁盘上去

redis cluster,写可以到任意 master,任意 master 计算 key 的 hashslot 以后,告诉 client,重定向,路由到其他 mater 去执行,分布式存储的一个经典的做法

elasticsearch 建立索引的时候,也会根据 doc id/routing value,做路由,路由到某个其他节点,重定向到其他节点去执行

分布式的一些系统,如 hadoop,spark,storm 里面很多核心的思想都是类似的

# 实验不同 master 各自的 slave 读取 -> 读写分离

[root@eshop-cache01 ~]# redis-cli -h 192.168.99.171 -p 7004
192.168.99.171:7004> get mykey2
(error) MOVED 14119 192.168.99.172:7005
192.168.99.171:7004> get mykey1
(error) MOVED 1860 192.168.99.170:7001
192.168.99.171:7004> readonly
OK
192.168.99.171:7004> get mykey2
(error) MOVED 14119 192.168.99.172:7005
192.168.99.171:7004> get mykey1
"v1"
1
2
3
4
5
6
7
8
9
10
11

可以看到在 salve 节点上登录之后,一开始获取数据会报错,但是带上 readonly 指令后,再次获取只要是对应 master 的 salve 就能获取到数据

注意:check 命令查看到的集群信息,一个 M 一个 S,不要以为这个顺序就是互相对应的。

[root@eshop-cache01 ~]# redis-trib.rb check eshop-cache01:7001
>>> Performing Cluster Check (using node eshop-cache01:7001)
M: 3807711e01cd28509d7ba9839e601058bf2a30cf eshop-cache01:7001
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
S: 9247dfb394441619da9da5b75b62b034c3f420e5 192.168.99.172:7006
   slots: (0 slots) slave
   replicates cb2256b653cf5b7b3f9d1478cfa2953cc334c5bf
M: cb2256b653cf5b7b3f9d1478cfa2953cc334c5bf 192.168.99.172:7005
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
S: a174fe6613862db8985f82caac58bde91dfbd664 192.168.99.171:7004
   slots: (0 slots) slave
   replicates 3807711e01cd28509d7ba9839e601058bf2a30cf
M: 0ff0e8ab05a8b032aeacf24e0c7fea77be3f5c55 192.168.99.171:7003
   slots:5461-10922 (5462 slots) master
   1 additional replica(s)
S: b0d66be3b15e117696c50a781ff24a842456733d 192.168.99.170:7002
   slots: (0 slots) slave
   replicates 0ff0e8ab05a8b032aeacf24e0c7fea77be3f5c55
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

可以通过命令 info 命令查看,可以看到 7004 才是 7001 的从节点

192.168.99.171:7004> info
....
# Replication
role:slave
master_host:192.168.99.170
master_port:7001
1
2
3
4
5
6

后面,马上把 redis 架构给讲完之后,就开始讲解业务系统的开发,包括高并发的商品详情页系统的大型的缓存架构,jedis cluster 相关 api 去封装和测试对 redis cluster 的访问

jedis cluster api,就可以自动针对多个 master 进行写入和读取

redis-cli -c 启动,就会自动进行各种底层的重定向的操作

[root@eshop-cache01 ~]# redis-cli -h 192.168.99.171 -p 7004 -c
192.168.99.171:7004> get mykey1
-> Redirected to slot [1860] located at 192.168.99.170:7001
"v1"
192.168.99.170:7001> get mykey2
-> Redirected to slot [14119] located at 192.168.99.172:7005
"v2"
1
2
3
4
5
6
7

实验 redis cluster 的读写分离的时候,会发现有一定的限制性,默认情况下 redis cluster 的核心的理念,主要是用 slave 做高可用的,每个 master 挂一两个 slave,主要是做数据的热备,还有 master 故障时的主备切换,实现高可用的

TIP

redis cluster 默认是不支持 slave 节点读或者写的,跟我们手动基于 replication 搭建的主从架构不一样的

slave node 上 使用 readonly,get,这个时候才能在 slave node 进行读取

redis cluster 主从架构是出来,读写分离,复杂了点也可以做,但是 jedis 客户端,对 redis cluster 的读写分离支持不太好的

默认的话就是读和写都到 master 上去执行的

如果你要让最流行的 jedis 做 redis cluster 的读写分离的访问,那可能还得自己修改一点 jedis 的源码,成本比较高

核心的思路,在 redis cluster 中,就没有所谓的读写分离的概念了

读写分离是为了什么?主要是因为要建立一主多从的架构,才能横向任意扩展 slave node 去支撑更大的读吞吐量

redis cluster 的架构下,实际上本身 master 就是可以任意扩展的,你如果要支撑更大的读吞吐量,或者写吞吐量,或者数据量,都可以直接对 master 进行横向扩展就可以了,也可以实现支撑更高的读吞吐的效果

这个结论不会去跟大家直接讲解的,很多东西都要带着一些疑问,未知,实际经过一些实验和操作之后,让你体会的更加深刻一些

画外音:这个我很赞同,现在我是终于明白了为什么说 redis cluster 读写分离,但是又不是 replication 读写分离,他们的区别很深刻了

redis cluster 主从架构、读写分离,没说错没有撒谎,但是支持不太好,在 server 层面、jedis client 层面也不太好。

所以说扩容 master,跟之前扩容 slave,效果是一样的

# 实验自动故障切换 -> 高可用性

# 查看哪一个是 master
[root@eshop-cache01 ~]# redis-trib.rb check eshop-cache01:7001
>>> Performing Cluster Check (using node eshop-cache01:7001)
M: 3807711e01cd28509d7ba9839e601058bf2a30cf eshop-cache01:7001
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
S: 9247dfb394441619da9da5b75b62b034c3f420e5 192.168.99.172:7006
   slots: (0 slots) slave
   replicates cb2256b653cf5b7b3f9d1478cfa2953cc334c5bf
M: cb2256b653cf5b7b3f9d1478cfa2953cc334c5bf 192.168.99.172:7005
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
S: a174fe6613862db8985f82caac58bde91dfbd664 192.168.99.171:7004
   slots: (0 slots) slave
   replicates 3807711e01cd28509d7ba9839e601058bf2a30cf
M: 0ff0e8ab05a8b032aeacf24e0c7fea77be3f5c55 192.168.99.171:7003
   slots:5461-10922 (5462 slots) master
   1 additional replica(s)
S: b0d66be3b15e117696c50a781ff24a842456733d 192.168.99.170:7002
   slots: (0 slots) slave
   replicates 0ff0e8ab05a8b032aeacf24e0c7fea77be3f5c55
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

# 我这里 7001 是 master
# kill 掉它
[root@eshop-cache01 ~]# ps -ef | grep redis
root     27419     1  0 09:25 ?        00:00:31 /usr/local/bin/redis-server 192.168.99.170:7001 [cluster]
root     27424     1  0 09:25 ?        00:00:31 /usr/local/bin/redis-server 192.168.99.170:7002 [cluster]
root     27505 27464  0 11:00 pts/2    00:00:00 grep redis
[root@eshop-cache01 ~]# kill 27419
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

再次运行 check 的时候就会发现少了一个节点,然而 master 节点却还是 3 个。 7004 变成 master 了

[root@eshop-cache01 ~]# redis-trib.rb check eshop-cache01:7002
>>> Performing Cluster Check (using node eshop-cache01:7002)
S: b0d66be3b15e117696c50a781ff24a842456733d eshop-cache01:7002
   slots: (0 slots) slave
   replicates 0ff0e8ab05a8b032aeacf24e0c7fea77be3f5c55
M: a174fe6613862db8985f82caac58bde91dfbd664 192.168.99.171:7004
   slots:0-5460 (5461 slots) master
   0 additional replica(s)
M: cb2256b653cf5b7b3f9d1478cfa2953cc334c5bf 192.168.99.172:7005
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
S: 9247dfb394441619da9da5b75b62b034c3f420e5 192.168.99.172:7006
   slots: (0 slots) slave
   replicates cb2256b653cf5b7b3f9d1478cfa2953cc334c5bf
M: 0ff0e8ab05a8b032aeacf24e0c7fea77be3f5c55 192.168.99.171:7003
   slots:5461-10922 (5462 slots) master
   1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

这个时候连接到 7004 上去读写都能正常使用,且能获取到 7001 上写入的数据

然后重启 7001,再次查看他的状态,发现 7001 被自动转为 slave 了

[root@eshop-cache01 ~]# /etc/init.d/redis_7001 start
Starting Redis server...
[root@eshop-cache01 ~]# redis-trib.rb check eshop-cache01:7001
>>> Performing Cluster Check (using node eshop-cache01:7001)
S: 3807711e01cd28509d7ba9839e601058bf2a30cf eshop-cache01:7001
   slots: (0 slots) slave
   replicates a174fe6613862db8985f82caac58bde91dfbd664
S: 9247dfb394441619da9da5b75b62b034c3f420e5 192.168.99.172:7006
   slots: (0 slots) slave
   replicates cb2256b653cf5b7b3f9d1478cfa2953cc334c5bf
M: a174fe6613862db8985f82caac58bde91dfbd664 192.168.99.171:7004
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
S: b0d66be3b15e117696c50a781ff24a842456733d 192.168.99.170:7002
   slots: (0 slots) slave
   replicates 0ff0e8ab05a8b032aeacf24e0c7fea77be3f5c55
M: cb2256b653cf5b7b3f9d1478cfa2953cc334c5bf 192.168.99.172:7005
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
M: 0ff0e8ab05a8b032aeacf24e0c7fea77be3f5c55 192.168.99.171:7003
   slots:5461-10922 (5462 slots) master
   1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
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

通过登录 7004 查看信息也是一样的

[root@eshop-cache01 ~]# redis-cli -h 192.168.99.171 -p 7004
192.168.99.171:7004> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.99.170,port=7001,state=online,offset=421,lag=1
master_repl_offset:421
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:420
1
2
3
4
5
6
7
8
9
10
11

在这里发现,之前使用 info 命令返回了一大堆信息,但是其中就包含了 replication,原来这个 info 命令后面可以跟一个具体的项,就只返回该了

总的感觉上来说,redis cluster 还是很强大。这个思路对于缓存来说比数据库考虑得要少,但是针对缓存场景来说,的确是很赞的一种设计