在使用Kafka的过程中,分区是一个不可忽视的概念。很多时候你会带着这样的疑问:Kafka的分区该怎么划分?按什么划分?分多少个?撰稿之时,我在《深入理解Kafka》一书中搜索了一下“分区”这个词,结果发现出现的频率至少有4位数之多。Kafka中分区的概念涉猎很多,比如:分区分配、分区重分配、失效分区等。在本公众号里也发表过几篇文章来讲述Kafka分区中的某些知识点:
本文并不延续讲解这些内容,而是要剑走偏锋,聊一聊Kafka分区被大家所忽视的一个隐藏属性——消息的归类。
在Kafka中主题和分区是两个非常重要的概念。Kafka中的消息向来以“主题”来进行归类(划分),一个主题可以包含若干个分区,一个分区只属于单个主题。同一主题下的不同分区所包含的消息不同的(如果需要数据冗余,那么涉及到的是Kafka的多副本机制,这里不展开论述)。对于单个分区而言,消息是顺序追加的,也因此为Kafka提供了分区内的顺序特性,这一点在RabbitMQ不是太好保证(其实RabbitMQ也不是不可以,就是有点麻烦)。
使用分区最主要的作用还是提高可扩展性。如果一个主题只有一个分区,那么所有的数据读写都经过这一个分区的话势必会造成性能瓶颈。当然,如果规定主题只能有一个分区的话,那么分区这个概念就可以去除掉了。如果主题有多个分区,那么可以将这种读写的压力分散开来。主题中的多个分区不一定要在同一个broker上,完全可以分布在多个broker上,特殊情况下还可以一个分区一个broker。
“消息的归类”并不是不是主题所独有的特性,其实分区也可以,分区可以看做是消息的二次归类,让分区变得有意义。举个例子,我们创建了一个主题用来存储人员信息,这个人员信息就是对数据的一种归类。由于人员众多,我们想着是要用多个分区来分摊一下读写压力,那么我们可以依据什么来划分呢?第一种,按照生肖,这样就可以创建12个分区,分区编号从0开始分别代表鼠、牛、虎、兔、牛等等,这样的分区规则就是对人员信息的二次归类。第二种,我们估算12个分区的压力也有点顶不住,那么我们可以按照姓名拼音的首字母来划分,这也是二次归类。
在实际实践时可以将消息(ProducerRecord<生肖,人员信息>)对应的key设置为与其对应分区的“归类类别”即可。比如,按生肖分,那么“属牛的肥朝”可以这一条消息(人员信息)可以表示为:key=“牛”,value=“姓名:肥朝,特长:帅”。
聪明的同学还会接着想到用姓氏来归类。。也不是不可行,中国百家姓(其实何止)。但是要考虑到一种情况就是数据倾斜,“赵钱孙李”所对应的分区肯定比“令狐”、“独孤”、“上官”之类的分区所承担的数据负载要多很多,这样严重的数据倾斜会导致服务整体效能的降低。“赵钱孙李”可以视为热点数据,而“令狐”、“独孤”、“上官”可以视为冷数据,我们可以这样划分,参考下图:
在某些时候,按照字面的类别划分也很难避免数据倾斜。沿用上面的例子,宋朝时期,姓赵的人实在太多,划分到一个分区还是会有严重的数据倾斜,那么就需要对这一姓氏做多一些处理,多化几个分区给它。
举这些例子是为了说明:如果要让分区获得“二次归类”的特性,就需要做好“二次归类”的准备,也就是需要应对数据热点问题。为了尽可能的数据分布均匀而弱化热点问题应该根据归类的特性做好合理的规划。
在Kafka中创建消息的时候,如果不指定分区,那么就会根据消息的key来进行计算。
如果key不为null,那么默认的分区器会对key进行哈希(采用MurmurHash2算法,具备高运算性能及低碰撞率),最终根据得到的哈希值来计算分区号,拥有相同key的消息会被写入同一个分区。如果key为null,那么消息将会以轮询的方式发往主题内的各个可用分区。前者根据key进行计算,潜在的使用了“二次归类”这个特性,后者采用轮询的方式,潜在的丢弃了“二次归类”的特性。
当然,我们还可以自定义Kafka中的分区器来实现随机的分类,这个实现很简单,从[0,分区数)中挑选一个整数而已,如此也同样会丢弃“二次归类”的特性。
关于分区的这个属性,你还有什么需要补充的吗?不妨在留言去留言来一起探讨。
欢迎支持笔者小册:《图解Kafka之实战指南》和《图解Kafka之核心原理》
欢迎支持笔者新作:《深入理解Kafka:核心设计与实践原理》和《RabbitMQ实战指南》,同时欢迎关注笔者的微信公众号:朱小厮的博客。