一、搭建读写分离+高可用+多master的redis cluster集群

redis cluster:

自动,master+slave复制和读写分离,master+slave高可用和主备切换,支持多个master的hash slot支持数据分布式存储

停止之前所有的实例,包括redis主从和哨兵集群

1、redis cluster的重要配置

cluster-enabled <yes/no>

cluster-config-file < filename >:

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

cluster-node-timeout < milliseconds >:

节点存活超时时长,超过一定时长,认为节点宕机,master宕机的话就会触发主备切换,slave宕机就不会提供服务

2、在三台机器上启动6个redis实例

(1)在s3上部署目录

/etc/redis(存放redis的配置文件),/var/redis/6379(存放redis的持久化文件)

(2)编写配置文件

redis cluster集群,要求至少3个master,去组成一个高可用,健壮的分布式的集群,每个master都建议至少给一个slave,3个master,3个slave,最少的要求

正式环境下,建议都是说在6台机器上去搭建,至少3台机器

保证,每个master都跟自己的slave不在同一台机器上,如果是6台自然更好,一个master+一个slave就死了

3、台机器去搭建6个redis实例的redis cluster

mkdir -p /etc/redis-cluster #创建让redis-cluster自己管理的目录
mkdir -p /var/log/redis #创建日志目录
mkdir -p /var/redis/7001,7002,7003,7004,7005,7006 #创建redis-cluster配置文件目录
  • 7001,7002,7003,7004,7005,7006.conf对应配置
port 7001 #对应端口号,下面的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个master节点启动,每个master加一个slave节点,先选择6个节点,启动6个实例

将上面的配置文件,在/etc/redis下放6个,

分别为: 7001.conf,7002.conf,7003.conf,7004.conf,7005.conf,7006.conf

(3)准备生产环境的启动脚本

在/etc/init.d下,放6个启动脚本,

分别为: redis_7001, redis_7002, redis_7003, redis_7004, redis_7005, redis_7006

每个启动脚本内,都修改对应的端口号

redis批量写入pipeline java redis批量写入集群_redis-cluster

基于每个init.d启动脚本权限

chmod 777 redis_700X

(4)分别在3台机器上,启动6个redis实例

将每个配置文件中的slaveof给删除

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
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

因为,以前比如公司里面搭建集群,公司里的机器的环境,运维会帮你做好很多事情

在讲课的话,我们手工用从零开始装的linux虚拟机去搭建,那肯定会碰到各种各样的问题

redis批量写入pipeline java redis批量写入集群_多master写入_02

yum install -y ruby
yum install -y rubygems
gem install redis

cp /usr/local/redis-3.2.8/src/redis-trib.rb /usr/local/bin
#这个cp如果没有,就到redis根目录的src下


redis-trib.rb create --replicas 1 192.168.109.101:7001 192.168.109.101:7002 192.168.109.102:7003 192.168.109.102:7004 192.168.109.103:7005 192.168.109.103:7006

--replicas: 每个master有几个slave

6台机器,3个master,3个slave,尽量自己让master和slave不在一台机器上

yes

#检查集群的状态
redis-trib.rb check 192.168.109.101:7001

5、上面创建的集群包含内容

读写分离+高可用+多master

6台实例、2个一组,一主一从,共3组

读写分离:每个master都有一个slave

高可用:master宕机,slave自动被切换过去

多master:横向扩容支持更大数据量

二、redis cluster进行多master写入、读写分离、高可用验证

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

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

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

你在redis cluster写入数据的时候,其实是你可以将请求发送到任意一个master上去执行

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

如果对应的master就在自己本地的话,set mykey1 v1,mykey1这个key对应的hashslot就在自己本地,那么自己就处理掉了

但是如果计算出来的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持久化 -> 灾难的时候,做数据恢复,复制 -> 读写分离,扩容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里面很多核心的思想都是类似的

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

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

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

在这个redis cluster中,如果你要在slave读取数据,那么需要带上readonly指令get mykey1

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

redis批量写入pipeline java redis批量写入集群_读写分离_03


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


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

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

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

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

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

要不然你就是自己基于jedis,封装一下,自己做一个redis cluster的读写分离的访问api

核心的思路,就是说,redis cluster的时候,就没有所谓的读写分离的概念了

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


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

也可以实现支撑更高的读吞吐的效果

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

