Kafka出色的I/O优化以及多处异步化设计,相比其余消息队列系统具备更高的吞吐,同时可以保证不错的延迟,十分适合应用在整个大数据生态中。html
目前在美团数据平台中,Kafka承担着数据缓冲和分发的角色。以下图所示,业务日志、接入层Nginx日志或线上DB数据经过数据采集层发送到Kafka,后续数据被用户的实时做业消费、计算,或通过数仓的ODS层用做数仓生产,还有一部分则会进入公司统一日志中心,帮助工程师排查线上问题。git
目前美团线上Kafka规模:github
当前Kafka支撑的实时做业数量众多,单机承载的Topic和Partition数量很大。这种场景下很容易出现的问题是:同一台机器上不一样Partition间竞争PageCache资源,相互影响,致使整个Broker的处理延迟上升、吞吐降低。算法
接下来,咱们将结合Kafka读写请求的处理流程以及线上统计的数据来分析一下Kafka在线上的痛点。后端
对于Produce请求:Server端的I/O线程统一将请求中的数据写入到操做系统的PageCache后当即返回,当消息条数到达必定阈值后,Kafka应用自己或操做系统内核会触发强制刷盘操做(如左侧流程图所示)。缓存
对于Consume请求:主要利用了操做系统的ZeroCopy机制,当Kafka Broker接收到读数据请求时,会向操做系统发送sendfile系统调用,操做系统接收后,首先试图从PageCache中获取数据(如中间流程图所示);若是数据不存在,会触发缺页异常中断将数据从磁盘读入到临时缓冲区中(如右侧流程图所示),随后经过DMA操做直接将数据拷贝到网卡缓冲区中等待后续的TCP传输。微信
综上所述,Kafka对于单一读写请求均拥有很好的吞吐和延迟。处理写请求时,数据写入PageCache后当即返回,数据经过异步方式批量刷入磁盘,既保证了多数写请求都能有较低的延迟,同时批量顺序刷盘对磁盘更加友好。处理读请求时,实时消费的做业能够直接从PageCache读取到数据,请求延迟较小,同时ZeroCopy机制可以减小数据传输过程当中用户态与内核态的切换,大幅提高了数据传输的效率。架构
但当同一个Broker上同时存在多个Consumer时,就可能会因为多个Consumer竞争PageCache资源致使它们同时产生延迟。下面咱们以两个Consumer为例详细说明:并发
如上图所示,Producer将数据发送到Broker,PageCache会缓存这部分数据。当全部Consumer的消费能力充足时,全部的数据都会从PageCache读取,所有Consumer实例的延迟都较低。此时若是其中一个Consumer出现消费延迟(图中的Consumer Process2),根据读请求处理流程可知,此时会触发磁盘读取,在从磁盘读取数据的同时会预读部分数据到PageCache中。当PageCache空间不足时,会按照LRU策略开始淘汰数据,此时延迟消费的Consumer读取到的数据会替换PageCache中实时的缓存数据。后续当实时消费请求到达时,因为PageCache中的数据已被替换掉,会产生预期外的磁盘读取。这样会致使两个后果:异步
咱们针对HDD的性能和读写并发的影响作了梯度测试,以下图所示:
能够看到,随着读并发的增长,HDD的IOPS和带宽均会明显降低,这会进一步影响整个Broker的吞吐以及处理延迟。
目前Kafka集群TP99流量在170MB/s,TP95流量在100MB/s,TP50流量为50-60MB/s;
单机的PageCache平均分配为80GB,取TP99的流量做为参考,在此流量以及PageCache分配状况下,PageCache最大可缓存数据时间跨度为80*1024/170/60 = 8min,可见当前Kafka服务总体对延迟消费做业的容忍性极低。该状况下,一旦部分做业消费延迟,实时消费做业就可能会受到影响。
同时,咱们统计了线上实时做业的消费延迟分布状况,延迟范围在0-8min(实时消费)的做业只占80%,说明目前存在线上存在20%的做业处于延迟消费的状态。
总结上述的原理分析以及线上数据统计,目前线上Kafka存在以下问题:
按目前的PageCache空间分配以及线上集群流量分析,Kafka没法对实时消费做业提供稳定的服务质量保障,该痛点亟待解决。
根据上述痛点分析,咱们的预期目标为保证明时消费做业不会因为PageCache竞争而被延迟消费做业影响,保证Kafka对实时消费做业提供稳定的服务质量保障。
根据上述缘由分析可知,解决目前痛点可从如下两个方向来考虑:
对于第一个方向,因为PageCache由操做系统管理,若修改其淘汰策略,那么实现难度较为复杂,同时会破坏内核自己对外的语义。另外,内存资源成本较高,没法进行无限制的扩展,所以须要考虑第二个方向。
SSD目前发展日益成熟,相较于HDD,SSD的IOPS与带宽拥有数量级级别的提高,很适合在上述场景中当PageCache出现竞争后承接部分读流量。咱们对SSD的性能也进行了测试,结果以下图所示:
从图中能够发现,随着读取并发的增长,SSD的IOPS与带宽并不会显著下降。经过该结论可知,咱们可使用SSD做为PageCache与HDD间的缓存层。
在引入SSD做为缓存层后,下一步要解决的关键问题包括PageCache、SSD、HDD三者间的数据同步以及读写请求的数据路由等问题,同时咱们的新缓存架构须要充分匹配Kafka引擎读写请求的特征。本小节将介绍新架构如何在选型与设计上解决上述提到的问题。
Kafka引擎在读写行为上具备以下特性:
下文给出了两种备选方案,下面将对两种方案给出咱们的选取依据与架构决策。
备选方案一:基于操做系统内核层实现
目前开源的缓存技术有FlashCache、BCache、DM-Cache、OpenCAS等,其中BCache和DM-Cache已经集成到Linux中,但对内核版本有要求,受限于内核版本,咱们仅能选用FlashCache/OpenCAS。
以下图所示,FlashCache以及OpenCAS两者的核心设计思路相似,两种架构的核心理论依据为“数据局部性”原理,将SSD与HDD按照相同的粒度拆成固定的管理单元,以后将SSD上的空间映射到多块HDD层的设备上(逻辑映射or物理映射)。在访问流程上,与CPU访问高速缓存和主存的流程相似,首先尝试访问Cache层,若是出现CacheMiss,则会访问HDD层,同时根据数据局部性原理,这部分数据将回写到Cache层。若是Cache空间已满,会经过LRU策略替换部分数据。
FlashCache/OpenCAS提供了四种缓存策略:WriteThrough、WriteBack、WriteAround、WriteOnly。 因为第四种不作读缓存,这里咱们只看前三种。
写入:
读取:
更多详细实现细节,极大可参见这两者的官方文档:
备选方案二:Kafka应用内部实现
上文提到的第一类备选方案中,核心的理论依据“数据局部性”原理与Kafka的读写特性并不能彻底吻合,“数据回刷”这一特性依然会引入缓存空间污染问题。同时,上述架构基于LRU的淘汰策略也与Kafka读写特性存在矛盾,在多Consumer并发消费时,LRU淘汰策略可能会误淘汰掉一些近实时数据,致使实时消费做业出现性能抖动。
可见,备选方案一并不能彻底解决当前Kafka的痛点,须要从应用内部进行改造。总体设计思路以下,将数据按照时间维度分布在不一样的设备中,近实时部分的数据缓存在SSD中,这样当出现PageCache竞争时,实时消费做业从SSD中读取数据,保证明时做业不会受到延迟消费做业影响。下图展现了基于应用层实现的架构处理读请求的流程:
当消费请求到达Kafka Broker时,Kafka Broker直接根据其维护的消息偏移量(Offset)和设备的关系从对应的设备中获取数据并返回,而且在读请求中并不会将HDD中读取的数据回刷到SSD,防止出现缓存污染。同时访问路径明确,不会因为Cache Miss而产生的额外访问开销。
下表对不一样候选方案进行了更加详细的对比:
最终,结合与Kafka读写特性的匹配度,总体工做量等因素综合考虑,咱们采用Kafka应用层实现这一方案,由于该方案更贴近Kafka自己读写特性,能更加完全地解决Kafka的痛点。
根据上文对Kafka读写特性的分析,咱们给出应用层基于SSD的缓存架构的设计目标:
依据上述目标,咱们给出应用层基于SSD的Kafka缓存架构实现:
Kafka中一个Partition由若干LogSegment构成,每一个LogSegment包含两个索引文件以及日志消息文件。一个Partition的若干LogSegment按Offset(相对时间)维度有序排列。
根据上一小节的设计思路,咱们首先将不一样的LogSegment标记为不一样的状态,如图所示(图中上半部分)按照时间维度分为OnlyCache、Cached以及WithoutCache三种常驻状态。而三种状态的转换以及新架构对读写操做的处理如图中下半部分所示,其中标记为OnlyCached状态的LogSegment只存储在SSD上,后台线程会按期将Inactive(没有写流量)的LogSegment同步到SSD上,完成同步的LogSegment被标记为Cached状态。最后,后台线程将会按期检测SSD上的使用空间,当空间达到阈值时,后台线程将会按照时间维度将距离如今最久的LogSegment从SSD中移除,这部分LogSegment会被标记为WithoutCache状态。
对于写请求而言,写入请求依然首先将数据写入到PageCache中,知足阈值条件后将会刷入SSD。对于读请求(当PageCache未获取到数据时),若是读取的offset对应的LogSegment的状态为Cached或OnlyCache,则数据从SSD返回(图中LC2-LC1以及RC1),若是状态为WithoutCache,则从HDD返回(图中LC1)。
对于Follower副本的数据同步,可根据Topic对延迟以及稳定性的要求,经过配置决定写入到SSD仍是HDD。
上文介绍了基于SSD的Kafka应用层缓存架构的设计概要以及核心设计思路,包括读写流程、内部状态管理以及新增后台线程功能等。本小节将介绍该方案的关键优化点,这些优化点均与服务的性能息息相关。主要包括LogSegment同步以及Append刷盘策略优化,下面将分别进行介绍。
LogSegment同步
LogSegment同步是指将SSD上的数据同步到HDD上的过程,该机制在设计时主要有如下两个关键点:
同步方式
关于LogSegment的同步方式,咱们给出了三种备选方案,下表列举了三种方案的介绍以及各自的优缺点:
最终,咱们对一致性维护代价、实现复杂度等因素综合考虑,选择了后台同步Inactive的LogSegment的方式。
同步限速
LogSegment同步行为本质上是设备间的数据传输,会同时在两个设备上产生额外的读写流量,占用对应设备的读写带宽。同时,因为咱们选择了同步Inactive部分的数据,须要进行整段的同步。若是在同步过程当中不加以限制会对服务总体延迟形成较大的影响,主要表如今下面两个方面:
基于上述两点,咱们须要在LogSegment同步过程当中增长限速机制,整体的限速原则为在不影响正常读写请求延迟的状况下尽量快速地进行同步。由于同步速度过慢会致使SSD数据没法被及时清理而最终被写满。同时为了能够灵活调整,该配置也被设置为单Broker粒度的配置参数。
日志追加刷盘策略优化
除了同步问题,数据写入过程当中的刷盘机制一样影响服务的读写延迟。该机制的设计不只会影响新架构的性能,对原生Kafka一样会产生影响。
下图展现了单次写入请求的处理流程:
在Produce请求处理流程中,首先根据当前LogSegment的位置与请求中的数据信息肯定是否须要滚动日志段,随后将请求中的数据写入到PageCache中,更新LEO以及统计信息,最后根据统计信息肯定是否须要触发刷盘操做,若是须要则经过fileChannel.force
强制刷盘,不然请求直接返回。
在整个流程中,除日志滚动与刷盘操做外,其余操做均为内存操做,不会带来性能问题。日志滚动涉及文件系统的操做,目前,Kafka中提供了日志滚动的扰动参数,防止多个Segment同时触发滚动操做给文件系统带来压力。针对日志刷盘操做,目前Kafka给出的机制是以固定消息条数触发强制刷盘(目前线上为50000),该机制只能保证在入流量必定时,消息会以相同的频率刷盘,但没法限制每次刷入磁盘的数据量,对磁盘的负载没法提供有效的限制。
以下图所示,为某磁盘在午高峰时间段write_bytes的瞬时值,在午高峰时间段,因为写入流量的上升,在刷盘过程当中会产生大量的毛刺,而毛刺的值几乎接近磁盘最大的写入带宽,这会使读写请求延迟发生抖动。
针对该问题,咱们修改了刷盘的机制,将本来的按条数限制修改成按实际刷盘的速率限制,对于单个Segment,刷盘速率限制为2MB/s。该值考虑了线上实际的平均消息大小,若是设置太小,对于单条消息较大的Topic会过于频繁的进行刷新,在流量较高时反而会加剧平均延迟。目前该机制已在线上小范围灰度,右图展现了灰度后同时间段对应的write_bytes指标,能够看到相比左图,数据刷盘速率较灰度前明显平滑,最高速率仅为40MB/s左右。
对于SSD新缓存架构,一样存在上述问题,所以在新架构中,在刷盘操做中一样对刷盘速率进行了限制。
Case1: 仅有延迟消费时,观察集群的生产和消费性能。
Case2: 存在延迟消费时,观察实时消费的性能。
从单Broker请求延迟角度看:
在刷盘机制优化前,SSD新缓存架构在全部场景下,较其余方案都具备明显优点。
刷盘机制优化后,其他方案在延迟上服务质量有提高,在较小流量下因为flush机制的优化,新架构与其余方案的优点变小。当单节点写入流量较大时(大于170MB)优点明显。
从延迟做业对实时做业的影响方面看:
新缓存架构在测试所涉及的全部场景中,延迟做业都不会对实时做业产生影响,符合预期。
Kafka在美团数据平台承担统一的数据缓存和分发的角色,针对目前因为PageCache互相污染、进而引起PageCache竞争致使实时做业被延迟做业影响的痛点,咱们基于SSD自研了Kafka的应用层缓存架构。本文主要介绍Kafka新架构的设计思路以及与其余开源方案的对比。与普通集群相比,新缓存架构具有很是明显的优点:
目前,这套缓存架构优已经验证完成,正在灰度阶段,将来也优先部署到高优集群。其中涉及的代码也将提交给Kafka社区,做为对社区的回馈,也欢迎你们跟咱们一块儿交流。
世吉,仕禄,均为美团数据平台工程师。
| 想阅读更多技术文章,请关注美团技术团队(meituantech)官方微信公众号。
| 在公众号菜单栏回复【2019年货】、【2018年货】、【2017年货】、【算法】等关键词,可查看美团技术团队历年技术文章合集。