文章目录

  • 1:redis单机产生的问题和方案
  • 1:问题:
  • 2:解决方案
  • 2:利用集群(分布式)解决单节点/单主节点容量和压力问题-分片
  • 1:客户端分类存储
  • 2:当数据无法划分时利用算法分发数据
  • 1:利用算法加取模来分发数据(不用)
  • 2:利用random算法来计算(不用)
  • 3:利用hash环和虚拟节点来数据分片
  • 4:采用redis预分区的概念(推荐)
  • 5:在预分区的概念上假如代理,可以减少redis连接成本
  • 3:Redis集群概念
  • 1:什么是slots-预分区设置的插槽
  • 2:什么是集群
  • 3:不同的数据存放在不同的redis节点插槽中,不可实现聚合操作
  • 4:Redis自带集群设置
  • 1:制作集群6个节点
  • 1:环境说明
  • 2:复制六个redis节点
  • 3:启动六个节点
  • 4:将六个节点合成一个集群
  • 5:采用集群策略连接,设置数据会自动切换到相应的写主机
  • 6:通过 cluster nodes 命令查看集群信息
  • 2:在集群中录入值
  • 3:查询集群中的值
  • 4:故障恢复
  • 5:集群的Jedis开发
  • 5:使用redis代理-predixy
  • 6:predixy和其他代理的区别对比


1:redis单机产生的问题和方案

1:问题:

  • 单点故障
  • 单节点容量有限
  • 单节点访问压力,IO压力太大

2:解决方案

根据AFK(拓展立方体)理论从xyz轴来解决上述问题方案,实现

  • x:全量镜像来解决单点故障问题,例如主从复制
  • y:业务,功能,分模块存储,比如集群和分布式
  • z:按照模块数据优先级拆分

2:利用集群(分布式)解决单节点/单主节点容量和压力问题-分片

1:客户端分类存储

如果当前redis的数据是可以按照模块进行拆分的,那么我们可以在客户端使用的时候对不同模块的数据进行划分,例如:用户的数据放在redis1中,订单的数据放在redis2中

Redis的分布式集群怎么做 redis分布式和集群_redis

2:当数据无法划分时利用算法分发数据

1:利用算法加取模来分发数据(不用)

比如当数据准备存储时,在客户端进行hash计算,分局固定的模数来选择不同的redis。缺点是模数是固定的。而且增加节点时会损失或者一部分数据命中率降低,造成击穿。

Redis的分布式集群怎么做 redis分布式和集群_插槽_02

2:利用random算法来计算(不用)

此类算法本质上和上边的一样。弊端也一样

Redis的分布式集群怎么做 redis分布式和集群_集群_03

3:利用hash环和虚拟节点来数据分片

1:假设我们创建一个虚拟的hash环,这个环上一共100个节点
2:每个真实的redisnode是一个真实的节点。
3:当一个数据来到redis时,通过一定的算法,这个值得出来0-99的数值,看这个数值和哪个redisnode里的最近,那么我们就将此数据存放在那个redisnode
优点:

  • 你加节点,的确可以分担其他节点的压力,不会造成全局洗牌
    缺点:
  • 新增节点造成一小部分数据不能命中,造成击穿
  • 会造成数据倾斜问题,比如大量的数据计算出的hash值在0-30之间,全部存放在了redis1上,redis2上几乎没有数据

4:采用redis预分区的概念(推荐)

在上述hash环的理念上创建出预分区的概念,这也是目前redis在使用的方案,并且上述三个方案都会造成数据丢失,无法命中。不适合做数据库
1:假如我们设置模数为10即10个小分区。不同的redisnode节点一开始的时候就分好各自负责的小分区
2:当有数据要存储在redis中时,通过计算key值得到0-9直接的数值,那么计算的值在那个分区就放在那个分区。
3:如果key连接redis2想要存放之,但是redis2经过计算key发现该值应该在redis3中,那么就会告诉客户端分区位置,由客户端自行再去redis3存放
4:如果发生了之前说的数据倾斜的问题,那么可以适当的调整redis节点对应的模数,让数据均衡
总结:
1:这种方法可以很好的解决了之前说过的所有问题;
2:redis中采用的小分区为16384 个插槽(hash slot), 数据库中的每个键都属于这 16384 个插槽的其中一个,集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽, 其中 CRC16(key) 语句用于计算键 key 的 CRC16 校验和 。
3:上述【3】中说的经过计算告诉客户端该值在那个分区的方案,redis也已经实现了,可以自动转换

