分析
为什么要分布式存储
如果有1~2亿条缓存数据需要缓存,这么大的量,单台服务器指定扛不住,必须要进行分布式存储,而为了保证redis的服务不宕机,或者宕机的影响最小化,可以采用主从复制加上redis自带的哨兵模式进行一个事故处理。
分布式存储的方式
1、哈希取余
2亿条记录就是2亿个k,v,单机不行必须要分布式多机,假设有3台机器构成一个集群,用户每次读写操作都是根据公式:
hash(key) % N个机器台数,计算出哈希值,用来决定数据映射到哪一个节点上。
优点:
简单粗暴,直接有效,只需要预估好数据规划好节点,例如3台、8台、10台,就能保证一段时间的数据支撑。使用Hash算法让固定的一部分请求落到同一台服务器上,这样每台服务器固定处理一部分请求(并维护这些请求的信息),起到负载均衡+分而治之的作用。
缺点:
原来规划好的节点,进行扩容或者缩容就比较麻烦了额,不管扩缩,每次数据变动导致节点有变动,映射关系需要重新进行计算,在服务器个数固定不变时没有问题,如果需要弹性扩容或故障停机的情况下,原来的取模公式就会发生变化:Hash(key)/3会变成Hash(key) /?。此时地址经过取余运算的结果将发生很大变化,根据公式获取的服务器也会变得不可控。
某个redis机器宕机了,由于台数数量变化,会导致hash取余全部数据重新洗牌。
2、一致性Hash算法
容错性:
假设Node C宕机,可以看到此时对象A、B、D不会受到影响,只有C对象被重定位到Node D。一般的,在一致性Hash算法中,如果一台服务器不可用,则受影响的数据仅仅是此服务器到其环空间中前一台服务器(即沿着逆时针方向行走遇到的第一台服务器)之间数据,其它不会受到影响。简单说,就是C挂了,受到影响的只是B、C之间的数据,并且这些数据会转移到D进行存储。
扩展性
数据量增加了,需要增加一台节点NodeX,X的位置在A和B之间,那收到影响的也就是A到X之间的数据,重新把A到X的数据录入到X上即可,
不会导致hash取余全部数据重新洗牌。
缺点:Hash环的数据倾斜问题
一致性Hash算法在服务节点太少时,容易因为节点分布不均匀而造成数据倾斜(被缓存的对象大部分集中缓存在某一台服务器上)问题,
例如系统中只有两台服务器:
小总结
为了在节点数目发生改变时尽可能少的迁移数据
将所有的存储节点排列在收尾相接的Hash环上,每个key在计算Hash后会顺时针找到临近的存储节点存放。
而当有节点加入或退出时仅影响该节点在Hash环上顺时针相邻的后续节点。
优点
加入和删除节点只影响哈希环中顺时针方向的相邻的节点,对其他节点无影响。
缺点
数据的分布和节点的位置有关,因为这些节点不是均匀的分布在哈希环上的,所以数据在进行存储时达不到均匀分布的效果。
3、哈希槽
1 为什么出现
哈希槽实质就是一个数组,数组[0,2^14 -1]形成hash slot空间。
2 能干什么
解决均匀分配的问题,在数据和节点之间又加入了一层,把这层称为哈希槽(slot),用于管理数据和节点之间的关系,现在就相当于节点上放的是槽,槽里放的是数据。
槽解决的是粒度问题,相当于把粒度变大了,这样便于数据移动。
哈希解决的是映射问题,使用key的哈希值来计算所在的槽,便于数据分配。
3 多少个hash槽
一个集群只能有16384个槽,编号0-16383(0-2^14-1)。这些槽会分配给集群中的所有主节点,分配策略没有要求。可以指定哪些编号的槽分配给哪个主节点。集群会记录节点和槽的对应关系。解决了节点和槽的关系后,接下来就需要对key求哈希值,然后对16384取余,余数是几key就落入对应的槽里。slot = CRC16(key) % 16384。以槽为单位移动数据,因为槽的数目是固定的,处理起来比较容易,这样数据移动问题就解决了。
技术栈
- 1、redis,这个不用讲了吧
- 2、docker,因为docker能快速将需要的环境搭建起来,用实体机差不多的,把中间的命令稍微做更改就行了
- 3、采用哈希槽的方式进行分布式存储
步骤
前提
需要自己安装docker,让后我的 docker中redis版本是
6.0.8这个版本。只需要执行docker pull redis:6.0.8
这个命令就行。
然后启动6个redis实例就行了
docker run -d --name redis-1 --net host --privileged=true -v /data/redis/share/redis-1:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6381docker run -d --name redis-2 --net host --privileged=true -v /data/redis/share/redis-2:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6382docker run -d --name redis-3 --net host --privileged=true -v /data/redis/share/redis-3:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6383docker run -d --name redis-4 --net host --privileged=true -v /data/redis/share/redis-4:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6384docker run -d --name redis-5 --net host --privileged=true -v /data/redis/share/redis-5:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6385docker run -d --name redis-6 --net host --privileged=true -v /data/redis/share/redis-6:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6386
命令详解
- 1、 docker run 创建并运行docker实例
- 2、-- name redis-4 为每一个实例设置一个名字
- 3、 --net host 网络模式,代表使用宿主机的ip和端口
- 4、–privileged=true 使用宿主机的root权限,能写能读
- 5、-v /data/redis/share/redis-5:/data 容器卷地址 宿主机:实例
- 6、redis:6.0.8 redis的镜像和版本号
- 7、–cluster-enabled yes 开启redis集群
- 8、 --appendonly yes 开启持久化
- 9、–port 6386 redis端口号
执行完上述命令,可以输入docker ps
命令查看当前运行的实例都有哪些
随便输入命令docker exec -it redis-1 bash
命令进入的其中一台,然后输入redis-cli -p 6381
进入的redis中,发现输入set key value
会提示
说没有提供哈希槽,是不能直接使用的。
接下来分配主从机器,将ip换成你们自己的ip
redis-cli --cluster create 172.16.16.144:6381 172.16.16.144:6382 172.16.16.144:6383 172.16.16.144:6384 172.16.16.144:6385 172.16.16.144:6386 --cluster-replicas 1
- –cluster-replicas 1 表示为每个master创建一个slave节点
第一个框指出了哈希槽的分配情况,第二个指出了那些是主服务,那些事从服务。最后询问了是否遵循以上设置,输入yes进入下一步。
出现以上提示代表成功了。
查看集群状态
然后还以 名字为redis-1
这台机器作为切入点,命令为docker exec -it redis-1 bash
,进入后输入redis-cli -p 6381
可以看到状态时OK的,并且知道有6个节点。
可以输入 cluster nodes
查看每个节点的状态
上面可以看出,端口为 6381、6382、6383三台机器为主服务器,其余三台为从服务。可以看看出每个机器分配的哈希槽的范围。
可以通过redis-cli --cluster check 172.16.16.144:6381
这么命令查看每个节点存储的信息,现在并没有存入任何信息,可以看到都是0个key,也能看到每一台主服务器都有一台从服务器。
存入数据
如果时通过普通的redis-cli -p
直接进入会出现以下情况
原是key1
这个key 哈希计算后的插槽并不在这台机器上
而key2
这个key的哈希在这这台机器上。对于这个情况,需要在连接的时候加上参数 -c
就行了,redis就能自动帮把数据路由到位置。
提示说已经把这个数据移动到开放端口为6382这台机器上了。然后检查集群
看到有两个key已经放入到了对应的主机上
容错切换
- redis的策略是如果主服务挂了,从服务器会立马顶上,不过不知道这个和哨兵模式有什么联系,反转能顶。
现在假如6381这台服务器宕机了,就是先停掉了这台服务。然后端口为6382这台服务器重启了
然后进入端口为6382这台服务器。
输入redis-cli --cluster check 172.16.16.144:6381
可以看到端口为6386这台机器顶上了之前宕机的这台机器,插槽也继承了宕机的那台。然后重启的端口为6382这台机器,然后6382这台机器还是mast节点。
最后还原三主三从,发现端口为6381这台服务器并没成为主服务器
也就是具体的主从服务器要具体来看。