docker进阶(redis主从集群,一致性hash算法)

docker进阶(redis主从集群,一致性hash算法)

redis主从集群,一致性哈希算法

哈希取余分区

在这里插入图片描述

举例,我们要存储2亿条数据,也就是2亿个k.v。这时候我们单机不行,必须要进行分布式多级,假设我们有3台机器构成一个集群,用户每次读写操作都是根据公式hash(key)%N(N为机器的太熟),计算出哈希值,用来决定数据映射到哪一个节点上。

优点:

简单粗暴,直接有效,只需要预估好数据,规划好节点;例如3台,8台,10台,就能保证一段时间的数据支撑。使用Hash算法让固定的一部分请求落在同一台服务器上,这样每台服务器固定处理一部分请求(并维护这些请求信息),起到负载均衡+分而治之的作用。

缺点:

原来规划好的节点,进行扩容或者缩容就比较麻烦了。不管扩缩,每次数据变动导致节点有变动,映射关系需要重新进行计算,在服务器个数固定不变时没有问题,如果需要弹性扩容或者故障停机的情况下,原来的取模公式就会发生变化,Hash(key)/3会变成Hash(key)/?。此时地址经过某个redis机器宕机了,由于台数数量变化,会导致hash取余全部数据重新洗牌。

一致性哈希算法分区

算法背景:一致性哈希算法在1997年由麻省理工学院中提出,设计目标是为了解决分布式缓存数据变动和映射问题,单个机器宕机了,分母数量改变了,自然取余数就不行了。提出一致性Hash解决方案,目的是当服务器个数发生变动时,尽量减少影响客户端到服务器的映射关系

实现一致性哈希算法的三个步骤:

  1. 算法构建一致性哈希环

    一致性哈希算法必然有个hash函数并按照算法产生hash值,这个算法的所有可能哈希值会构成一个全量集,这个集合可以成为一个hash空间[0,232-1],这是一个线性空间,但是在算法中,我们通过适当的逻辑控制将它收尾相连(0=232),这样让它逻辑上形成了一个环形空间。

    扫描二维码关注公众号,回复: 15628768 查看本文章

    它也是按照使用取模的方法,前面的哈希取余算法分区使用的是对节点数进行取模。而一致性Hash算法是对232取模,简单来说,一致性Hash算法将整个哈希值空间组织成一个虚拟的圆环,假设某个哈希函数H的值空间为0-232-1(即hash值是一个32位无符号整形),整个哈希环按照顺时针方向组织,圆环的正上方的点代表0,0的右侧的第一个点代表1,以此类推2,3,4,…,2…32-1,也就是说在0点上左侧的第一个点代表332-1,0和232-1在零点中方向重合,我们把这个由232个点组成的圆环成为Hash环。

  2. 服务IP节点映射

    将集群中各个IP节点映射到环上的某一个位置。将各个服务器使用Hash进行哈希,具体可以选择服务器的IP或主机名作为关键字进行哈希,这样每台机器就能确定其在哈希环上的位置。

  3. key落到服务器的落键规则

    当我们需要存储一个key/value时,首先计算key的hash值,hash(key),将每个key使用第二步服务IP节点映射相同的函数Hash计算出哈希值并确定此数据在环上的位置,从此位置沿环顺时针行走,第一台遇到的服务器就是其应该定位到的服务器,并将该键值对存储在该节点。

在这里插入图片描述

优点:

加入和删除节点只影响哈希环中顺时针方向的相邻的节点,对其他节点无影响。

一致性哈希算法的容错性

假设 cs3 服务器出现故障导致服务下线,这时原本存储于 cs3 服务器的对象 o4,需要被重新分配至 cs2 服务器,其它对象仍存储在原有的机器上。

在这里插入图片描述

一致性哈希算法的扩展性

假设由于业务需要,我们需要增加一台服务器 cs4,经过同样的 hash 运算,该服务器最终落于 t1 和 t2 服务器之间 。

在这里插入图片描述

缺点:

数据的分布和节点的位置有关,因为这些节点不是均匀的分布在哈希环上,所以数据在进行存储时达不到均匀分布的效果。

一致性哈希算法的数据倾斜问题

一致性Hash算法在服务节点太少时,容易因为节点分布不均匀而造成数据倾斜(被缓存的对象大部分集中缓存在某一台服务器上)

哈希槽分区

哈希槽分区算法的出现就是为了解决一致性哈希算法的数据倾斜问题。哈希槽实质就是一个数组,数组[0,2^14-1]形成hash slot空间。

