Kafka摸鱼系列01-百万QPS吞吐量

引言

最近打算写一下关于Kafka系列的文章,在整个数据体系中,Kafka扮演着一个非常重要的角色-数据总线.作为一个数据开发工程师,在数据的采集/存储/流计算/ETL/数据仓库/数据应用这几个方面,Kafka起到的作用是非常大的,甚至会影响到其他组建或者环节的技术选型.
在很久以前,在Kafka还没那么成熟的时候,很多的数据基础组件在设计之初并没有考虑到数据接收/数据输出解藕,以及数据容灾式持久化,往往都需要配合第三方或者额外开发其他的组件去保证数据的吞吐量以及可靠性,例如sqoop,canal.不过这个都不是本文的重点,本文的重点在于:为何现在很多公司都把Kafka作为整个数据链路的数据总线,这里很关键的一点是–吞吐量.

常用MQ介绍

这里我不会对其他MQ做过多的介绍,但是市面上的主流MQ也必须有个大概的了解,之所以流行开来也是有其独特的优势,Kafka也不例外,先放一张阿里云栖社区做的MQ对比图:
业界主流MQ对比
主要看下吞吐量这个地方,除了和RocketMQ领先的不多,基本上是碾压其他的MQ.一般性能好的MQ吞度量能达到几十万这个量级就非常厉害了,但是可以看到用机械磁盘的Kafka单机TPS差不多可以到200w了,那么对于一个集群而言,几百万的TPS完全不在话下.

这就引出了本文要讨论的一个重要问题:Kafka为什么这么快,吞吐量为何这么惊人?

Kafka吞吐量之谜

这个问题要想回答的好或者说回到的全面,其实并不简单.一个系统设计的这么好,往往是多方面综合考虑的结果,当然在剖析Kafka性能之前,先大概说一下实际使用情况下Kafka性能是否真的如网上说的那么优秀.因为目前BU内部的Kafka是由自己维护,所以规模不是很大,但也支撑了整个BU所有的日常数据业务.

线上规模

集群| 版本 | CPU | 内存 | 磁盘 | 网卡 | brokers数量
—–|—–|——|——|——–|——–
1| 0.8.2.1 | 32核| 64G |2T | 10Gbps | 5
2| 1.0.0 | 32核| 64G |2T | 10Gbps | 5
3| 0.10.2.1 | 32核| 128G |2T | 1Gbps | 3

目前主要数据在0.8.2.1,这个集群使用了大概有3年了,一直很稳定,也承载了几乎所有的数据,可以注意到两个上面的机器其实对配置要求不是很高,但是对磁盘(机械磁盘,T级别)和网卡(万兆)要求会稍微高一些.从这里可以看出,Kafka的性能瓶颈一般在磁盘和网卡.对CPU和内存的要求其实不是很高.实际使用也确实是,最开始本来也是千兆网卡,后来发现brokers节点容易出现网卡被打满,性能上不去的情况.还有就是磁盘有时候会不够用.

下面来说一一介绍一下,为啥Kafka吞吐量能做到这么高.

存储设计

单机写入能到百万级别,并且还是廉价的磁盘.要知道读/写机械磁盘,寻址操作是一个很耗时的IO操作,这也就是为什么现在的DB或者像ES这样的存储系统都慢慢换成SSD了.
Kafka是怎么做的呢,它在设计之初的一个目标就是:

以时间复杂度为O(1)的方式提供消息持久化能力,即使对TB级以上数据也能保证常数时间复杂度的访问性能.

所以Kafka一开始便被设计成一个日志系统,消息只能append,这使得Kafka非常适合用来作为数据总线,所有的数据根据到来的顺序被顺序序列化到文件末尾,然后消费者也是按顺序消费.
实际测试使用中,顺序读写机械磁盘有时候比随机读写内存的吞吐量还要好.当然这个还归功于CPU的工作方式,在加载数据的时候,CPU会预测,连带读取一整块数据,下次读取如果命中,就直接从内存中读,也不用再去加载.

Producer

消息的写入主要由producer完成,首先简单说下Kafka的消息结构,每一个主题topic的消息有多个partition组成,每个partition都会有leader,follower.这里强调一下,不管是producer写数据还是consumer读数据,都是跟leader打交到,follower只负责从对应的leader同步数据.followerleader一起构成了这个partitionISR(同步复制队列),如果follower复制没有跟上,会被从ISR中剔除.所以只有当leader节点挂掉的时候,ISR中的follower节点才有可能备胎转正,数据的读写有新的leader节点负责.
所以说到写数据,就必须要说到KafkaAck机制.有时候性能和可靠性本身就是矛盾的,Kafka发送数据光快还不行,还得保证可靠性.
Ack机制

  1. 0:表示producer无需等待leader的确认,
  2. 1:代表需要leader确认写入它的本地log并立即确认,
  3. -1:代表所有的备份都完成后确认