Redis数据增多了,是该加内存还是加实例?
但是 , 在使用的过程中 , 我发现 , Redis的响应有时会非常慢 。 后来 , 我们使用INFO命令查看Redis的latest_fork_usec指标值(表示最近一次fork的耗时) , 结果显示这个指标值特别高 , 快到秒级别了 。
这跟Redis的持久化机制有关系 。 在使用RDB进行持久化时 , Redis会fork子进程来完成 , fork操作的用时和Redis的数据量是正相关的 , 而fork在执行时会阻塞主线程 。 数据量越大 , fork操作造成的主线程阻塞的时间越长 。 所以 , 在使用RDB对25GB的数据进行持久化时 , 数据量较大 , 后台运行的子进程在fork创建时阻塞了主线程 , 于是就导致Redis响应变慢了 。
看来 , 第一个方案显然是不可行的 , 我们必须要寻找其他的方案 。 这个时候 , 我们注意到了Redis的切片集群 。 虽然组建切片集群比较麻烦 , 但是它可以保存大量数据 , 而且对Redis主线程的阻塞影响较小 。
【Redis数据增多了,是该加内存还是加实例?】切片集群 , 也叫分片集群 , 就是指启动多个Redis实例组成一个集群 , 然后按照一定的规则 , 把收到的数据划分成多份 , 每一份用一个实例来保存 。 回到我们刚刚的场景中 , 如果把25GB的数据平均分成5份(当然 , 也可以不做均分) , 使用5个实例来保存 , 每个实例只需要保存5GB数据 。 如下图所示:
如何保存更多数据?
在刚刚的案例里 , 为了保存大量数据 , 我们使用了大内存云主机和切片集群两种方法 。 实际上 , 这两种方法分别对应着Redis应对数据量增多的两种方案:纵向扩展(scaleup)和横向扩展(scaleout) 。
纵向扩展:升级单个Redis实例的资源配置 , 包括增加内存容量、增加磁盘容量、使用更高配置的CPU 。 就像下图中 , 原来的实例内存是8GB , 硬盘是50GB , 纵向扩展后 , 内存增加到24GB , 磁盘增加到150GB 。 横向扩展:横向增加当前Redis实例的个数 , 就像下图中 , 原来使用1个8GB内存、50GB磁盘的实例 , 现在使用三个相同配置的实例 。
第一个问题是 , 当使用RDB对数据进行持久化时 , 如果数据量增加 , 需要的内存也会增加 , 主线程fork子进程时就可能会阻塞(比如刚刚的例子中的情况) 。 不过 , 如果你不要求持久化保存Redis数据 , 那么 , 纵向扩展会是一个不错的选择 。
不过 , 这时 , 你还要面对第二个问题:纵向扩展会受到硬件和成本的限制 。 这很容易理解 , 毕竟 , 把内存从32GB扩展到64GB还算容易 , 但是 , 要想扩充到1TB , 就会面临硬件容量和成本上的限制了 。
与纵向扩展相比 , 横向扩展是一个扩展性更好的方案 。 这是因为 , 要想保存更多的数据 , 采用这种方案的话 , 只用增加Redis的实例个数就行了 , 不用担心单个实例的硬件和成本限制 。 在面向百万、千万级别的用户规模时 , 横向扩展的Redis切片集群会是一个非常好的选择 。
不过 , 在只使用单个实例的时候 , 数据存在哪儿 , 客户端访问哪儿 , 都是非常明确的 , 但是 , 切片集群不可避免地涉及到多个实例的分布式管理问题 。 要想把切片集群用起来 , 我们就需要解决两大问题:
数据切片后 , 在多个实例之间如何分布?客户端怎么确定想要访问的数据在哪个实例上?接下来 , 我们就一个个地解决 。
数据切片和实例的对应分布关系在切片集群中 , 数据需要分布在不同实例上 , 那么 , 数据和实例之间如何对应呢?这就和接下来我要讲的RedisCluster方案有关了 。 不过 , 我们要先弄明白切片集群和RedisCluster的联系与区别 。
实际上 , 切片集群是一种保存大量数据的通用机制 , 这个机制可以有不同的实现方案 。 在Redis3.0之前 , 官方并没有针对切片集群提供具体的方案 。 从3.0开始 , 官方提供了一个名为RedisCluster的方案 , 用于实现切片集群 。 RedisCluster方案中就规定了数据和实例的对应规则 。
具体来说 , RedisCluster方案采用哈希槽(HashSlot , 接下来我会直接称之为Slot) , 来处理数据和实例之间的映射关系 。 在RedisCluster方案中 , 一个切片集群共有16384个哈希槽 , 这些哈希槽类似于数据分区 , 每个键值对都会根据它的key , 被映射到一个哈希槽中 。
具体的映射过程分为两大步:
首先根据键值对的key , 按照CRC16算法计算一个16bit的值;然后 , 再用这个16bit值对16384取模 , 得到0~16383范围内的模数 , 每个模数代表一个相应编号的哈希槽 。 关于CRC16算法,如果感兴趣!可以自行Googel查询那么 , 这些哈希槽又是如何被映射到具体的Redis实例上的呢?
我们在部署RedisCluster方案时 , 可以使用clustercreate命令创建集群 , 此时 , Redis会自动把这些槽平均分布在集群实例上 。 例如 , 如果集群中有N个实例 , 那么 , 每个实例上的槽个数为16384/N个 。
当然 , 我们也可以使用clustermeet命令手动建立实例间的连接 , 形成集群 , 再使用clusteraddslots命令 , 指定每个实例上的哈希槽个数 。
客户端如何定位数据?在定位键值对数据时 , 它所处的哈希槽是可以通过计算得到的 , 这个计算可以在客户端发送请求时来执行 。 但是 , 要进一步定位到实例 , 还需要知道哈希槽分布在哪个实例上 。
一般来说 , 客户端和集群实例建立连接后 , 实例就会把哈希槽的分配信息发给客户端 。 但是 , 在集群刚刚创建的时候 , 每个实例只知道自己被分配了哪些哈希槽 , 是不知道其他实例拥有的哈希槽信息的 。
那么 , 客户端为什么可以在访问任何一个实例时 , 都能获得所有的哈希槽信息呢?这是因为 , Redis实例会把自己的哈希槽信息发给和它相连接的其它实例 , 来完成哈希槽分配信息的扩散 。 当实例之间相互连接后 , 每个实例就有所有哈希槽的映射关系了 。
客户端收到哈希槽信息后 , 会把哈希槽信息缓存在本地 。 当客户端请求键值对时 , 会先计算键所对应的哈希槽 , 然后就可以给相应的实例发送请求了 。
总结上述讲述切片集群在保存大量数据方面的优势 , 以及基于哈希槽的数据分布机制和客户端定位键值对的方法
在应对数据量扩容时 , 虽然增加内存这种纵向扩展的方法简单直接 , 但是会造成数据库的内存过大 , 导致性能变慢 。 Redis切片集群提供了横向扩展的模式 , 也就是使用多个实例 , 并给每个实例配置一定数量的哈希槽 , 数据可以通过键的哈希值映射到哈希槽 , 再通过哈希槽分散保存到不同的实例上 。 这样做的好处是扩展性好 , 不管有多少数据 , 切片集群都能应对 。
推荐阅读
- 甲状腺癌|夜间灯光会使甲状腺癌风险增加55%?
- 猪蹄肥而不腻,能够给粥带来肉香,满满的胶原蛋白,幸福感倍增
- 丁道祥|安徽省马鞍山市雨山区:中马磁能:小试车间发挥增效大作用
- 卡瑞利珠单抗|2021 ELCC|春风得意马蹄疾:肺鳞癌一线治疗两大研究数据出炉
- 鸡蛋和这肉是绝配,吃多也不胖,减脂又增肌
- 怕长肉,一定多吃这道菜,润肠排毒,增强免疫力,越吃越苗条
- 冬天用它包饺子再适合不过,5毛钱一斤,鲜嫩美味增强抵抗力
- ebmt|2021 EBMT大会研究进展精选,这些数据你了解吗?
- 卤水常用的7种辣椒,来认识一下,去异增香提辣,用对了事半功倍
- 隔三差五就吃的“开胃菜”,健脾开胃,促进食欲,让孩子食欲大增
