如何保存更多的数据
为了保存更多的数据,通常使用大内存云主机和切片集群两种方法。实际上,这两种方法分别对应着redis应对数据量增多的两种方法:纵向扩展和横向扩展。
- 纵向扩展:升级单个redis实例的资源配置,包括增加内存容量、增加磁盘容量、使用更高配置的cpu。就像下图中,原来的实例内存是8GB,硬盘是50GB,纵向扩展后,内存增加到24GB,磁盘增加到150GB。
- 横向扩展:横向增加当前redis实例个数,就像下图中,原来使用1个8GB内存、50GB磁盘的实例,现在使用三个相同配置的实例。
那么,这两种方式的优缺点分别是什么呢?
首先,纵向扩展的好处是,实施起来简单、直接。不过该方案会面临两个潜在的问题。
第一个问题是,当使用RDB对数据进行持久化时,如果数据量增加,需要的内存也会增加,主线程fork子进程时就可能阻塞。不过,如果你不要求持久化保存redis数据,那么纵向扩展是一个不错的选择。
不过,这时,你还要面临第二个问题:纵向扩展会受到硬件和成本的限制。这很容易理解,毕竟,把内存从32G扩展到64GB还算容易,但是,要想扩展到1TB,就要面临硬件容量个成本上的限制。
与纵向扩展相比,横向扩展是一个扩展性更好的方案。这是因为,要想保存更多的数据,采用这种方案的话,只用增加redis的实例个数就行了,不用担心单个实例的硬件成本限制。在面向百万,千万级别的用户规模时,横向扩展redis切片集群会是一个非常好的选择。
不过在使用单个实例的时候,数据存放在哪儿,客户端访问哪儿,都是非常明确的,但是,切片集群不可避免地涉及到多个实例的分布式管理问题。要想把切片集群用起来,我们需要解决两大问题:
- 数据切片后,在多个实例之间如何分布?
- 客户端怎么确定想要访问的数据在哪个实例上?
接下来,一一解决。
数据切片和实例的对应分布关系
在切片集群中,数据需要分布在不同的实例上,那么数据和实例之间如何应对呢?这就和接下来将的redis cluster方案有关了。不过,我们首先要弄明白切片集群和redis cluster的联系与区别。
实际上,切片集群是一种保存大量数据的通用机制,这个机制可以有不同的实现方案。在redis3.0之前,官方并没有针对切片集群提供具体方案。从3.0开始,官方提供了一个名为redis cluster的方案,用于实现切片集群。redis cluster方案中就规定数据和实例的对应规则。
具体来说,redis cluster方案使用哈希槽(hash slot),来处理数据和实例之间的映射关系。在redis cluster方案中,一个切片集群共有16384个哈希槽,这些哈希槽类似于数据分区,每个键值对都会根据它的key,被映射到一个哈希槽中。
具体的映射过程分为两大步:首先根据键值对的key,按照CRC16算法计算一个16bit的值;然后,再用这个16bit的值对16384取模,得到0~16383范围内的模数,每个模数代表一个相应编号的哈希槽。
那么,这些哈希槽又是如何被映射到具体的redis实例上的呢?
我们在部署redis cluster方案时,可以使用cluster create命令创建集群,此时,redis会自动把这些槽平均分布到集群实例上。例如:如果集群有N个实例,那么,每个实例上的槽个数为16384/N
当然,我们也可以使用cluster meet 命令手动建立实例间的连接,形成集群,在使用 cluster addslots命令,指定每个实例上的哈希槽个数。
举一个例子,假设集群中不同redis实例的内存大小配置不一,如果把哈希槽均分在各个实例上,在保存相同数量的键值对时,和内存大的实例相比,内存小的实例就会有更大的容量压力。遇到这种情况时,可以根据不同实例的资源配置情况,使用cluster addslots命令手动分配哈希槽
示意图中的切片集群一共有三个实例,同时假设有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上了。
另外,一个小提醒 在手动分配哈希槽时,需要把16384个槽都分配完,否则redis无法正常工作
客户端如何数据定位
在定位键值对数据时,它所处的哈希槽是可以通过计算得到的,这个计算可以在客户端发送请求时来执行。但是,要进一步定位到实例,还需要知道哈希槽分布在哪个实例上。
一般来说,客户端和集群建立连接之后,实例就会把哈希槽的分配信息发送给客户端。但是,在集群当当创建的时候,每个实例只知道自己被分配了哪些哈希槽,是不知道其他实例拥有哈希槽的信息的。
那么客户端为什么可以在访问任何一个实例时,都能获得所有的哈希槽信息呢?这是因为redis实例会把自己的哈希槽信息发给和它相连的其他实例,来完成哈希槽分配信息的扩散。当实例之间相互连接后,每个实例就有所有哈希槽的映射关系了。
客户端收到哈希槽信息后,会把哈希槽信息缓存在本地。当客户端请求键值对时,会先计算键所对应的哈希槽,然后就可以给相应的实例发送请求了。
但是,集群中,实例和哈希槽的对应关系并不是一成不变的,最常见的变化有两个:
- 在集群中,实例有新增或者删除,redis需要重新分配哈希槽
- 为了负载均衡,redis需要把哈希槽在所有实例上重新分配一遍。
此时,实例之间还可以通过相互传递信息,获得最新的哈希槽分配信息,但是,客户端是无法主动感知这些变化的。这就会导致,它缓存的分配信息和最新的分配信息不一致了,那该怎么办呢?
redis cluster方案提供了一个重定向机制,所谓重定向,就是指,客户端给一个实例发送数据读写操作时,这个实例是并没有相应的数据,客户端要再给一个新实例发送操作命令。
那客户端又是怎么知道重定向时的新实例的访问地址呢?当客户端把一个键值对的操作请求发送给一个实例时,如果这个实例并没有这个键值对映射的哈希槽,那么这个实例就会给这个客户端返回下面的move命令响应结果,这个结果中就包含了新实例的访问地址。
GET hello:key
(error) MOVED 13320 172.16.19.5:6379
其中,moved命令表示,客户端请求的键值对所在的哈希槽13320,实际是在172.16.19.5这个实例上。通过返回的moved命令,就相当于把哈希槽所在的新实例信息告诉给客户端。这样一来,客户端就可以直接和172.16.19.5连接,并发操作请求了。
标签:redis,Redis,切片,cluster,实例,集群,哈希,客户端 From: https://www.cnblogs.com/cuipengchong/p/16737389.html