Redis cluster简介

为了避免redis单点故障,提高redis的读写性能,redis官方提供了一种集群方案redis cluster。它的特点是:

  • 高性能:做了数据分片,可由多个节点提供读写功能
  • 高可靠性:每个主节点都有从节点备份,可做主从转换
  • 高可扩展:可水平扩展,将数据重新分片即可

架构:
Redis cluster简介_第1张图片
Cluster将根据一致性哈希规则将数据分片,每个master放置一部分数据,slave从master同步数据,当master挂掉,slave切换为master。Master之间通过心跳交换元数据如slot信息,节点健康信息。每个redis均需开通两个端口,一个对外提供服务,另一个+10000,用来集群间通过gossip协议相互通讯。该通讯协议是一种二进制协议,节点间可高效交换数据,占用更少的网络带宽及处理时间。

客户端访问cluster:

  1. 客户端访问集群的某一节点
  2. 节点判断该key的slot是否属于自己

    • 如果属于,返回数据
    • 如果不属于,返回MOVED重定向至所在节点

数据分片:

  • 简单的hash算法

按key计算hash后对节点个数取余,按余数落到对应机器
c = hash(key)/n。当增加或缩减集群节点时,会有大量key对应的节点变化,缓存失效,造成缓存击穿。

  • 一致性hash

将key的hash值映射到一个圆环,落在顺时针最近的节点。当某个节点挂掉后,只需迁移该节点的数据至顺时针下一个节点即可。如果增加节点,将原先属于一个节点的数据分成两部分即可。节点的扩缩容只需迁移一个节点的数据即可,大部分数据不受影响。
c –> hash(key)/n
问题:数据分配不均匀,有可能有的节点数据很多,有的很少。

  • 虚拟hash槽

Cluster采用虚拟hash槽,slot=crc16(key)%16383,每个节点维护一定槽位和槽位对应的键值。虚拟hash槽将圆环分成了更加细小的16383份,用户可自行维护槽位和节点的关系,内存多的节点多分配。使数据的落位更加均匀,避免某一节点的数据过多。节点的扩缩容是槽位的重新分配,只影响槽位对应的数据。
Redis cluster简介_第2张图片
优点:

  • 解耦数据和节点的关系,降低了扩缩容的难度及影响性
  • 集群维护槽位与数据的关系,无需客户端或中间件代理

Cluster可在不影响对外提供服务的情况下进行扩缩容,槽是cluster维护数据的基本单位,集群的扩缩容是槽位的重新分配,cluster不会自动分配,需要手动维护。
客户端指定节点操作数据
Redis 计算槽时并非只简单的计算键值内容,当键值内容包括大括号时,则只计算括号内的内容。比如key为 user:{10000}:books时,计算哈希值只计算10000
当所有槽位均分配完毕后,集群才可对外提供服务

节点间的通讯:
采用gossip协议
Redis cluster将数据分片存储,但集群元信息通过节点间相互通信,集群内所有节点的元信息是一样的。

  • 分散存储元信息特点:ap优先,保证每个节点都有元信息,数据的更新有延迟,保证最终一致。
  • 集中式存储元信息特点:cp优先,各节点会很快感知元数据的变化。但是读写都在一个地方,存储会有压力。
  • 当节点数很多时,集中式存储可能会有读写的压力,而分散存储会有数据不一致的问题。

Cluster各节点通过+10000端口发送ping信息,其他节点收到后回复pong。交换节点信息,slot信息。

消息种类:

  • Meet:新节点发送meet给集群的某一结点,即加入集群,新节点会与其他节点通讯。Cluster初始化时会meet其他节点
  • Ping:每个节点给其他节点发送ping信息以交换元信息,每次会选择5个最久没有通信的其他节点,每次ping带上自己节点和1/10其他节点的信息,最少3个,最多all-2个。
  • Pong:对ping和meet的响应,包含自己的状态
  • Fail:判断某个节点宕机

Smart客户端 jediscluster
由于cluster会返回moved重定向以及该slot所属的正确的节点信息,需要客户端重新发请求获取数据,因此需要smart客户端。smart客户端功能:

  • 缓存并维护slot和节点对应关系
  • 如果服务端slot更新,处理moved/ask重定向逻辑

客户端查询流程:

  • 客户端根据本地缓存slot信息发往对应节点,如果数据存在直接返回结果
  • 如果返回moved错误,更新本地slot映射信息,发往新的节点

在我们项目的使用:

  • 没有用spring-data-redis,封装了service层,针对常用操作实现了get,set,delete,hasKey,hget,hset,hdelete,lpush,rpop,ttl,publish等方法
  • 实现了分布式锁,分布式计数器,get后立即失效,集群环境下执行lua脚本的命令

    • 分布式锁:如果key在redis中存在,则获取失败返回false,否则设置该key及失效时间,返回成功。Redis操作为单线程,判断key是否存在及设置key应为一个原子操作,通过lua脚本实现。在分布式定时任务中抢锁执行定时任务有用到,定时任务在多个服务中启用,但同时只有抢到锁的那个服务能调用成功,保证服务的冗余及同一时刻只有一个节点执行定时任务。
    • 分布式计数器:和分布式锁类似,如果key在redis中存在,则+1,否则设为1。该过程为原子操作。用于接口限流。
    • get后立即失效:和分布式锁类似,如果key在redis中存在,则删除,返回true,否则返回false。用于用户登录时校验图片验证码,校验后验证码立即失效,提高脚本进攻的难度。
    • lua脚本执行方法。由于在集群环境下,key存储在不同节点,为确保lua脚本是一个原子操作,需要不同的key在同一节点执行。即lua脚本中的每个key前添加同一{xx},cluster分片时会按照xx进行hash分槽。
  • 支持同时连接多个cluster,根据dbkey指定集群。为了将缓存隔离,搭建了多个redis集群,配置文件中通过dbkey区分不同的集群。调用redisService方法时如果不指定dbkey则操作默认cluster,如果传入dbkey参数则操作指定cluster
  • 可按cluster添加统一前缀。由于测试环境分为sit,qa,uat,希望使用同一套cluster,cluster没有单机db的概念,因此在配置cluster时,统一添加前缀sit,qa,uat进行区分,调用redisService方法时自动添加。
  • 支持配置密码。可连接配置和不配置密码的cluster,由配置文件决定

你可能感兴趣的