Redis 高可扩展

        对于数据库而言,可扩展一般有两种方式:横向扩展和纵向扩展,横向扩展又称水平扩展,指的是单机无法满足的时候使用多机来提升处理能力,纵向扩展又称垂直扩展,简单来说就是提升单机性能,如更快的CPU、更大的内存、性能更高的硬盘。 下表简单对比两种方式差别:

优点缺点
横向扩展

1. 基本不受数据规模限制,可以一直堆机器应对,扩展性强。

2. 系统鲁棒性(robust)强,即便部分机器故障,集群依然可以使用

1. 分布式多机管理复杂,部署难。

纵向扩展

1. 部署简单。

1. 受限于技术发展,单机性能存在上限。

2. 单点故障后系统不可用。

       

         Redis Cluster是Redis提供的分布式高可扩展解决方案。和单机版相比,Redis Cluster存在一些限制,比如有些同时操作多个key(跨slot)的命令无法执行,包括事务、lua脚本等等,此外,集群版本只能有一个数据库,单机版可以有多个。

        集群版Redis由多个Redis节点组成,每个节点负责一部分key,客户端访问key的时候,只需要访问对应的节点即可。在这个过程中有一些问题:

        1. 不同Redis节点之间怎么分配Key?

        2. 客户端如何感知到key在哪个节点?

        3. 节点有增删之后,Key的位置会怎么变化?

        对于第一个问题,Redis Cluster将每个key通过计算crc16得到一个16bit值,然后对16384取模得到一个槽位,每个节点都会负责一些槽位对应的key。这样就可以使得不同节点负责不同的key。此外,这个规则也限制了Redis Cluster最多支持16384个节点(每个节点可以有多个从机),官方文档建议最好使用1000个节点,节点过多会导致节点之间通信过多带点性能损耗。

        对于第二个问题,集群中每个Redis节点相互之间都会通信,告知对方自己当前的信息,以及自己所知道的其它节点的信息,这些信息包括节点的状态、所负责的槽位号等等,经过一段时间通信,每个节点都可以知道整个集群槽位号的分布信息。当客户端连接到任意一台节点时,会得到每个槽位号对应的节点地址映射表,通过计算key的槽位号,就能够得知对应负责的节点。

        对于第三个问题,当节点有增加的时候,这时候可能会从其它节点迁移部分数据过来,亦或者删除节点时,会将本节点所负责的槽位对应的数据迁移到其它节点上去。

        下面详细介绍下Redis集群的运行过程:

        在redis.conf配置文件中有个cluster-enabled配置,当配置为yes的时候,则以集群版的方式启动,否则则以单机版的方式启动。此外,也可以通过命令行的方式启动Redis集群:

#  这条命令创建了包含6个节点的Redis集群,每个节点有一个备份
redis-cli --cluster create 127.0.0.1:7000 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 \
--cluster-replicas 1

             以集群版方式启动后,也可以手动执行如下命令来添加节点:

# 添加节点
CLUSTER MEET ip port

        不同节点之间会使用gossip协议(这个协议主要用来传播信息)进行通信,告知对方我知道哪些节点等等,经过一段时间后,整个集群内的节点之间就都知道相互的信息了。

        节点启动之后,就需要进行槽位指派,可以使用如下命令:

#逐个槽位指派
CLUSTER ADDSLOTS slot [slot ...]

#范围指派
CLUSTER ADDSLOTSRANGE start-slot end-slot

        集群总共有16384个槽位,所有槽位必须分配OK后集群才能够正常运行。通过gossip协议,每个节点都能够知道每个槽位由谁负责,这样当客户端连接到任意一个节点,都能够拿到集群完整的槽位映射信息。

        当有节点增删的时候,需要将其负责的节点转移或者从其它节点迁入:

CLUSTER SETSLOT slot <IMPORTING node-id | MIGRATING node-id |
  NODE node-id | STABLE>

        当槽位号迁移完成的时候,客户端并不知晓,这时候仍会访问旧节点,此时,旧节点会返回Moved错误,错误中会携带槽位号以及新的节点地址,一般情况下,redis客户端会自动重发请求并更新自己本地的映射表。

GET x
-MOVED 3999 127.0.0.1:6381

        当槽位号正在迁移的时候,比如说迁移一半,这时候客户端访问旧节点某个已经迁移的key,此时节点会返回ASK错误,这时候客户端需要使用asking命令要求新客户端返回该key,和move响应处理不同,ask错误不会更新本地槽位映射,因为这时候迁移没完成,数据仍有可能在老节点。

GET x
(error) ASK 3999 127.0.0.1:6381

        Redis集群还有一个值得注意的地方就是它不支持跨slot命令,对于某些同时处理多个key的命令,比如lua脚本、事务、zinter、mset等等,如果key跨slot,会返回跨slot错误。

(error) CROSSSLOT Keys in request don't hash to the same slot

        当然,对于一些需要经常操作多个key的业务来说,Redis也提供了一个解决方案:hash tag 

 所谓 hash tag,就是通过在key 的定义里面加一个花括号,这样当计算这个key的crc16值的时候,只使用花括号里面的值来计算,比如下面几个例子,redis只使用第一个花括号,如果内容为空则使用这个key。

{user1000}.followers // 使用 user1000 计算hash

foo{}{bar}  // 使用整个key hash,因为第一个花括号内容为空

foo{{bar}}zap // 使用 {bar 计算hash

foo{bar}{zap} // 使用 bar 计算hash

         这样一来,即便操作多个key,只要他们的hash tag一样,就会落在同一个槽位里。

        集群版的redis也支持主从备份,每个节点可以拥有一个或多个从机,单机版使用哨兵集群来维护,集群版则使用主节点来判断。判断逻辑类似于哨兵,也是采用少数服从多数的原则。当某个主节点故障后,其它主节点为将其标记为PFAIL(Possible failure)状态,然后会和其它主节点沟通,如果超过半数以上节点都认为PFAIL,则将其标记为FAIL状态,并通知其它节点更新该状态。该FAIL节点的从机则开始故障转移并生主,这个过程中涉及到从机选举,即从多个从机中选择一个,从机会向主节点发出生主请求,拿到半数票数以上的从机会被选为主从机,这时则替换FAIL的主机并将自己的状态更新为主机。

       需要注意的是,集群版Redis并不保证数据不丢失,这一点和单机版一样。数据丢失原因有很多,比如数据备份使用异步复制,主从故障转移时,从节点可能并没有同步完成,又或者客户端连接到一个发生网络分区的主机,该主机还未检测到网络异常,此时的写入就有可能丢失。集群版Redis设计目标是提供高性能和高可扩展,数据的强一致性是无法保证的。

        以上就是Redis集群的介绍,核心部分就是hash slot,hash tag,槽位转移、故障恢复等等。

在实际使用过程中,接触最多的就是hash tag。之前我负责的一个业务因为初期使用了单机版,后来用户规模增长后,单机版内存扛不住,升级成了集群版,结果遇到了一些坑,比如我们使用zset保存用户的好友列表,有些场景需要查询两个用户的共同好友,当时使用ZINTERSTORE计算,结果升级后,这两个key因为跨slot无法使用带来业务也无法使用等等。

       

参考资料:

1. 《Redis设计与实现》黄健宏

2.  切片集群:数据增多了,是该加内存还是加实例?

3. Redis cluster specification | Redis  (官方文档:内容最详细)

4. Scaling with Redis Cluster | Redis

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值