实例
需要先启动redis实例,注意端口,8081-8083
#创建3个新文件夹!
cd /opt/nosql/redis/redis/conf mkdir 1 2 3
#拷贝conf文件到各自文件夹,2和3端口设置不同,其他一致
port 8081
cluster-enabled yes
#启动注意!切换到自己目录下再执行,因为集群启动需要在当前目录生成nodes.conf文件,否则冲突!
cd 1
redis-server redis.conf
集群
#replicas表示副本数
redis-cli --cluster create 127.0.0.1:8081 127.0.0.1:8082 127.0.0.1:8083 -- cluster-replicas 0
如果节点上有数据,可能会有错误提示:
[ERR] Node 127.0.0.1:8004 is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0.
删除dump.rdb,nodes.conf,登录redis-cli,flushdb即可
扩容
1)按上面方式,新起一个redis , 8084端口
#第一个参数是新节点的地址,第二个参数是任意一个已经存在的节点的IP和端口
redis-cli --cluster add-node 127.0.0.1:8084 127.0.0.1:8081
2)使用redis-cli登录任意节点,使用cluster nodes可以查看集群信息
127.0.0.1:8081> cluster nodes
#注意!新加进来的这个8084是空的,没有分配片段
eb49056da71858d58801f0f28b3d4a7b354956bc 127.0.0.1:8084@18084 master - 0
1602665893207 0 connected
16a3f8a4be9863e8c57d1bf5b3906444c1fe2578 127.0.0.1:8082@18082 master - 0
1602665891204 2 connected 5461-10922
214e4ca7ece0ceb08ad2566d84ff655fb4447e19 127.0.0.1:8083@18083 master - 0
1602665892000 3 connected 10923-16383
864c3f763ab7264ef0db8765997be0acf428cd60 127.0.0.1:8081@18081 myself,master - 0
1602665890000 1 connected 0-5460
3)重新分片
redis-cli --cluster reshard 127.0.0.1:8081
#根据提示一步步进行,再次查看node分片,可以了!
127.0.0.1:8081> cluster nodes eb49056da71858d58801f0f28b3d4a7b354956bc 127.0.0.1:8084@18084 master - 0
1602666306047 4 connected 0-332 5461-5794 10923-11255
16a3f8a4be9863e8c57d1bf5b3906444c1fe2578 127.0.0.1:8082@18082 master - 0
1602666305045 2 connected 5795-10922
214e4ca7ece0ceb08ad2566d84ff655fb4447e19 127.0.0.1:8083@18083 master - 0
1602666305000 3 connected 11256-16383
864c3f763ab7264ef0db8765997be0acf428cd60 127.0.0.1:8081@18081 myself,master - 0
1602666303000 1 connected 333-5460
5) pre-shading
扩容reshard带来值的重新hash和移动,那么有没有办法减少这种影响呢?看官方方案:
- 启动时,预先在每台机器起两个,这样3台机器有1-6个redis组成集群。
- 当机器资源扛不住了,再购入3台物理机。
- 在新物理机上起redis,并配置为要迁移的redis的从库(比如4,5,6)。
- 配置从库为新的主库,移除老的端口实例。
- 修改应用层ip地址为物理机地址,扩容完成。
原理
1)hash slot 原理
- redis cluster使用数据分片,不是一致性哈希!
- redis cluster有固定的16384个hash slot(一致性hash的槽是可变的)
- slot是redis cluster中数据管理的基本单位,每个节点负责一定数量的槽,迁移也是整槽迁移
- 所以redis集群中可以灵活指派,哪些槽属于哪些机器
- key所属槽的计算使用crc16算法,slot = crc16(key) % 16384
2)来自服务器的思考……
分布式协作中的两种管理思想(以班级找人为例,是班长统一接待,还是全班都互相认识?)
集中式:
- 所有插槽配置信息交给一个管理节点,统一协调,所有操作来这里,我告诉你
- 一致性高,单点风险,所有操作均需要转交
去中心化:
- 所有节点都保持集群信息,互相同步,你可以去任意节点操作
- 可互为备份,但一致性差,信息同步需要时间
redis集群采取无中心化:
- 每个节点都会记录slot对应的机器(在nodes.conf文件里),互相间使用gossip协议进行通信
- 客户端可以向任意节点发送键命令,节点要计算这个键属于哪个槽
- 如果是自己负责这个槽,那么直接执行命令,否则,返回客户端一个MOVED错误,告诉它在哪里
2)来自客户端的思考……
上面是对集群,也就是服务端来说,下面思考客户端
懒惰的客户端:
- 随便连一个,由服务器端去查找,找到直接就给我,找不到让我重定向我就重定向再去找
- 实现起来非常简单,本地不需要运算,大概率会多一次请求
积极的客户端:
- 我和服务器端一样的计算,发送key之前,自己算算在哪个节点,直接去拿
- 实现起来相对复杂,但是!效率高
JedisCluster采取积极客户端:
- JedisCluster初始化的时候,就会随机选择一个node,本地初始化一张hashslot -> node映射表
- 将映射表缓存起来,需要时,通过key本地先算slot,查表找这个槽在哪个node上
- 去这个node上操作!如果操作成功,一切ok!
如果正好,重新分片,那么会得到MOVED错误,怎么办?
- 取该节点的元数据,更新本地的hashslot -> node映射表缓存,再来一遍。
- 重试超过5次,那么就报错,JedisClusterMaxRedirectionException