想要知道分区的目的,首先我们要知道如果是单实例的话,会出现什么问题:
上述截图是简约版的主从部署架构(一主两从)。如果项目中部署了主从架构,从一定程度上可以解决压力大,单点故障的问题。
但是主从架构其实是将数据全量的备份到slave节点,所以相同的数据会在三个节点同时出现。
这种主从架构并没有解决内存容量有限
的问题
但是实际生产环境中并非一定要将数据添加到一台Redis中,我们可以部署多台Redis实例,将不同的数据分散打入到不同的Redis中,其实这就是我们所说的分区
。 那就让我们一步一步来看下分区的演进:
如果数据之间关联不大的话,我们可以通过在client
端手动干预,将不同的数据手动打入不同的Redis实例中,通过这种方式来完成数据的分区
缺点:
我们可以通过上述的hash + 取模
来实现数据的分区,其实也是需要client端
进行干预
缺点:
rehash
操作上述截图中,多个Redis实例中都存在相同的keyooxx
. 当请求发送过来的时候利用API lpush
向Redis中推送数。随机保存到不同实例的相同key中。另一端使用rpop
来进行消费。
有点类似于消息队列的性质,先进先消费。在高并发
下将内容保存到多台实例中,再以一定速度去消费
缺点:
random
,所以随机性太大,从一端无法获取到数据其实此方案也算是相对权威的方案了。但是讲述此方法之前我们先来回顾下上述的方案有什么不足:
取模
运算就无法定位到存在数据的实例了分布式
扩展一致性Hash
我们先来看下,一致性Hash的官方定义,无非是解决:分布式缓存的问题
。
如果没有一致性Hash
应该怎么做呢???
我们分布式缓存的目的之一:就是为了解决服务器中缓存不足的问题,我们需要将不同的数据分散到不同的服务器上。那我们怎么能平均的分散的不同的服务器上呢??? 取模运算
必不可少。一旦牵扯到固定值模数
。弊端就暴露出来了。
因为每次读取缓存/ 更新缓存,都需要通过hash + 取模
来确定哪台服务器。 但是如果服务器的个数发生变化,那么通过计算的结果都不正确了,可以理解为间接导致缓存失效。
我们一致性Hash算法
就是在增加/ 减少 服务器时,能够尽可能小得改变已存在的服务请求 以及处理请求服务之间的映射关系
一致性Hash
讲解实例可以先看下 上述讲述的官方定义
后,再来看接下来的内容:
从虚拟的角度来讲的话,可以将一致性Hash
理解为一个闭环。这个环的长度是.
而我们获取服务器的node(这里的node,可以是IP,也可以是服务器的id。 保存唯一就行
) 通过hash运算 映射到Hash环
中的一个位置。说到这里就跟取模
没什么关系了
如果我们想存储数据,数据的基础结构一般是:key/ vaue
. 我们可以拿到key,经过hash运算,再次拿到闭环中一个对应的值,此值在闭环中不一定有内容(有内容指的是:通过IP(node) 映射到该节点
)。但是我们可以向前找,找到被映射到的节点,从该节点对应的服务器中进行 增删改查。如下图所示:
如果说此时有新服务器加入怎么办呢???
会出现如上图所示:
有可能会添加一个服务器p3
. 还是使用原来的key 进行查询 以及添加。 如果向上找的时候,可能会找不到数据。但是最起码不会影响到别的数据,所以使用一致性Hash
也是存在一定问题
优点:增加/ 减少服务器 不会影响到别的数据(其实这个就是,一致性Hash的目的
)。
缺点:如果增加服务器后,可能会出现部分数据查询不到。
综合上述而言,这种方式更倾向于做缓存
,而不是数据库
如上述截图所示:
如果高并发
的情况下,每台client都会连接Redis Server。连接成本是非常高的
其实我们可以在client
以及Redis Server
端之间,添加一个代理层。
其实代理层就是一个转发的过程。 我们只要关注代理层的性能就可以了
我们上述讨论的方案都是基于现有Redis实例,进行分区。那什么叫
预分区
呢
比如:此时公司的Redis服务器是3台,但是会随着业务的不断扩大,同时增加Redis服务器,最大可以部署到10台。那我们就可以基于10台服务器进行预分区,而不是真实的3台服务器
其实10台服务器的预分区
跟部署3台服务器,从本质来说没有任何变化,只不过需要添加映射关系,怎么理解呢???
因为我们的最大部署实例是10台,但是目前只有3台服务器。
我们可以将计算Hash结果进行10的取模运算
. 通过一定的映射关系来找到具体的Redis实例。比如:我们规定,如果是0~2
都会放到第一台服务器中,5~7
放到第二台服务器中,以此类推。
如果此时服务器增加到了4台,我们可以重新调整mapping 映射
关系。如下图:
Mapping 映射关系
但是即使是使用这种方案,我们还是需要一个代理层的角色,来规定数据到底会流向哪个服务器
所以我们出现了一种新的方案,可以避免使用代理层:
我们完全可以将代理层的角色放到每个Redis实例中,从实例内部告诉我们,数据是否存在于该实例,如上述截图中的1, 2, 3步
k1
被推入到Redis实例2中, 由Redis内部来判断该key 是否属于该实例实例3
). 此时步骤其实就是上述第二步实例3
后,再对k1的数据进行操作。其实上述就是数据分治的过程。但是存在一个问题:聚合操作难以实现
。例如:事务。因为每次在编辑key
的时候,不一定会修改哪个实例的数据。
但是我们可以手动人为的控制,例如下图:我们可以通过给key
添加共同的前缀,能让不同的key落到同一个实例上,而从实现事务等 聚合操作。
上述的方式是Hash槽位
的一种实现,虽然此时我们的Redis实例是3天,但是将来最大是10台,那么我们就可以按照10台实例进行预分区。
其实Redis cluster集群的数据分片也是相同的原理。只不过是槽位
更多,原文可以参照
为了每个集群节点 创建一个配置文件,配置文件内容如下:
vi redis-7001.conf
port 7001
cluster-enabled yes
cluster-config-file nodes-7001.conf
cluster-node-timeout 15000
daemonize yes
protected-mode no
pidfile /var/run/redis_7001.pid
需要几个节点,就将上述配置文件赋值几份。修改每个配置文件的端口
使用此命令:%s/7001/7002/g
修改
```bash
#!/bin/bash
for i in {1..6}
do
./redis-server "redis-700${i}.conf"
done
## 5.4 关联集群
```shell
./redis-cli --cluster create 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006 --cluster-replicas 1