第一种,最简单的轮询,写入是最直观方便的,假设集群三个节点,那就1,2,3;1,2,3……循环地写,但是查找的时候,就困难了,必须一个个节点去翻,这显然不符合。还有随机写入模式就更不用说了,也是一样的问题。
第二种,简单hash,也就是把对应的key对节点数量取模,找到对应的节点,既能解决写入的分配问题,也能解决读取问题,性能更优。但是当集群新增或减少节点的时候,所有缓存数据都要重新进行分配,不然同一个key,扩容/缩容前后对应的取模数值不一致,会导致找不到数据。而所有缓存数据重新分配,可想而知有多可怕…
第三种,一致性hash。通过维护一个哈希环,把所有的节点分区都包含在里面,可以想象成一个头尾相连的数组或者链表结构。虽然也是通过取模的方式获取对应key位置,但是它在节点扩缩的时候不需要重新分配所有数据。只需要把对应节点数据写入到距离它顺时针最近的一个节点即可,其它节点不需要改动。查询的时候也是一样,找出对应key哈希位置,如果没有百分百匹配的,那么就顺时针找到最近的一个节点,迁移后或者写入后的数据肯定在那里面!
但是,这种设计,在服务节点数量较少的时候(假设是3),还是会出现数据倾斜问题,于是一致性哈希算法引入了虚拟节点机制,也就是把3台物理服务器节点,又根据一些规则划分成N个逻辑节点(有的也称为虚拟节点),这样就可以更加平均地把数据平摊到N*3个逻辑节点中,而逻辑节点又可以确定读写哪个物理服务器节点。但是,它还是不够均匀,首先是Hash算法上要进一步优化,还有虚拟节点个数该怎么合理确定?
第四种,Slot+CRC16,这也是Cluster集群正在使用的分配算法,划分了16384个slot,借助类似hash slot list槽位链表来划分不同slot的位置,其次通过CRC16更加均匀地分配,每个节点都是master,在请求时,客户端发送查询给随机一个master节点,在通过hash_slot计算后如果在当前master,直接返回查询结果,如果不是当前master,则返回对应hash_slot的master的信息,客户端在下一次就会直接向目标master发起请求。
Slot槽位
在集群中,假设有3台服务器,因为服务器资源少,如果只是简单hash决定写入位置,容易导致数据倾斜(也就是大量数据集中在少量的节点),这就不符合我们的使用集群的高容错和平摊读写压力的预期。
CRC16算法:循环冗余校验算法,是一种质量的Hash算法,通过不断的循环计算,尽可能把key值都均匀分配到16384个槽位。具体的计算过程和原理请看大神的笔记https://www.cnblogs.com/esestt/archive/2007/08/09/848856.html
Redis集群模式下,会有多个节点,节点之间需要进行pingpong心跳通信,通信内容除了消息类型,节点id外,还会在消息主题中携带总结点数十分之一的节点信息进行交换,相互告知。如果槽位分配过大,会导致心跳通信的心跳包过大,浪费带宽且没必要,甚至容易造成网络拥堵。如果哈希槽设计过少,例如几千个的话,我们知道redis建议集群模式不要超过1000节点,那么如果是满载1000个节点,平分几千个哈希槽,在节点迁移时候容易出现数据倾斜,造成单节点访问过高,甚至造成节点连续崩溃,最终导致大规模服务集群雪崩相像。
更详细的解答参考:https://www.cnblogs.com/rjzheng/p/11430592.html
在redis.conf文件修改参数进行配置,最大10000,但是一般不配置那么多
单一环境(redis集群只为一套系统服务):
最小空闲连接数:可以考虑调整为客户端的服务器集群的总核心数
最大空闲连接数:可以考虑调整为客户端服务实例数量*开启的线程数(反正不要超过线程数,配置了也用不上)