如何为Kafka集群选择合适的Partitions数量

转载:http://blog.csdn.net/odailidong/article/details/52571901php

 

 这是许多kafka使用者常常会问到的一个问题。本文的目的是介绍与本问题相关的一些重要决策因素,并提供一些简单的计算公式。java

文章目录缓存

  越多的分区能够提供更高的吞吐量

  首先咱们须要明白如下事实:在kafka中,单个patition是kafka并行操做的最小单元。在producer和broker端,向每个分区写入数据是能够彻底并行化的,此时,能够经过加大硬件资源的利用率来提高系统的吞吐量,例如对数据进行压缩。在consumer段,kafka只容许单个partition的数据被一个consumer线程消费。所以,在consumer端,每个Consumer Group内部的consumer并行度彻底依赖于被消费的分区数量。综上所述,一般状况下,在一个Kafka集群中,partition的数量越多,意味着能够到达的吞吐量越大。操作系统

  咱们能够粗略地经过吞吐量来计算kafka集群的分区数量。假设对于单个partition,producer端的可达吞吐量为p,Consumer端的可达吞吐量为c,指望的目标吞吐量为t,那么集群所须要的partition数量至少为max(t/p,t/c)。在producer端,单个分区的吞吐量大小会受到批量大小、数据压缩方法、 确认类型(同步/异步)、复制因子等配置参数的影响。通过测试,在producer端,单个partition的吞吐量一般是在10MB/s左右。在consumer端,单个partition的吞吐量依赖于consumer端每一个消息的应用逻辑处理速度。所以,咱们须要对consumer端的吞吐量进行测量。

  虽然随着时间的推移,咱们可以对分区的数量进行添加,可是对于基于Key来生成的这一类消息须要咱们重点关注。当producer向kafka写入基于key的消息时,kafka经过key的hash值来肯定消息须要写入哪一个具体的分区。经过这样的方案,kafka可以确保相同key值的数据能够写入同一个partition。kafka的这一能力对于一部分应用是极为重要的,例如对于同一个key的全部消息,consumer须要按消息的顺序进行有序消费。若是partition的数量发生改变,那么上面的有序性保证将不复存在。为了不上述状况发生,一般的解决办法是多分配一些分区,以知足将来的需求。一般状况下,咱们须要根据将来1到2年的目标吞吐量来设计kafka的分区数量。

  一开始,咱们能够基于当前的业务吞吐量为kafka集群分配较小的broker数量,随着时间的推移,咱们能够向集群中增长更多的broker,而后在线方式将适当比例的partition转移到新增长的broker中去。经过这样的方法,咱们能够在知足各类应用场景(包括基于key消息的场景)的状况下,保持业务吞吐量的扩展性。

  在设计分区数时,除了吞吐量,还有一些其余因素值得考虑。正如咱们后面即将看到的,对于一些应用场景,集群拥有过的分区将会带来负面的影响。

  越多的分区须要打开更多地文件句柄

  在kafka的broker中,每一个分区都会对照着文件系统的一个目录。在kafka的数据日志文件目录中,每一个日志数据段都会分配两个文件,一个索引文件和一个数据文件。当前版本的kafka,每一个broker会为每一个日志段文件打开一个index文件句柄和一个数据文件句柄。所以,随着partition的增多,须要底层操做系统配置更高的文件句柄数量限制。这更多的是一个配置问题。咱们曾经见到过,在生产环境Kafka集群中,每一个broker打开的文件句柄数量超过30,000。

  更多地分区会致使更高的不可用性

  Kafka经过多副本复制技术,实现kafka集群的高可用和稳定性。每一个partition都会有多个数据副本,每一个副本分别存在于不一样的broker。全部的数据副本中,有一个数据副本为Leader,其余的数据副本为follower。在kafka集群内部,全部的数据副本皆采用自动化的方式进行管理,而且确保全部的数据副本的数据皆保持同步状态。不管是producer端仍是consumer端发往partition的请求,皆经过leader数据副本所在的broker进行处理。当broker发生故障时,对于leader数据副本在该broker的全部partition将会变得暂时不可用。Kafka将会自动在其余数据副本中选择出一个leader,用于接收客户端的请求。这个过程由kafka controller节点broker自动完成,主要是从Zookeeper读取和修改受影响partition的一些元数据信息。在当前的kafka版本实现中,对于zookeeper的全部操做都是由kafka controller来完成的(serially的方式)。

  在一般状况下,当一个broker有计划地中止服务时,那么controller会在服务中止以前,将该broker上的全部leader一个个地移走。因为单个leader的移动时间大约只须要花费几毫秒,所以从客户层面看,有计划的服务停机只会致使系统在很小时间窗口中不可用。(注:在有计划地停机时,系统每个时间窗口只会转移一个leader,其余leader皆处于可用状态。)

  然而,当broker非计划地中止服务时(例如,kill -9方式),系统的不可用时间窗口将会与受影响的partition数量有关。假如,一个2节点的kafka集群中存在2000个partition,每一个partition拥有2个数据副本。当其中一个broker非计划地宕机,全部1000个partition同时变得不可用。假设每个partition恢复时间是5ms,那么1000个partition的恢复时间将会花费5秒钟。所以,在这种状况下,用户将会观察到系统存在5秒钟的不可用时间窗口。

  更不幸的状况发生在宕机的broker刚好是controller节点时。在这种状况下,新leader节点的选举过程在controller节点恢复到新的broker以前不会启动。Controller节点的错误恢复将会自动地进行,可是新的controller节点须要从zookeeper中读取每个partition的元数据信息用于初始化数据。例如,假设一个kafka集群存在10,000个partition,从zookeeper中恢复元数据时每一个partition大约花费2ms,则controller的恢复将会增长约20秒的不可用时间窗口。

  一般状况下,非计划的宕机事件发生的状况是不多的。若是系统可用性没法容忍这些少数状况的场景,咱们最好是将每一个broker的partition数量限制在2,000到4,000,每一个kafka集群中partition的数量限制在10,000之内。

  越多的分区可能增长端对端的延迟

  Kafka端对端延迟定义为producer端发布消息到consumer端接收消息所须要的时间。即consumer接收消息的时间减去producer发布消息的时间。Kafka只有在消息提交以后,才会将消息暴露给消费者。例如,消息在全部in-sync副本列表同步复制完成以后才暴露。所以,in-sync副本复制所花时间将是kafka端对端延迟的最主要部分。在默认状况下,每一个broker从其余broker节点进行数据副本复制时,该broker节点只会为此工做分配一个线程,该线程须要完成该broker全部partition数据的复制。经验显示,将1000个partition从一个broker到另外一个broker所带来的时间延迟约为20ms,这意味着端对端的延迟至少是20ms。这样的延迟对于一些实时应用需求来讲显得过长。

  注意,上述问题能够经过增大kafka集群来进行缓解。例如,将1000个分区leader放到一个broker节点和放到10个broker节点,他们之间的延迟是存在差别的。在10个broker节点的集群中,每一个broker节点平均须要处理100个分区的数据复制。此时,端对端的延迟将会从原来的数十毫秒变为仅仅须要几毫秒。

  根据经验,若是你十分关心消息延迟问题,限制每一个broker节点的partition数量是一个很好的主意:对于b个broker节点和复制因子为r的kafka集群,整个kafka集群的partition数量最好不超过100*b*r个,即单个partition的leader数量不超过100.

  越多的partition意味着须要客户端须要更多的内存

  在最新发布的0.8.2版本的kafka中,咱们开发了一个更加高效的Java producer。新版producer拥有一个比较好的特征,他容许用户为待接入消息存储空间设置内存大小上限。在内部实现层面,producer按照每个partition来缓存消息。在数据积累到必定大小或者足够的时间时,积累的消息将会从缓存中移除并发往broker节点。

  若是partition的数量增长,消息将会在producer端按更多的partition进行积累。众多的partition所消耗的内存聚集起来,有可能会超过设置的内容大小限制。当这种状况发生时,producer必须经过消息堵塞或者丢失一些新消息的方式解决上述问题,可是这两种作法都不理想。为了不这种状况发生,咱们必须从新将produder的内存设置得更大一些。

  根据经验,为了达到较好的吞吐量,咱们必须在producer端为每一个分区分配至少几十KB的内存,而且在分区数量显著增长时调整可使用的内存数量。

  相似的事情对于consumer端依然有效。Consumer端每次从kafka按每一个分区取出一批消息进行消费。消费的分区数越多,须要的内存数量越大。尽管如此,上述方式主要运用于非实时的应用场景。

  总结

  一般状况下,kafka集群中越多的partition会带来越高的吞吐量。可是,咱们必须意识到集群的partition总量过大或者单个broker节点partition过多,都会对系统的可用性和消息延迟带来潜在的影响。将来,咱们计划对这些限制进行一些改进,让kafka在分区数量方面变得更加可扩展。

英文原文:http://www.confluent.io/blog/how-to-choose-the-number-of-topicspartitions-in-a-kafka-cluster/

相关文章
相关标签/搜索