解决均匀分配的问题,在数据和节点之间又加入了一层,把这一层称为哈希槽(slot),用于管理数据和节点之间的关系,现在就相当于节点上放的是槽,槽里放的是数据。

在这里插入图片描述

槽解决的是颗粒度问题,相当于把粒度变大,这样便于数据移动。哈希解决的是映射问题,使用key的哈希值来计算,所在的槽,便于数据分配。

一个集群只能又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之间的哈希槽,也就是映射到某个节点上。

在这里插入图片描述

三主三从Redis集群配置

关闭防火墙:

# 环境为CentOS7.8
# 查看防火墙服务状态命令
systemctl status firewalld
# 查看防火墙的状态
firewall-cmd --state
# 开启防火墙服务
service firewalld start
# 重启防火墙服务
service firewalld restart
# 关闭防火墙服务
service firewalld stop

启动docker服务:

# 启动docker
systemctl start docker
# 查看docker状态
systemctl status docker

在这里插入图片描述

启动六台Redis实例:

## 参数说明
## docker run:创建并运行docker容器实例
## --name redis-node-6:容器名字
## --net host:使用宿主机的IP和端口,默认
## --privileged=true:获取宿主机root用户权限
## -v /data/redis/share/redis-node-6:/data:容器卷,宿主机地址:docker内部地址
## redis:6.2.6:redis镜像和版本号
## --cluster-enabled yes:开启Redis集群
## --appendonly yes:开启持久化
## --prot 6386:redis端口号

docker run -d --name redis-node-1 --net host --privileged=true -v /data/redis/share/redis-node-1:/data redis:6.2.6 --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.2.6 --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.2.6 --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.2.6 --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.2.6 --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.2.6 --cluster-enabled yes --appendonly yes --port 6386

在这里插入图片描述

进入容器redis-node-1并为6台机器构建集群关系:

# 进入容器
docker exec -it redis-node-1 /bin/bash
# 以下命令是进入容器内执行的(ip为你自己实际的IP)
# 命令说明:
# redis-cli:进入redis实例内部
# --cluster create:创建redis集群
# --cluster-replicas 1:为每一个master创建一个slave节点
redis-cli --cluster create 你自己的IP:6381 你自己的IP:6382 你自己的IP:6383 你自己的IP:6384 你自己的IP:6385 你自己的IP:6386  --cluster-replicas 1

在这里插入图片描述

以6381作为切入点,查看集群的状态:

# 注意以下命令是进入容器内执行的(docker exec -it redis-node-1 /bin/bash)
# 进入6381端口这台redis内
redis-cli -p 6381
# 查看集群信息
cluster info
# 查看节点信息
cluster nodes

在这里插入图片描述

根据节点信息可以找出以下的主从关系:

6381主机对应6385的从机

6382主机对应6386的从机

6383主机对应6384的从机

主从容错切换迁徙

数据存储:

由于现在是cluster环境了,直接连接某一台主机的话,存储数据可能会出现数据存不进去的问题(也就是key-value的key的槽位可能刚好不是你连接的那个集群,就会出现存不进去的问题)

# 演示存不进去数据的情况
docker exec -it redis-node-1 /bin/bash
redis-cli -p 6381

在这里插入图片描述

下面演示正确的连接redis集群的方式:

# 正确的连接方式 -c就表示以集群的方式连接,优化路由
docker exec -it redis-node-1 /bin/bash
redis-cli -p 6381 -c

在这里插入图片描述

在这里插入图片描述

注意:

我们存储完k1之后,能看到数据是在第三台redis上。当存储完成后可以看到,我们是从6381上重定向(Redirected)到6383上的。最后我们控制台也是停留在6383上。如果存储的数据机器没有变,就不用切换了。

在这里插入图片描述

查看集群的存储情况:

redis-cli --cluster check 你自己的IP:6381

在这里插入图片描述

容错切换迁移

将其中一台主机人为宕机掉,看下对应的从机是否会上位????

主体步骤:

  1. 登录redis-node-1节点查看集群状态,各个节点均存活
  2. 登录redis-node-2节点查看集群状态,各个节点均存活
  3. 将redis-node-1节点容器停止
  4. 在redis-node-2节点查看集群状态,发现之前redis-node-1的slave6385上位成功

在这里插入图片描述

在这里插入图片描述

这时如果将redis-node-1重启,6385是退位还是redis-node-1从新上位为master????

  • 重启redis-node-1
docker start redis-node-1

在这里插入图片描述

  • 查看集群状态
cluster nodes

在这里插入图片描述

结论:

6381并没有上位,而是6385继续当master。

猜你喜欢

转载自blog.csdn.net/weixin_45340300/article/details/126673310