一、Redis的分布式结构解读
首先redis采用去中心化的设计这个理解是不到位的。redis分布式的模式,具有主从和集群两种,redis社区的集群方案redis cluster采用的是去中心化设计。我们先看看redis的演化过程:
上图是标准的Redis主从模式,只有Master接收写入请求,并将写入的数据复制给一个或多个Slave,这就形成了良好的读写分离机制,多个Slave就可以分担读操作。所以redis主从是标准的分布式中心化思想。
由于redis的应用场景大多是极高并发的内存I/O,因此上图的主从模式下Master既要承担写入,又要承担对内各个节点复制,Master的资源消耗很大,而且随着slave节点越多,这个问题越明显,因此,redis又形成主从的一个变种形式:
上图是redis主从拓扑结构的一种树形结构,这个拓扑结构的好处在于Master不需要给无数多的slave节点进行复制数据了,交给处于下一层节点的Slave来处理。这样就能将Master的工作消耗尽量从复制中抽身。
可问题是像这种高并发的业务场景,Master始终是一个隐患,因为它承受着所有的写操作,一旦崩溃,若没有HA解决方案,集群整体就不可用了。因此redis社区推出的集群方案,其实就是解决主的压力,很自然地就考虑到使用集群的分布式无中心模式。
上图中,左边是集群的中心模式,右边就是redis cluster使用的无中心模式。
redis cluster一些细节:redis无中心采用虚拟槽概念,这是独立于物理节点的,往往很容易将这块混淆,虚拟槽有0~16383个,redis的数据进行key的hash计算(具体公式网上很多),确定这笔数据是进入哪个槽位,而物理节点负责哪些虚拟槽,这是由我们指定的。
例如:当1个G的数据按照一条条带有key的记录写入redis cluster的时候,那么集群的各个节点只要接受到数据,就计算此条记录应该归哪个槽哪个节点,归本节点就写入与槽位映射的数据,不归自己的,就反馈客户端真正需要写入的节点,客户端再向记录所属节点发起二次请求,这就完成了1个G的数据在集群中的分片。
我们先不论redis cluster更多的优劣问题,单从上面的演化可以看到redis的主从结构向cluster演化的过程,其实就是去中心的过程,就是为了让多客户端多业务请求并发性能可以得到更好负载。另外为了高可靠HA,每个节点也可以在演变成master/slave的主从模式部署,即便是主节点宕掉,salve也会顶替上来。HA的缺点是节点数量又增加了一倍。
redis与rocketmq最大的不同,redis更偏重在线联机业务的高并发处理,而后者是海量积压数据流的大吞吐接收和消费。因此其选择分布式架构的目的也不同。当然这不代表着一定是中心化就不适合高并发,例如LSM-Tree代表的oceanbase作为集中式处理的特点,就很好的做到了在线联机业务的高并发写入,以及高速的热点数据(最近时间)查找。
另外,因为redis cluster作为分布式中每个节点都是对等的,那么就一定会存在集群管理上的一致性风险,由于在生产环境中各种异常情况都很特别,就会导致不同节点对集群的认可状态不一致,所以这时候手动介入调整每个节点在集群中状态情况就会增多。
二、Kafka和RocketMQ的分布式解读
我们先看看比rocketmq更让人熟知的大师兄Kafka,解读一下Kafka集群的分布式特点。
Kafka的集群管理来自zookeeper集群,Broker的注册发现,Broker Controller的选举都是由zookeeper来协助完成,但是Controller其实也不在消息处理时做什么事情,只是在创建分区、分区再平衡等方面对其他节点做领导性工作。
Kafka真正起作用的还是分区leader和分区follower。例如:一个topic会被分成4个分区,3个副本,那么一共4*3=12个分区副本,若有4个broker,那么每个broker就会以一主两从,放置三个分区的形式均匀分布。
Kafka的分区关系就是上图这个通讯形式,生产者(Product)从任意节点获取Meta信息,找到broker中的leader分区副本,会向里面写分配好的数据,leader会向集群中其他broker的follower分区副本复制一份。
在这种分区结构关系下,其实每个broker都具有了topic分区数据请求访问以及副本复制的Master能力。所以你问我kafka是不是中心模式,下来再说。
我们再看看kafka的阿里兄弟rocketmq
rocketmq的架构已经不使用zookeeper集群作为服务的注册发现了
rocketmq队列模式很大程度上与kafka非常像,但是具体操作细节上有自己的特点,更符合高并发的,更多topic的,有顺序要求的业务消息处理,例如Topic进行了多个分片划分,分区又进行了多个Queue的划分,每个Queue只能对应一个消费者,来实现更高并发的消费端均衡负载。具体细节这就不赘述了。我们主要还是看看rocketmq的分布式特征。
其实NameServer也就是做了一个broker的注册表,新注册broker或者异常退出broker都向对应的NameSever汇报或感知,NameServer之间是无中心的,大家通过锁注册表的方式共享信息,NameServer增加/删除所辖broker到这个注册表,并定时读取最新的集群所有broker信息。
生产者(Producet)连接上一个NameServer,就能获取到想要的发送分区数据的brokers,消费者同理,发送消费的这个过程很类似Kafka操作topic,只是更细致到topic下的queue这个级别。
rocketmq还有一个特点是每个borker可以再分成主从模式,master进行队列操作,slave只做数据同步,等待master出现故障进行替换。
rocketmq的namesever相对于zookeeper具有更简单的结构,生产者和消费者对broker以及分区的获取必须来自namesever,尽管namesever集群本身是无中心的,但整个rocketmq的brokers就是被namesever中心化管理的,但整体上product、consumer、brokers集群对这种集中管理的依赖程度其实不高,只是提供了很简单的broker元信息服务,真正的数据流还是交给各个broker自己去解决。
kafka的broker分区信息是分布在每一台broker的meta缓存里面,生产者和消费者可以在任意一台borker上获取需要操作的leader分区信息,kafka这就有点去中心的意思。然而这些meta缓存信息实质是来自zookeeper,zookeeper是必须依赖的,所以本质上Kafka依然是中心化管理。
oceanbase分布式架构
oceanbase是LSM-Tree的一个典型实现,对于LSM-Tree可以看我的另一篇针对TiDB的回答文章中,主要对RocksDB的LSM-Tree的特征做了描述:
为什么分布式数据库这么喜欢用kv store?
作为oceanbase的架构,这次就不说太多了,就是想简单总结一下,oceanbase架构非常巧妙地融入了Lambda架构思想,但又和Lambda架构思想的关注点不同,Lambda架构关注的是计算,而oceanbase是存储。
oceanbase往往rootServer、updateServer部署在一个节点,共同承担了分布式中心的作用。
rootServer用于管理集群。
updateServer用于增量数据更新,尽量在内存中完成增量,形成最高效的近期增量数据查询,往往是当天数据。
chunkServer用于基线数据存储,实际情况往往是隔天历史数据。
mergeServer,接受客户端的SQL进行解释,并且对updateServer查询结果、不同chunkServer节点查询结果数据合并,往往是当天增量数据和隔天历史数据的查询与合并。
这与Lambda架构的速度层、批量层、服务层的思想非常类似。当客户发起查询统计请求,updateServer满足当天增量数据的实时查询统计,chunkServer节点提供基线数据的分布式查询,最终由mergeServer对updateServer当日结果和各chunkServer基线结果进行合并后,反馈给客户端,总之oceanbase架构设计是个艺术品。
总结
这篇文章主要是介绍了分布式中redis cluster去中心化管理,kafka与rocketmq中心化管理的架构特点,顺便提了一些oceanbase的架构特色。
消息队列架构对于集中模式的依赖很轻,rocketmq也只是简单粗暴地使用了nameserver,用于broker注册发现,我认为kafka完全可以在将来的设计取消zookeeper,用更为去中心化的思路来设计注册和发现。
反观redis最成熟的方案还是主从,redis cluster带来的性能优势无法抵消去中心化带来的不成熟和不可靠问题,导致人工运维的复杂度和难度。所以redis cluster慎用!
oceanbase的架构很优雅也很艺术,抽时间好好再理解实践写一篇,oceanbase类似Google的Bigtable,Hadoop的Hbase,只是在其之上融入了Lambda架构的思想。让系统表现得更符合实际需求,也更为灵活可靠。但集群对资源需求不少。
我是“读字节”创作者,深入大数据技术、解读分布式架构
前往读字节的知乎——了解更多关于大数据的知识公众号“读字节” 分布式,大数据,软件架构的深度,专业解读