软件外包平台的服务商,专业放心关键词优化参考价格,小程序定制开发解决方案,网络营销推广案例分析切片集群
我曾遇到过这么一个需求#xff1a;要用 Redis 保存 5000 万个键值对#xff0c;每个键值对大约是 512B#xff0c;为了能快速部署并对外提供服务#xff0c;我们采用云主机来运行 Redis 实例#xff0c;那么#xff0c;该如何选择云主机的内存容量呢#xff…切片集群
我曾遇到过这么一个需求要用 Redis 保存 5000 万个键值对每个键值对大约是 512B为了能快速部署并对外提供服务我们采用云主机来运行 Redis 实例那么该如何选择云主机的内存容量呢
在使用的过程中我发现Redis 的响应有时会非常慢。后来我们使用 INFO 命令查看 Redis 的 latest_fork_usec 指标值表示最近一次 fork 的耗时结果显示这个指标值特别高快到秒级别了。这跟 Redis 的持久化机制有关系。在使用 RDB 进行持久化时Redis 会 fork 子进程来完成fork 操作的用时和 Redis 的数据量是正相关的而 fork 在执行时会阻塞主线程。数据量越大fork 操作造成的主线程阻塞的时间越长。所以在使用 RDB 对 25GB 的数据进行持久化时数据量较大后台运行的子进程在 fork 创建时阻塞了主线程于是就导致 Redis 响应变慢了。 这个时候我们注意到了 Redis 的切片集群。虽然组建切片集群比较麻烦但是它可以保存大量数据而且对 Redis 主线程的阻塞影响较小。 切片集群也叫分片集群就是指启动多个 Redis 实例组成一个集群然后按照一定的规则把收到的数据划分成多份每一份用一个实例来保存
在切片集群中实例在为 5GB 数据生成 RDB 时数据量就小了很多fork 子进程一般不会给主线程带来较长时间的阻塞。采用多个实例保存数据切片后我们既能保存 25GB 数据又避免了 fork 子进程阻塞主线程而导致的响应突然变慢。在实际应用 Redis 时随着用户或业务规模的扩展保存大量数据的情况通常是无法避免的。而切片集群就是一个非常好的解决方案。这节课我们就来学习一下
Redis 数据量增多处理
Redis 应对数据量增多的两种方案纵向扩展scale up和横向扩展scale out 纵向扩展升级单个 Redis 实例的资源配置包括增加内存容量、增加磁盘容量、使用更高配置的 CPU。就像下图中原来的实例内存是 8GB硬盘是 50GB纵向扩展后内存增加到 24GB磁盘增加到 150GB。
横向扩展横向增加当前 Redis 实例的个数就像下图中原来使用 1 个 8GB 内存、50GB 磁盘的实例现在使用三个相同配置的实例。 纵向扩展好处和缺点 首先纵向扩展的好处是实施起来简单、直接。不过这个方案也面临两个潜在的问题。第一个问题是当使用 RDB 对数据进行持久化时如果数据量增加需要的内存也会增加主线程 fork 子进程时就可能会阻塞比如刚刚的例子中的情况。不过如果你不要求持久化保存 Redis 数据那么纵向扩展会是一个不错的选择。 纵向扩展好处和缺点 这时你还要面对第二个问题纵向扩展会受到硬件和成本的限制。这很容易理解毕竟把内存从 32GB 扩展到 64GB 还算容易但是要想扩充到 1TB就会面临硬件容量和成本上的限制了。在面向百万、千万级别的用户规模时横向扩展的 Redis 切片集群会是一个非常好的选择
数据切片后在多个实例之间如何分布
客户端怎么确定想要访问的数据在哪个实例上接下来我们就一个个地解决
Redis Cluster 集群 哈希槽
从 3.0 开始官方提供了一个名为 Redis Cluster 的方案用于实现切片集群。Redis Cluster 方案中就规定了数据和实例的对应规则。 具体来说Redis Cluster 方案采用哈希槽Hash Slot接下来我会直接称之为 Slot来处理数据和实例之间的映射关系。在 Redis Cluster 方案中一个切片集群共有 16384 个哈希槽这些哈希槽类似于数据分区每个键值对都会根据它的 key被映射到一个哈希槽中。
首先根据键值对的 key按照CRC16 算法计算一个 16 bit 的值然后再用这个 16bit 值对 16384 取模得到 0~16383 范围内的模数每个模数代表一个相应编号的哈希槽。
我们在部署 Redis Cluster 方案时可以使用 cluster create 命令创建集群此时Redis 会自动把这些槽平均分布在集群实例上。例如如果集群中有 N 个实例那么每个实例上的槽个数为 16384/N 个。 当然 我们也可以使用 cluster meet 命令手动建立实例间的连接形成集群再使用 cluster addslots 命令指定每个实例上的哈希槽个数。
在手动分配哈希槽时需要把 16384 个槽都分配完否则 Redis 集群无法正常工作
假设集群中不同 Redis 实例的内存大小配置不一如果把哈希槽均分在各个实例上在保存相同数量的键值对时和内存大的实例相比内存小的实例就会有更大的容量压力。遇到这种情况时你可以根据不同实例的资源配置情况使用 cluster addslots 命令手动分配哈希槽。
示意图中的切片集群一共有 3 个实例同时假设有 5 个哈希槽我们首先可以通过下面的命令手动分配哈希槽实例 1 保存哈希槽 0 和 1实例 2 保存哈希槽 2 和 3实例 3 保存哈希槽 4。
redis-cli -h 172.16.19.3 –p 6379 cluster addslots 0,1
redis-cli -h 172.16.19.4 –p 6379 cluster addslots 2,3
redis-cli -h 172.16.19.5 –p 6379 cluster addslots 4在集群运行的过程中key1 和 key2 计算完 CRC16 值后对哈希槽总个数 5 取模再根据各自的模数结果就可以被映射到对应的实例 1 和实例 3 上了
客户端如何定位数据
在定位键值对数据时它所处的哈希槽是可以通过计算得到的这个计算可以在客户端发送请求时来执行。但是要进一步定位到实例还需要知道哈希槽分布在哪个实例上。
一般来说客户端和集群实例建立连接后实例就会把哈希槽的分配信息发给客户端。但是在集群刚刚创建的时候每个实例只知道自己被分配了哪些哈希槽是不知道其他实例拥有的哈希槽信息的。
那么客户端为什么可以在访问任何一个实例时都能获得所有的哈希槽信息呢这是因为Redis 实例会把自己的哈希槽信息发给和它相连接的其它实例来完成哈希槽分配信息的扩散。当实例之间相互连接后每个实例就有所有哈希槽的映射关系了;
客户端收到哈希槽信息后会把哈希槽信息缓存在本地。当客户端请求键值对时会先计算键所对应的哈希槽然后就可以给相应的实例发送请求了。
Redis 集群hash在分配
实例和哈希槽的对应关系并不是一成不变的实例和哈希槽的对应关系并不是一成不变的最常见的变化有两个(由于负载均衡)
集群中实例有新增或删除Redis 需要重新分配哈希槽为了负载均衡Redis 需要把哈希槽在所有实例上重新分布一遍。
实例之间还可以通过相互传递消息获得最新的哈希槽分配信息但是客户端是无法主动感知这些变化的。这就会导致它缓存的分配信息和最新的分配信息就不一致了那该怎么办呢 Redis Cluster 方案提供了一种重定向机制所谓的“重定向”就是指客户端给一个实例发送数据读写操作时这个实例上并没有相应的数据客户端要再给一个新实例发送操作命令。
那客户端又是怎么知道重定向时的新实例的访问地址呢当客户端把一个键值对的操作请求发给一个实例时如果这个实例上并没有这个键值对映射的哈希槽那么这个实例就会给客户端返回下面的 MOVED 命令响应结果这个结果中就包含了新实例的访问地址
GET hello:key (error) MOVED 13320 172.16.19.5:6379 其中MOVED 命令表示客户端请求的键值对所在的哈希槽 13320实际是在 172.16.19.5 这个实例上。通过返回的 MOVED 命令就相当于把哈希槽所在的新实例的信息告诉给客户端了。这样一来客户端就可以直接和 172.16.19.5 连接并发送操作请求了。
在 Redis 3.0 之前Redis 官方并没有提供切片集群方案但是其实当时业界已经有了一些切片集群的方案例如基于客户端分区的 ShardedJedis基于代理的 Codis、Twemproxy 等。这些方案的应用早于 Redis Cluster 方案在支撑的集群实例规模、集群稳定性、客户端友好性方面也都有着各自的优势我会在后面的课程中专门和你聊聊这些方案的实现机制以及实践经验。这样一来当你再碰到业务发展带来的数据量巨大的难题时就可以根据这些方案的特点选择合适的方案实现切片集群以应对业务需求了。
hash链
原理简介
每个数据存储进来的时候要根据hash算法进行算值取余存入到对应的机器中。取数据的时候用同样的hash算法对key进行计算即可取出数据。
应用场景
Redis集群扩容或宕机缩减那么就需要进行全库数据的重洗hash取模的值调整。这样就比较耗费时间。所以该方案要预先估计一下自己公司业务的数据量多大服务器的存储能力多大然后考虑在扩容时所需的时间多久只要在允许的时间范围内能够完成重洗数据那就可以采取该方案。该方案的一个好处就是简单。存数据简单取数据简单理解容易。
hash环
原理简介
上面说的Hash链只经过了1次hash即把key hash到对应的机器编号。 而Hash环有2次Hash 1把所有机器编号hash到这个环上 2把key也hash到这个环上。然后在这个环上进行匹配看这个key和哪台机器匹配。这样每个机器负责对应段上的数据。
应用场景
当hash链性能满足不了公司业务数据量的时候就要采取该方案进行性能提升。 当服务器缩减时对应段数据向下游转移即可这样就不会影响到其他段服务器的数据。 当服务器扩容时对应服务器下游的服务器数据要进行重洗把部分数据转移到新扩容的服务器上即可。这样在查找时按照最新的hash算法取余即可取出对应的数据。 这相较于hash链进行全库冲洗还是节省了很多 也就是说hash链和hash环在新增服务器的时候都是要冲洗数据的只是hash链是全库冲洗算法复杂度是Nhash环是下游节点冲洗算法复杂度是1 那么会有道友问我直接上第二种方案不就得了。这显然是不行的。
原因 hash环是有大小的它的特点是把hash链首尾相连那么假设你公司业务只有百万级数据量你设置成一个hash环。假设hash环周长是100小厂有4台服务器第一次可以人为均匀分布到环上但是如果业务量数据增加导致需要扩容。这时候如果你对hash环进行扩周长如果不重新分配服务器在环上的位置那就会出现数据倾斜问题如果重新均匀分布服务器在环上的位置那么就要全库重洗数据。所以这样就和hash链没什么区别。还多出一个数据倾斜问题。
那么有道友就说了那我把hash环周长设置的超大。这样不就可以减轻扩容时数据倾斜问题的严重性了吗并不是这样当你保持环周长不变的前提下扩容的时候数据倾斜和环的周长并没有关系。只是和你扩容的服务器策略有关就是假设第一次设置4台服务器那么你扩容的服务器必须是2的N次方台这样才能人为的避免数据倾斜。那么你小厂有这个实力吗显然没有不划算。
另外你的环周长越大也就意味着取余的除数越大那么计算取余的时间就越久比如你对2取余口算即可。你对2的32次方取余那就要多用很多时间这样随着积累你浪费的时间就很多了。相对于公司业务数据量不大但是损耗的计算时间却很多那就很不划算了。所以环的周长也是要考虑的点。
所以使用hash环算法要考虑两点 1、公司自身实力每次扩容需要的服务器数据量是a*2^n。其中a是第一次均匀分布的服务器数据量n0的整数。这个扩容方法解决数据倾斜问题。 2、hash环周长大小选择。周长越大计算越耗费时间。所以要根据公司业务量大小选择合理的周长大小不能太小否则经常扩容不能太大浪漫每次的取余时间。 3、数据倾斜问题的本质就在于服务器节点在环上的分布是否均匀还是密集。分布密集了那就会出现数据倾斜问题。