一、3种分布式实现原理
1、哈希取余
方法:2亿条记录就是2亿个kv,我们单机不行必须要分布式多机,假设有3台机器构成一个集群,用户每次读写操作都是根据公式:hash(key)%N个机器台数,计算出哈希值,用来决定数据映射到哪一个节点上。
优点:简单粗暴,直接有效,只需要预估好数据规划好节点,例如3台、8台、10台,就能保证一段时间的数据支撑。使用Hash算法让固定的一部分请求落到同一台服务器上,这样每台服务器固定处理一部分请求(并维护这些请求的信息),起到负载均衡+分而治之的作用。
缺点:原来规划好的节点,进行扩容或者缩容就比较麻烦了额,不管扩缩,每次数据变动导致节点有变动,映射关系需要重新进行计算,在服务器个数固定不变时没有问题,如果需要弹性扩容或故障停机的情况下,原来的取模公式就会发生变化: Hash(key)/3会变成Hash(key)/P?。此时地址经过取余运算的结果将发生很大变化,根据公式获取的服务器也会变得不可控。某个redis机器宕机了,由于台数数量变化,会导致hash取余全部数据重新洗牌。
hash(key)的实现:crc32()函数
1、PHP,java,甚至Mysql等都自带函数crc32()
2、python好像需要自己写了
2、一致性哈希算法
方法:一致性哈希算法将整个服务器模拟成0 - 2^32-1个节点,散落在一个环上,但实际不会有这么多台服务器提供服务。我们通过对key的hash计算得到一个值,并把他映射到对应的位置,如果计算的值大于了环的长度0 - 2^32-1,可以除法取余。如果映射到的节点没有机器,那么顺时针把值存到下一个节点。
优点:
1、扩展性:如果在node1和node2之间加一台机器的话node5,那一部请求会打到node5上,相当于原本存在node2的数据存到了node5上。
2、容错性:如果node2坏了或者下掉了,原本打到node2的请求会打到node4上,只会顺时针影响一台服务器
不糊影响整个服务器的运行,只会影响一台服务器。
缺点:数据倾斜问题,比如突然增加或者减少节点,会造成下一个节点的请求突然减少或者倍增。
3、哈希槽
方法:一个集群只能有16384个槽,编号0-16383(0-2^14-1)。这些槽会分配给集群中的所有主节点,分配策略没有要求。可以指定哪些编号的槽分配给哪个主节点。集群会记录节点和槽的对应关系。解决了节点和槽的关系后,接下来就需要对key求哈希值,然后对16384取余,余数是几key就落入对应的槽里。slot =CRC16(key) %16384。以槽为单位移动数据,因为槽的数目是固定的,处理起来比较容易,这样数据移动问题就解决了。槽解决的是粒度问题,相当于把粒度变大了,这样便于数据移动。哈希解决的是映射问题,使用key的哈希值来计算所在的槽,便于数据分配。
优点:解决了一致性哈希算法的数据倾斜问题,在数据和节点之间又加入了一层,把这层称为哈希槽(slot),用于管理数据和节点之间的关系,现在就相当于节点上放的是槽,槽里放的是数据。
二、实战哈希槽分布式
1、先创建6个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 7001
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 7002
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 7003
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 7004
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 7005
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 7006
解释几个参数
--net host 使用宿主机的IP和端口,默认
--cluster-enabled yes 开启redis集群
--appendonly yes 开启持久化
*** 非容器操作---修改配置文件
1)redis.conf
# 如果是同一台机器启动多个redis服务,那需要配置多个redis.conf文件,需要修改如下
pidfile " /var/run/redis_7001.pid"
port 7001
dbfilename "dump7001.rdb"# 开启集群
cluster-enabled yes
#当前节点配置文件名字
cluster-config-file nodes-7001.conf# 设置节点失联时间,节点超过多长时间失联会进行主从切换
cluster-node-timeout15000cluster-require-full-coverage no
如果某一段插槽的主从都挂掉,而cluster-require-full-coverage为yes ,那么,整个集群都挂掉,
如果某一段插槽的主从都挂掉,而cluster-require-full-coverage为no,那么,该插槽数据全都不能使用,也无法存储,但其它机器不受影响。
2)创建完启动6个redis
redis-server redis.conf
2、创建主从关系
进入第一台机器执行,其实进入哪个容器都可以的哈。执行下面命令创建集群。
redis-cli --cluster create 192.168.186.129:7001 192.168.186.129:7002 192.168.186.129:7003 192.168.186.129:7004 192.168.186.129:7005 192.168.186.129:7006 --cluster-replicas 1
遇到询问输入:yes,这样就创建完成了。
--replicas 1采用最简单的方式配置集群,一台主机,一台从机,正好三组
*** 非容器操作---
1)redis5和redis6都集成了将多个节点配置成一个集群的机制ruby,在更低版本的redis里需要安装集群插件,这里不追溯了,安装步骤可以百度下。
2)先进入redis的安装目录,在进入src目录,就可以执行上面容器使用的创建集群命令了。
3、查看集群信息命令
127.0.0.1:7001> cluster info
127.0.0.1:7001>cluster nodes
查看集群的主从关系
从上面的截图可以看出来:7001、7002、7003是master,7004、7005、7006是slave,他们的主从关系是:
7001 => 7006
7002 => 7004
7003 => 7005
redis-cli --cluster check 192.168.186.129:7001
4、集群链接redis
如果还像之前链接redis就会出现下面的情况有些值存储不成功
redis-cli -p 7001
要改用集群方式链接加参数:-c
redis-cli -c -p 7001
这样如果不在当前的槽点机器,会跳转到集群中的槽点机器。
5、主从容错切换
当集群中7001机器挂掉的时候,他的从机7006会立马上位,升级为master提供服务;但是当7001恢复的时候,他会变为从机而不是主机。但是保证了集群的高可用性。
6、扩容
a. 我们新增2台集群机器,作为第四组主从
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 7007
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 7008
b.将新增的7007添加到7001所在集群中
redis-cli --cluster add-node 192.168.186.129:7007 192.168.186.129:7001
c.检查下集群情况:7007没有槽位,接下来给它分配槽位。
d. 重新给集群分配槽号
redis-cli --cluster reshard 192.168.186.129:7001
输入完命令,会有询问:
4096:16384/4算出来的;
cb0a3a9f45fb4b83a589a9a8f32d96a77eea858f :是给谁分配槽位,我写入的是7007的id
all:是给集群中的主机全部重新分配
下面还有询问,输入yes即可。这样我们就给7001分配了槽位,等分配完成。查看下分配情况:
你会发现,7007的槽位是其他主机挪过来一部分,给他的,片段。而不是全部重新分配,也就保证了,之前在7001的数据还在7001,只是把没用到的槽位分配给了7007.这点狠重要。
e. 把7008挂到7007作为slave。
redis-cli --cluster add-node 192.168.186.129:7008 192.168.186.129:7001 --cluster-slave --cluster-master-id cb0a3a9f45fb4b83a589a9a8f32d96a77eea858f
到此为止,我们扩容就完成了:4主4从
7、缩容:把7007和7008删除
a.先删除7008
命令:redis-cli --cluster del-node 集群中任意一个主机:IP端口 要删除的机器ID
redis-cli --cluster del-node 192.168.186.129:7001 97fd8daa9e20613d5f31928f0baae475961dec9a
b.重新分配槽号
redis-cli --cluster reshard 192.168.186.129:7001
c.删除7007
redis-cli --cluster del-node 192.168.186.129:7001 cb0a3a9f45fb4b83a589a9a8f32d96a77eea858f
到此缩容完毕。
8、常用命令
1)计算key的插槽值
cluster keyslot aaa
2)统计槽位有多少个值,注意,这个命令只能查看自己服务器插槽里的值,其它看不到
countkeysinslot 4847
3)获取槽位中的keys
ysinslot 484710
9、PHP、Java操作redis集群
操作的时候直接连接redis集群即可,不用单个连接操作
10、优势和劣势
1)优势:实现扩容、分摊压力、无中心化
2)缺点:
a、多键操作是不被支持的L
b、多键的Redis事务是不被支持的。
c、lua脚本不被支持
d、由于集群方案出现较晚,很多公司已经采用了其他的集群方案,而代理或者客户端分片的方案想要迁移至redis cluster,需要整体迁移而不是逐步过渡,复杂度较大。·