Redis的分布式集群怎么做 redis分布式和集群_Redis的分布式集群怎么做_04

5:在预分区的概念上假如代理,可以减少redis连接成本

在客户端和redis服务之间搭建一个代理层,代理层对客户端隐藏redis的所有实现细节。客户端只需要访问代理层的ip:port就可以了。

Redis的分布式集群怎么做 redis分布式和集群_Redis的分布式集群怎么做_05

3:Redis集群概念

1:什么是slots-预分区设置的插槽

一个 Redis 集群包含 16384 个插槽(hash slot), 数据库中的每个键都属于这 16384 个插槽的其中一个,
集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽, 其中 CRC16(key) 语句用于计算键 key 的 CRC16 校验和 。
集群中的每个节点负责处理一部分插槽。 举个例子, 如果一个集群可以有主节点, 其中:
节点 A 负责处理 0 号至 5460 号插槽。
节点 B 负责处理 5461 号至 10922 号插槽。
节点 C 负责处理 10923 号至 16383 号插槽。

2:什么是集群

Redis 集群实现了对Redis的水平扩容,即启动N个redis节点,将整个数据库分布存储在这N个节点中,每个节点存储总数据的1/N。
Redis 集群通过分区(partition)来提供一定程度的可用性(availability): 即使集群中有一部分节点失效或者无法进行通讯, 集群也可以继续处理命令请求。

  • redis是一个开源的key value存储系统,受到了广大互联网公司的青睐。redis3.0版本之前只支持单例模式,在3.0版本及以后才支持集群,我这里用的是redis3.0.0版本;
  • redis集群采用P2P模式,是完全去中心化的,不存在中心节点或者代理节点;
  • redis集群是没有统一的入口的,客户端(client)连接集群的时候连接集群中的任意节点(node)即可,集群内部的节点是相互通信的(PING-PONG机制),每个节点都是一个redis实例;
  • 为了实现集群的高可用,即判断节点是否健康(能否正常使用),redis-cluster有这么一个投票容错机制:如果集群中超过半数的节点投票认为某个节点挂了,那么这个节点就挂了(fail)。这是判断节点是否挂了的方法;
  • 那么如何判断集群是否挂了呢? -> 如果集群中任意一个节点挂了,而且该节点没有从节点(备份节点),那么这个集群就挂了。这是判断集群是否挂了的方法;
  • 那么为什么任意一个节点挂了(没有从节点)这个集群就挂了呢? -> 因为集群内置了16384个slot(哈希槽),并且把所有的物理节点映射到了这16384[0-16383]个slot上,或者说把这些slot均等的分配给了各个节点。当需要在Redis集群存放一个数据(key-value)时,redis会先对这个key进行crc16算法,然后得到一个结果。再把这个结果对16384进行求余,这个余数会对应[0-16383]其中一个槽,进而决定key-value存储到哪个节点中。所以一旦某个节点挂了,该节点对应的slot就无法使用,那么就会导致集群无法正常工作。
  • 综上所述,每个Redis集群理论上最多可以有16384个节点。

3:不同的数据存放在不同的redis节点插槽中,不可实现聚合操作

这种情况下,聚合查询(如list取交集)就难以实现。因为可能不同的数据放在了不同的节点。
redis并不解决数据移动问题,可以由调用者自行设计模方案,比如可能聚合处理的数据经过计算可以放在同一节点。
此时我们客户端可以通过设置HashTag标签{}值来让业务相关的key经过计算存放在一起的插槽中。
例如:
我们想要k1=5,k2=6 这两个值存放在一起,那么我们可以这样设置

set {oo}k1 5
set {oo}k2 6
这样经过计算,k1和k2就可以存放在同一个预分区;

4:Redis自带集群设置

1:制作集群6个节点

1:环境说明

  • Redis集群至少需要3个节点,因为投票容错机制要求超过半数节点认为某个节点挂了该节点才是挂了,所以2个节点无法构成集群。
  • 要保证集群的高可用,需要每个节点都有从节点,也就是备份节点,所以Redis集群至少需要6台服务器。因为我没有那么多服务器,也启动不了那么多虚拟机,所在这里搭建的是伪分布式集群,即一台服务器虚拟运行6个redis实例,修改端口号为(6379,6380,6381,6389,6390,6391),当然实际生产环境的Redis集群搭建和这里是一样的。