redis cluster,主从架构,读写分离,没说错,没有撒谎

redis cluster,不太好,server层面,jedis client层面,对master做扩容,所以说扩容master,跟之前扩容slave,效果是一样的

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

redis-trib.rb check 192.168.109.101:7001

redis批量写入pipeline java redis批量写入集群_高可用_04

redis批量写入pipeline java redis批量写入集群_高可用_05

比如把master1,101:7001,杀掉,看看它对应的102:7004能不能自动切换成master,可以自动切换

[root@s1 init.d]# ps -ef|grep redis
[root@s1 init.d]# kill -9 1176
[root@s1 init.d]# cd /var/run
[root@s1 run]# rm -rf redis_7001.pid 
#杀掉m1主节点,然后删除对应的pid文件

过了一会,查看情况,发现7004已经成为主节点了

redis批量写入pipeline java redis批量写入集群_多master写入_06

切换成master后的102:7004,可以直接读取数据

redis批量写入pipeline java redis批量写入集群_读写分离_07

查看7001的日志情况,显示了7001切换为从节点,并7004成为主节点

redis批量写入pipeline java redis批量写入集群_读写分离_08

再试着把101:7001给重新启动,

redis批量写入pipeline java redis批量写入集群_高可用_09

发现恢复过来,7001自动作为slave挂载到了102:7004上面去

redis批量写入pipeline java redis批量写入集群_高可用_10

redis批量写入pipeline java redis批量写入集群_多master写入_11

实验成功!!!


三、redis cluster通过master水平扩容来支撑更高的读写吞吐+海量数据

redis cluster模式下,不建议做物理的读写分离了

我们建议通过master的水平扩容,来横向扩展读写吞吐量,还有支撑更多的海量数据

redis单机,读吞吐是5w/s,写吞吐2w/s

扩展redis更多master,那么如果有5台master,不就读吞吐可以达到总量25/s QPS,写可以达到10w/s QPS

redis单机,内存,6-8G

如果redis内存过大,那么fork类操作的时候很耗时,会导致请求延时的问题

扩容到5台master,能支撑的总的缓存数据量就是30G,40G -> 100台,600G,800G,甚至1T+,海量数据

1、redis是怎么扩容的,加入新master

  • 创建需要的文件夹
mkdir -p /var/log/redis
mkdir -p /etc/redis-cluster
mkdir -p /var/redis/7007
  • 创建对应7001.conf的配置文件,并放置在/etc/redis/下
port 7007
cluster-enabled yes
cluster-config-file /etc/redis-cluster/node-7007.conf
cluster-node-timeout 15000
daemonize	yes							
pidfile		/var/run/redis_7007.pid 						
dir 		/var/redis/7007		
logfile /var/log/redis/7007.log
bind 192.168.109.103
appendonly yes
  • 创建对应启动脚本redis_7007,放置在/etc/init.d/下

redis批量写入pipeline java redis批量写入集群_redis-cluster_12

  • 手动启动一个新的redis实例,在7007端口上

redis批量写入pipeline java redis批量写入集群_多master写入_13

  • 连接到新的redis实例上,cluster nodes,确认自己是否加入了集群,作为了一个新的master
redis-trib.rb add-node 192.168.109.103:7007 192.168.109.101:7001
redis-trib.rb check 192.168.109.101:7001

7007加入成功,但是他是以master主节点加入的,并且没有slots,那他就无法处理数据

redis批量写入pipeline java redis批量写入集群_读写分离_14

2、reshard数据迁移

resharding把一部分hash slot从一些node上迁移到另外一些node上

./redis-trib.rb reshard 192.168.109.101:7001

要把之前3个master上,总共4096个hashslot迁移到新的第四个master上去

16384/4

redis批量写入pipeline java redis批量写入集群_redis_15

How many slots do you want to move (from 1 to 16384)?
4096
What is the receiving node ID? #指定要迁移的目标节点node id
1cc3fc38e522b6136832cea140595be390993460
Please enter all the source node IDs.
  Type 'all' to use all the nodes as source nodes for the hash slots.
  Type 'done' once you entered all the source nodes IDs.
#指定要迁移的数据源节点node id
Source node #1:c4220974b2bbca56502fea562dd47166af064026
Source node #2:dfd566548ec8e4d1a52773c84c0c3845aa5c9bbf
Source node #3:a74166c80bb4645a0bc71c50c9f36c33e2e0e7fb
Source node #4:done   #输入完就done

