2Docker下安装Redis集群:cluster(集群)模式-docker版,哈希槽分区进行亿级数据存储
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2Docker下安装Redis集群:cluster(集群)模式-docker版,哈希槽分区进行亿级数据存储相关的知识,希望对你有一定的参考价值。
背景
1~2亿条数据需要缓存,请问如何设计存储案例?
上述问题阿里P6~P7工程案例和场景设计类必考题目,一般业界有三种解决方案^:
2、一致性哈希算法分区
3、哈希槽分区
1、哈希取余分区
2亿条记录就是2亿个k,v,我们单机不行,必须要分布式多机,假设有3太机器构成一个集群,用户每次读写操作都是根据公式:hash(key)%N个机器台数,计算出哈希值,用来决定数据映射到哪一个节点上
优点:
简单粗暴,直接有效,只需要预估好数据,规划好节点,例如3台、8台、10台,就能保证一段时间的数据支撑。使用Hash算法让固定的一部分请求落到同一台机器上,这样每一台服务器固定处理一部分请求(并维护这些请求信息),起到负载均衡+分而治之的作用。
缺点:
原来规划好的节点,进行扩容或者缩容就比较麻烦了,不管扩容还是缩容,每次数据变动导致节点有变动,映射关系就需要进行重新计算,在服务器个数固定不变时没有问题,如果需要弹性扩容或者故障停机的情况下,原来的取余公式就会发生变化:hash(key)/3会变成hash(key)/?。此时,经过某个redis机器宕了,由于台数数量发生变化,会导致hash取余全部数据重新洗牌。
我们来举个例子:
假设请求1的取余结果为【hash(请求1)/3】:第一台机器,我们把数据存到了第一台机器,如果第一台机器宕机时,我们要去第一台机器查询我们请求1的数据时,是查询不到的。
也就是说,当服务器数量发生改变时,所有缓存在一定时间内是失效的,当应用无法从缓存中获取数据时,则会频繁向后端服务器请求也无法存储。造成雪崩
为了解决这个问题,就拥有了一次性哈希算法
2、一致性哈希算法分区
是什么:
一致性哈希算法在1997年由麻省理工学院中提出的,目的是为了解决分布式缓存数据变动和映射问题,某个单机坏了,分母数量变了,自然取余数就不ok了。
能干什么?
提出一致性哈希算法的解决方案。目的是当服务器个数发生变动的时候,尽量减少影响客户端到服务器的映射关系。
一致性哈希算法的具体步骤如下:
(一)、算法构建一致性哈希环
一致性哈希算法必然有个hash函数,并按照算法产生哈希值,这个算法的所有可能哈希值会构成一个全量集,这个集合可以成为一个哈希空间[0,2^32-1],这是一个线性的空间,我们通过适当的逻辑控制将它的首尾相连(0=2^32),这样让它逻辑上形成了一个闭环空间。
哈希取模算法是对节点(服务器)的数量进行取模,而一致性哈希算法是对2^32进行取模,简单来说,一致性哈希算法将整个哈希值空间组成一个虚拟的圆环。
我们可以将2^32想象成一个圆,像钟表一样,钟表的圆可以理解成由60个圆点组成,而一致性哈希环我们可以理解为由2^32个远点组成,成功空间按照顺时针方向组织,圆环的正上方的点代表0,0点右侧的第一个点代表1,以此类推,2、3、4、5、6……直到2^32-1,0点与2^32重合,我们把这个由2^32个点组成的圆环称为hash环
(二)、服务器IP节点映射
将集群上的各个点映射到环上的某一个位置
哈希算法:hash(服务器的IP) % 2^32
将各个服务器节点进行一个哈希计算,具体可以选择服务器的ip或主机名作为关键字进行哈希计算,这样每台机器就能确定其在哈希环上的位置。假如4个节点A、B、C、D,经过IP地址的hash计算,使用IP地址哈希后在环空间的位置如下:
(三)、key落到服务器的落键规则
当我们需要存储一个k,v键值对的时候,首先计算key的hash值,根据计算出的哈希值确定此数据在哈希环上的位置,从此位置沿着顺时针行走,遇到的第一台服务器就是其应该定位到的服务器,并将该键值对存储在该节点上。
如下图,有1、2、3,三个请求,按照上面规则,1、3请求顺时针行走遇到的第一个服务是A服务,所以将1、3的请求数据存储在A服务器上,2请求按照顺时针行走遇到的第一个服务是B服务,所以将2的请求数据存储在B服务上。
一致性哈希算法的优点
1、容错性
假设,我们上面例子的B服务宕机了,此时2请求进来时,沿着顺时针行走遇到的第一台服务是C服务器,所以2请求被重新定位到C服务器。此时A、B服务环中间的那部分取数会受到影响,而存数据不会受到影响,会定位到C服务。
2、扩展性
数据量增加了,需要增加一台节点X,X的位置在A和D之间,那受到查询影响的就是X和D之间的数据,而D到X之间的存数都落到X服务上即可,不会导致哈希取余所有数据重新洗牌。
一致性哈希算法的缺点
hash环的数据倾斜问题
一致性哈希算法在服务节点太少时,容易因节点分布不均匀而造成数据倾斜(被缓存的对象一大部分集中在某一台服务上)问题
3、哈希槽算法分区
(一)、为什么会出现
解决一致性哈希算法的数据倾斜问题
哈希槽实际上就是一个数组,数组[0,2^14-1]形成哈希槽空间(hash slot)
(二)、能干什么
解决均匀分配问题,在数据和节点之间又加入了一层,把这层成为哈希槽(hash slot) ,用于管理数据和节点之间的关系,现在就相当于节点上放的是槽,槽里放的是数据
槽解决的是粒度的问题,相当于把粒度变大了,这样便于数据移动
(三)、多少个hash槽
一个集群只能有16384个槽,编号0~16383(0-2^14-1)。这些槽会分配给集群中所有主节点,分配策略没有要求。可以指定哪些编号的槽分配给哪个主节点,集群会记录节点和槽的对应关系,解决了节点和槽的对应关系后,接下来就需要对key求哈希值,然后对16384取余,,余数是几,key就落入对应的槽里。slot=CRC16(key)%16384。以槽为单位移动数据,因为槽的数目是固定的,处理起来比较容易,这样数据移动的问题就解决了。
(四)、哈希槽的计算
Redis集群中内置了16384个哈希槽,redis会根据节点数量大致均匀的将哈希槽映射到不同的节点。当需要在Redis集群中放置一个key-value时,redis先对key使用crc16算法算出一个结果,然后把结果对16384取余,这样每个key都会对应一个编号在0-16383之间的哈希槽,也就是映射到某个节点上。
一、3主3从redis集群配置
docker run:创建并运行docker容器实例
--name redis-node-1:容器实例的名字
-d:后台守护进程运行
–net host:使用宿主机的ip和端口号,默认
--privileged=true:获取宿主机root用户权限,开启容器卷挂载
--cluster-enabled yes: 表示将 Redis 实例设置成集群节点而不是单机服务器
--appendonly yes:开启AOF持久化操作
--port 6386:redis端口号
docker run -d --name redis-node-1 --net host --privileged=true -v /data/redis/share/redis-node-1:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6381
docker run -d --name redis-node-2 --net host --privileged=true -v /data/redis/share/redis-node-2:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6382
docker run -d --name redis-node-3 --net host --privileged=true -v /data/redis/share/redis-node-3:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6383
docker run -d --name redis-node-4 --net host --privileged=true -v /data/redis/share/redis-node-4:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6384
docker run -d --name redis-node-5 --net host --privileged=true -v /data/redis/share/redis-node-5:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6385
docker run -d --name redis-node-6 --net host --privileged=true -v /data/redis/share/redis-node-6:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6386
返回以下结果,6个docker容器实例创建成功,此时,六台redis实例之间没有任何关系
进入容器redis-node-1并为6台机器构建集群关系:
1、进入容器:
docker exec -it redis-node-1 /bin/bash
2、构建主从关系:
redis-cli :进入到redis容器里
--cluster create:构建集群
--cluster-replicas 1:为每个master创建一个slave节点
注意使用自己的IP,进入docker容器后执行下面命令:
redis-cli --cluster create 192.168.150.128:6381 192.168.150.128:6382 192.168.150.128:6383 192.168.150.128:6384 192.168.150.128:6385 192.168.150.128:6386 --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 192.168.150.128:6385 to 192.168.150.128:6381
Adding replica 192.168.150.128:6386 to 192.168.150.128:6382
Adding replica 192.168.150.128:6384 to 192.168.150.128:6383
将哈希槽分配到6个节点中,三主三从,master1~master3平均分配,以及主从对应关系
连接进入6381作为切入点,查看集群状态:
1、进入6381:
redis-cli -p 6381
2、cluster info
3、cluster nodes
根据下图可以看出,本实例的主·从对应关系如下:
Master Slaver
1 5
2 6
3 4
二、主从容错切换迁移
1、数据读写存储
启动6机构成的集群,并通过exec进入
docker exec -it redis-node-1 /bin/bash
redis-cli -p 6381
对6381新增2个key
放入一个k1,v1后返回以下错误,数据存不进去,槽位号是12706,不在1服务上:
127.0.0.1:6381> set k1 v1
(error) MOVED 12706 192.168.150.130:6383
我们再来放一个k2,v2,结果如下,可以正常存放:
127.0.0.1:6381> set k2 v2
OK
防止路由失效,加参数-c并新增2个key
以上数据无法放进去,我们来加一个参数解决这个问题:
防止路由失效,加参数-c后,并新增2个key
-c代表集群环境连接
redis-cli -p 6381 -c
在此命令进入redis之后,发现可以正常插入数据,下图中,Redirected为重定向
查看集群信息
redis-cli --cluster check 192.168.150.130:6381
2、容错切换迁移
(一)、主6381和从机切换,先停止主机6381
docker stop redis-node-1
(二)、再次查看集群信息
随便进入一个节点:
docker exec -it redis-node-2 bash
redis-cli -p 6382 -c
查看集群信息如下:
cluster nodes
可以看出6381已经宕机,6381之前对应的从机6385已经上位变成主机
并且查询之前存储的数据都可以查到
(三)、先还原之前的3主3从
先启动6381:
docker start redis-node-1
我们启动节点1以后,发现,节点1成为了slaver,5节点还是master
再停6385:
docker stop redis-node-5
再启动6385:
docker start redis-node-5
此时,我们恢复了最初的状态
Master Slaver
1 5
2 6
3 4
(四)、查看集群状态
root@192:/data# redis-cli --cluster check 192.168.150.130:6381
3、主从扩容案例(新增一主一从)
新建6387、6388两个节点+新建后启动 +查看是否是8个节点
docker run -d --name redis-node-7 --net host --privileged=true -v /data/redis/share/redis-node-7:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6387
docker run -d --name redis-node-8 --net host --privileged=true -v /data/redis/share/redis-node-8:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6388
执行完上面两个命令后,只是启动了2个redis容器实例,这两个容器实例和集群没有任何关系
进入6387容器实例内部
docker exec -it redis-node-7 /bin/bash
新增的6387节点(空槽号)作为master节点加入原集群
将新增的6387节点作为master节点加入集群
redis-cli --cluster add-node 自己实际的IP地址:6387 自己实际的IP地址:6381
redis-cli --cluster add-node 192.168.150.130:6387 192.168.150.130:6381
6387就是将要作为master的新增节点
6381就是原来集群节点里的领路人,相当于6387加入6381领头的组织
检查集群情况第一次
redis-cli --cluster check 真实的ip地址:6381
redis-cli --cluster check 192.168.150.130:6381
此时我们的槽位分配情况为:
6381 :0~5460
6382: 5461~10922
6383:10923~16383
重新分配槽号
redis-cli --cluster reshard ip地址:端口号
redis-cli --cluster reshard 192.168.150.130:6381
检查集群情况第二次
重新分配槽后,槽位分配情况如下:
6381:1365~5460
6382:6827~10922
6383:12288~16383
6387: 0~1364;5461~6826;10923~12287
槽号分配说明:
为什么6387是3个新的区间,以前的旧节点还是连续槽位?
重新分配成本太高,所以前3家各匀出来一部分,从 6381/6382/6383三个旧节点分别匀出1364个坑位给新节点6387
为主节点6387分配从节点6388
redis-cli --cluster add-node IP:从机端口 IP:主机端口 --cluster-slave --cluster-master-id 主机ID
redis-cli --cluster add-node 192.168.150.130:6388 192.168.150.130:6387 --cluster-slave --cluster-master-id 1570427d5a692a5a3dfe45461470fd95aa407725
检查集群情况第三次
检查的时候,我们去任意一个master节点都可以,下面我们进去6382
redis-cli --cluster check 192.168.150.130:6382
4、主从缩容案例
检查集群情况,获得6388的节点id
redis-cli --cluster check 192.168.150.130:6381
可获取6388ID:
e6c0fcd4c7b39497efc1efeeeb370257344297d3
从集群中将4号从节点6388删除
redis-cli --cluster del-node IP:从机端口 从机6388节点ID
redis-cli --cluster del-node 192.168.150.130:6388 e6c0fcd4c7b39497efc1efeeeb370257344297d3
将6387的槽位号清空,重新分配,本例将清出来的槽位号都给6381
这里的端口号6381代表四台master机器以6381为落脚点去操作集群,
redis-cli --cluster reshard 192.168.150.130:6381
检查集群情况第二次
redis-cli --cluster check 192.168.150.130:6381
将6387删除
redis-cli --cluster del-node ip:主机4端口 节点ID
redis-cli --cluster del-node 192.168.150.130:6387 1570427d5a692a5a3dfe45461470fd95aa407725
检查情况第三次
redis-cli --cluster check 192.168.150.130:6381
docker 安装redis cluster集群
安装Redis镜像 docker pull yyyyttttwwww/redis 创建redis容器 docker run -it -d --name r1 -p 5001:6379 --net=net2 --ip 172.19.0.2 redis bash 查看网段 docker network ls 创建net2网络 docker network create --subnet=172.19.0.0/16 net2 创建redis容器 docker run -it -d --name r1 -p 5001:6379 --net=net2 --ip 172.19.0.2 redis bash docker run -it -d --name r1 -v /home/redis/redis.conf:/usr/redis/redis.conf -p 5001:6379 --net=net2 --ip 172.19.0.2 redis bash docker run -it -d --name r2 -v /home/redis/r2/redis.conf:/usr/redis/redis.conf -p 5002:6379 --net=net2 --ip 172.19.0.3 redis bash docker run -it -d --name r3 -v /home/redis/r3/redis.conf:/usr/redis/redis.conf -p 5003:6379 --net=net2 --ip 172.19.0.4 redis bash docker run -it -d --name r4 -v /home/redis/r4/redis.conf:/usr/redis/redis.conf -p 5004:6379 --net=net2 --ip 172.19.0.5 redis bash docker run -it -d --name r5 -v /home/redis/r5/redis.conf:/usr/redis/redis.conf -p 5005:6379 --net=net2 --ip 172.19.0.6 redis bash docker run -it -d --name r6 -v /home/redis/r6/redis.conf:/usr/redis/redis.conf -p 5006:6379 --net=net2 --ip 172.19.0.7 redis bash 进入容器 docker exec -it r1 bash 编辑配置文件 vi /usr/redis/redis.conf apt-get update apt-get install vim cd /usr/redis/src ./redis-server ../redis.conf 配置Redis节点 daemonize yes #以后台进程运行 cluster-enabled yes #开启集群 cluster-config-file nodes.conf #集群配置文件 cluster-node-timeout 15000 #超时时间 appendonly yes #开启AOF /usr/redis/redis.conf 安装redis-trib.rb docker exec -it r1 bash cd /usr/redis/ mkdir cluster cp /usr/redis/src/redis-trib.rb /usr/redis/cluster/ cd /usr/redis/cluster apt-get install ruby apt-get install rubygems gem install redis 创建Redis集群 ./redis-trib.rb create --replicas 1 172.19.0.2:6379 172.19.0.3:6379 172.19.0.4:6379 172.19.0.5:6379 172.19.0.6:6379
以上是关于2Docker下安装Redis集群:cluster(集群)模式-docker版,哈希槽分区进行亿级数据存储的主要内容,如果未能解决你的问题,请参考以下文章