2:复制六个redis节点

1:单节点的redis删除持久化数据,6379,6380,6381,6389,6390,6391
2:复制六份redis.conf,名字未redis6379.conf…
3:对redisxxx.conf进行配置,增加如下参数

参数说明:
cluster-enabled yes 打开集群模式
cluster-config-file nodes-6379.conf 设定节点配置文件名
cluster-node-timeout 15000 设定节点失联时间,超过该时间(毫秒),集群自动进行主从切换。

完整配置:
include /home/bigdata/redis.conf
port 6379
pidfile “/var/run/redis_6379.pid”
dbfilename “dump6379.rdb”
dir “/home/bigdata/redis_cluster”
logfile “/home/bigdata/redis_cluster/redis_err_6379.log”
cluster-enabled yes
cluster-config-file nodes-6379.conf
cluster-node-timeout 15000

3:启动六个节点

在此一个个启动也行,写shell脚本启动也可以

Redis的分布式集群怎么做 redis分布式和集群_插槽_06

4:将六个节点合成一个集群

组合之前,请确保所有redis实例启动后,nodes-xxxx.conf文件都生成正常。

Redis的分布式集群怎么做 redis分布式和集群_Redis的分布式集群怎么做_07


执行命令:

>cd  /opt/redis-6.2.1/src

>redis-cli --cluster create --cluster-replicas 1 192.168.11.101:6379 192.168.11.101:6380 192.168.11.101:6381 192.168.11.101:6389 192.168.11.101:6390 192.168.11.101:6391
要搭建集群的话,需要使用一个工具(脚本文件),这个工具在redis解压文件的源代码里。因为这个工具是一个ruby脚本文件,所以这个工具的运行需要ruby的运行环境,因为redis6版本已经自动集成了这个环境,故在此不赘述
此处不要用127.0.0.1, 请用真实IP地址
–replicas 1 采用最简单的方式配置集群(一个从机),一台主机,一台从机,正好三组。

Redis的分布式集群怎么做 redis分布式和集群_集群_08

5:采用集群策略连接,设置数据会自动切换到相应的写主机

Redis的分布式集群怎么做 redis分布式和集群_插槽_09


注意:一定要加上-c,不然节点之间是无法自动跳转的!如下图可以看到,存储的数据(key-value)是均匀分配到不同的节点的:

6:通过 cluster nodes 命令查看集群信息

Redis的分布式集群怎么做 redis分布式和集群_插槽_10

2:在集群中录入值

  • 在redis-cli每次录入、查询键值,redis都会计算出该key应该送往的插槽,如果不是该客户端对应服务器的插槽,redis会报错,并告知应前往的redis实例地址和端口。
  • redis-cli客户端提供了 –c 参数实现自动重定向。
  • 如 redis-cli -c –p 6379 登入后,再录入、查询键值对可以自动重定向。
    不在一个slot下的键值,是不能使用mget,mset等多键操作。也不可以进行whtch,事务等操作

3:查询集群中的值

CLUSTER GETKEYSINSLOT 返回 count 个 slot 槽中的键。

Redis的分布式集群怎么做 redis分布式和集群_插槽_11

4:故障恢复

  1. 如果主节点下线,从节点自动生成主节点(自动哨兵)
  2. 主节点恢复后,主从关系会如何?主节点回来变成从机,并且以刚刚从机变主机的节点为主
  3. 如果所有某一段插槽的主从节点都宕掉,redis服务是否还能继续?
  • 如果某一段插槽的主从都挂掉,而cluster-require-full-coverage 为yes ,那么 ,整个集群都挂掉
  • 如果某一段插槽的主从都挂掉,而cluster-require-full-coverage 为no ,那么,该插槽数据全都不能使用,也无法存储。

5:集群的Jedis开发

即使连接的不是主机,集群会自动切换主机存储。主机写,从机读。
无中心化主从集群。无论从哪台主机写的数据,其他主机上都能读到数据

public class JedisClusterTest {
  public static void main(String[] args) { 
     Set<HostAndPort>set =new HashSet<HostAndPort>();
     set.add(new HostAndPort("192.168.31.211",6379));
     JedisCluster jedisCluster=new JedisCluster(set);
     jedisCluster.set("k1", "v1");
     System.out.println(jedisCluster.get("k1"));
  }
}

5:使用redis代理-predixy

使用redis代理-predixy

6:predixy和其他代理的区别对比

predixy和其他代理的分析对比