前两篇文章对 Redis 主从复制和主从切换的知识点进行了介绍, 但是也很明显的有一点小弊端:
需要定时进行主从复制会影响 Redis 性能.
主节点宕机后, 从所有从节点选择进行主从切换. 主从切换的过程中非服务不可用.
引入分片概念 -- 分片机制的作用
而本篇文章主要谈谈 Redis 的分片机制, 如果没有分片机制, Redis 就被局限于单机所支持的内存容量. Redis 的分片机制允许数据拆分存放在不同的 Redis 实例上, 每个 Redis 实例只包含所有键的子集. 可以减轻单台 Redis 的压力, 提升 Redis 扩展能力和计算能力. 如果我们只使用一个 Redis 实例, 当 Redis 宕机将会直接停止服务, 所以我们可以采取分片机制, 将原本一台 Redis 实例维护的数据, 改为由多个 Redis 实例共同维护这部分数据.
分片方案
(1) 范围分片
分片需要将不同 key 映射到不同 Redis 实例上存储, 所以 key 的映射规则需要制定一个算法, 最简单的一个分片方案应该是范围分片. 范围分片理解起来很简单, 比如我们存储用户基本信息, 我们制定一个算法将用户 user_id 从 0 到 1000 映射到实例 A,user_id 从 1000 到 2000 映射到实例 B, 以此类推. 这个方案很轻松可以使用, 但是引发了一个问题: 我们需要维护 user_id 范围和映射实例之间的关系. 而正是这个问题导致范围分片虽然简单, 但是效率比其他分片方案低效许多, 所以 Redis 中一般不会使用范围分片作为分片方案.
(2) 哈希分片
比如我们目前有四个 Redis 实例, 我们需要存储一个 key. 我们可以通过哈希函数 crc32() 将 key 名转换成一个长整型数字, 然后对长整型数字对 4 取余, 就可以得到映射的实例. 但是这种分配方案一样存在弊端: 当我们需要增加或移除 Redis 实例时, 就会造成大量 key 无法被命中. 所以这时候出现了一种哈希分片的高级形式 -- 一致性哈希. 一致性哈希有三大特性:
key 哈希结果尽可能分配到不同 Redis 实例.
当实例增加或移除, 需要保护已映射的内容不会重新被分配到新实例上.
对 key 的哈希应尽量避免重复.
但是在 Redis 中没有使用一致性哈希这个概念, 而是引入了哈希槽. 在 Redis 集群中共有 16384 个哈希槽, 然后每个 key 通过哈希函数 crc16() 将 key 名转化成一个长整型数字再对 16384 取余, 最终决定这个 key 存储的哈希槽. 而每个 Redis 实例负责维护一部分哈希槽, 所有实例共同维护所有的哈希槽. 使用哈希槽最显而易见的特点就是 Redis 实例的增加或者移除很方便, 而不需要暂停所有 Redis 实例服务.
分片实现
上一篇谈到主从切换的哨兵模式已经提到, 哨兵模式可以实现高可用以及读写分离, 但是缺点在于所有 Redis 实例存储的数据全部一致, 所以 Redis 支持 cluster 模式, 可以简单将 cluster 理解为 Redis 集群管理的一个插件, 通过它可以实现 Redis 的分布式存储.
数据分片方式一般有三种: 客户端分片, 代理分片和服务器分片.
客户端分片
定义: 客户端自己计算 key 需要映射到哪一个 Redis 实例.
优点: 客户端分片最明显的好处在于降低了集群的复杂度, 而服务器之间没有任何关联性, 数据分片由客户端来负责实现.
缺点: 客户端实现分片则客户端需要知道当前集群下不同 Redis 实例的信息, 当新增 Redis 实例时需要支持动态分片, 多数 Redis 需要重启才能实现该功能.
代理分片
定义: 客户端将请求发送到代理, 代理通过计算得到需要映射的集群实例信息, 然后将客户端的请求转发到对应的集群实例上, 然后返回响应给客户端.
优点: 降低了客户端的复杂度, 客户端不用关心后端 Redis 实例的状态信息.
缺点: 多了一个中间分发环节, 所以对性能有些取的损失.
服务器分片
定义: 客户端可以和集群中任意 Redis 实例通信, 当客户端访问某个实例时, 服务器进行计算 key 应该映射到哪个具体的 Redis 实例中存储, 如果映射的实例不是当前实例, 则该实例主动引导客户端去对应实例对 key 进行操作. 这其实是一个重定向的过程. 这个过程不是从当前 Redis 实例转发到对应的 Redis 实例, 而是客户端收到服务器通知具体映射的 Redis 实例重定向到映射的实例中. 当前还不能完全适用于生产环境.
优点: 支持高可用, 任意实例都有主从, 主挂了从会自动接管.
缺点: 需要客户端语言实现服务器集群协议, 但是目前大多数语言都有其客户端实现版本.
预分片
从上面可以清楚地看出, 分片机制增加或移除实例是非常麻烦的一件事, 所以我们可以考虑一开始就开启 32 个节点实例, 当我们可以新增 Redis 服务器时, 我们可以将一半的节点移动到新的 Redis 服务器. 这样我们只需要在新服务器启动一个空节点, 然后移动数据, 配置新节点为源节点的从节点, 然后更新被移动节点的 ip 信息, 然后向新服务器发送 slaveof 命令关闭主从配置, 最后关闭旧服务器不需要使用的实例并且重新启动客户端. 这样我们就可以在几乎不需要停机时间时完成数据的移动.
分片机制的缺点
分片是由多台 Redis 实例共同运转, 所以如果其中一个 Redis 实例宕机, 则整个分片都将无法使用, 所以分片机制无法实现高可用.
如果有不同的 key 映射到不同的 Redis 实例, 这时候不能对这两个 key 做交集或者使用事务.
使用分片机制因为涉及多实例, 数据处理比较复杂.
分片中对于实例的添加或删除会很复杂, 不过可以使用预分片技术进行改善.
来源: https://www.cnblogs.com/niyueling/p/11655787.html