Do you want to proceed with the proposed reshard plan (yes/no)?
yes
  • 再次check检查一下
redis-trib.rb check 192.168.109.101:7001

发现7007已经有对应的4096个hash slots了

redis批量写入pipeline java redis批量写入集群_高可用_16

3、添加node作为slave

增加slave,在rediscluster中是增加高可用性,保证master宕机后还有slave可以被选成master,保持运作

  • 在s3虚拟机上再添加一个redis实例,作为上面的7007的slave从节点
  • 创建对应文件夹
mkdir -p /var/log/redis
mkdir -p /etc/redis-cluster
mkdir -p /var/redis/7008
  • 创建对应7001.conf的配置文件7008.conf,并放置在/etc/redis/下
port 7008
cluster-enabled yes
cluster-config-file /etc/redis-cluster/node-7008.conf
cluster-node-timeout 15000
daemonize yes           
pidfile    /var/run/redis_7008.pid          
dir     /var/redis/7008   
logfile /var/log/redis/7008.log
bind 192.168.31.227   
appendonly yes
  • 创建对应7008的启动脚本,在/etc/init.d/下,名为redis_7008

redis批量写入pipeline java redis批量写入集群_多master写入_17

  • 启动7008实例

redis批量写入pipeline java redis批量写入集群_redis_18

  • 将7008实例挂载到7004下面做slave从节点
redis-trib.rb add-node --slave --master-id a74166c80bb4645a0bc71c50c9f36c33e2e0e7fb 192.168.109.103:7008 192.168.109.101:7001
  • 检查
redis-trib.rb check 192.168.109.101:7001

redis批量写入pipeline java redis批量写入集群_多master写入_19

4、删除node

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

现在是4个master,一共16384个hash slots,那么一个master上有4096个hash slots,那么如果要删除一个node,就是16384/4等于4096个hash slots,我们要将这4096平分改不删除的3个master上面,也就是4096/3,那就要2个是1365,1个是1366

  • 先做hash slots的迁移,重复上面的步骤,迁移hash slots
./redis-trib.rb reshard 192.168.109.101:7001 #迁移命令
How many slots do you want to move (from 1 to 16384)? 1365
What is the receiving node ID? #另外3个master的nodeid,指定要迁移的目标节点node id
Source node #7007的node id,数据源
  • 检查,保证要删除的master节点的hash slots数量为 0
redis-trib.rb check 192.168.109.101:7001

redis批量写入pipeline java redis批量写入集群_高可用_20

如果你要删除的master节点下面有slave节点,当你清空了一个master的hash slot时,redis cluster就会自动将其slave挂载到其他master上去

  • 删除node节点

这个时候就只要删除掉master就可以了

redis-trib.rb del-node 192.168.109.101:7001 <nodeId>

redis批量写入pipeline java redis批量写入集群_高可用_21

  • 再次检查
redis-trib.rb check 192.168.109.101:7001
  • 发现7007已经被删了

redis批量写入pipeline java redis批量写入集群_redis_22

  • 在s3看状态,发现7007的cluster的redis已经自动下线了

redis批量写入pipeline java redis批量写入集群_redis-cluster_23

实验成功!!!


四、redis cluster的自动化slave迁移高可用架构

  • slave的自动迁移

比如现在有10个master,每个有1个slave,然后新增了3个slave作为冗余,有的master就有2个slave了,有的master出现了salve冗余

如果某个master的slave挂了,那么redis cluster会自动迁移一个冗余的slave给那个master

比如一个master下面有2个slave,一个master有1个slave,如果1个slave的master的slave宕机了,那redis会自动化迁移,将那个有两个slave的master下面的其中一个slave挂载到,那个master下面一个slave都没有的下面

只要多加一些冗余的slave就可以了

为了避免的场景,就是说,如果你每个master只有一个slave,万一说一个slave死了,然后很快,master也死了,那可用性还是降低了

但是如果你给整个集群挂载了一些冗余slave,那么某个master的slave死了,冗余的slave会被自动迁移过去,作为master的新slave,此时即使那个master也死了,还是有一个slave会切换成master的

之前有一个master是有冗余slave的,直接让其他master其中的一个slave死掉,然后看有冗余slave会不会自动挂载到那个master