kafka性能分析

来源:互联网 发布:联想m41指纹识别软件 编辑:程序博客网 时间:2024/06/03 21:39
1
.通过磁盘顺序读写,效率高,appendLog,对比raid-5 7200rpm的磁盘
sequence io 600M/s
random io 100kb/s
kafka写操作时,依赖底层文件系统的pagecache功能,pagecache会将尽量多的将空闲内存,当做磁盘缓存,写操作先写到pageCache,并将该page标记为dirty;发生读操作时,会先从pageCache中查找,当发生缺页时,才会去磁盘调整;当有其他应用申请内存时,回收pageCache的代价是很低的
,使用pageCache的缓存功能,会减少我们队JVM的内存依赖,JVM为我们提供了GC功能,依赖JVM内存在kafka高吞吐,大数据的场景下有很多问题。如果heap管理缓存,那么JVM的gc会频繁扫描heap空间,带来的开销很大,如果heap过大,full gc带来的成本也很高;所有的In-Process Cache在OS中都有一份同样的PageCache。所以通过只在PageCache中做缓存至少可以提高一倍的缓存空间。如果Kafka重启,所有的In-Process Cache都会失效,而OS管理的PageCache依然可以继续使用。

2.sendFile
传统网络IO模型
① 操作系统将数据从磁盘拷贝到内核区的pagecache
② 用户程序将内核区的pagecache拷贝到用户区缓存
③ 用户程序将用户区的缓存拷贝到socket缓存中
④ 操作系统将socket缓存中的数据拷贝到网卡的buffer上,发送

问题:四次system call 两次context切换,同一份数据在缓存中拷贝多次,效率很低,拷贝动作完全可以在内核中进行,将2 3 步去掉,sendfile就是完成这一功能

kafka设计初衷是数据的拷贝完全通过pageCache来进行,尽量减少磁盘读写,如果kafka生产消费配合的好,那么数据完全走内存,这对集群的吞吐量提升是很大的

当集群只有写操作时,此时的集群只有写,没有读操作。10M/s左右的Send的流量是Partition之间进行Replicate而产生的。从recv和writ的速率比较可以看出,写盘是使用Asynchronous+Batch的方式,底层OS可能还会进行磁盘写顺序优化。而在有Read Request进来的时候分为两种情况,第一种是内存中完成数据交换。

② 已经从pageCache刷出的数据,这时候的数据可以看出大部分走的是磁盘io

TIPS
① kafka官方不建议使用log.flush.interval.messages和log.flush.interval.ms来强制刷盘,因为高可靠应该通过replica副本来保证,强制刷盘对系统性能影响很大(生产是100000 和60000)
可以通过调整/proc/sys/vm/dirty_background_ratio和/proc/sys/vm/dirty_ratio来调优性能。
a. 脏页率超过第一个指标会启动pdflush开始Flush Dirty PageCache。
b. 脏页率超过第二个指标会阻塞所有的写操作来进行Flush。
c. 根据不同的业务需求可以适当的降低dirty_background_ratio和提高dirty_ratio
(生产是10 和 20)

2 partition

Partition是Kafka可以很好的横向扩展和提供高并发处理以及实现Replication的基础。
扩展性方面。首先,Kafka允许Partition在集群内的Broker之间任意移动,以此来均衡可能存在的数据倾斜问题。其次,Partition支持自定义的分区算法,例如可以将同一个Key的所有消息都路由到同一个Partition上去。 同时Leader也可以在In-Sync的Replica中迁移。由于针对某一个Partition的所有读写请求都是只由Leader来处理,所以Kafka会尽量把Leader均匀的分散到集群的各个节点上,以免造成网络流量过于集中。

并发方面。任意Partition在某一个时刻只能被一个Consumer Group内的一个Consumer消费(反过来一个Consumer则可以同时消费多个Partition),Kafka非常简洁的Offset机制最小化了Broker和Consumer之间的交互,这使Kafka并不会像同类其他消息队列一样,随着下游Consumer数目的增加而成比例的降低性能。此外,如果多个Consumer恰巧都是消费时间序上很相近的数据,可以达到很高的PageCache命中率,因而Kafka可以非常高效的支持高并发读操作,实践中基本可以达到单机网卡上限。

不过,Partition的数量并不是越多越好,Partition的数量越多,平均到每一个Broker上的数量也就越多。考虑到Broker宕机(Network Failure, Full GC)的情况下,需要由Controller来为所有宕机的Broker上的所有Partition重新选举Leader,假设每个Partition的选举消耗10ms,如果Broker上有500个Partition,那么在进行选举的5s的时间里,对上述Partition的读写操作都会触发LeaderNotAvailableException。

再进一步,如果挂掉的Broker是整个集群的Controller,那么首先要进行的是重新任命一个Broker作为Controller。新任命的Controller要从Zookeeper上获取所有Partition的Meta信息,获取每个信息大概3-5ms,那么如果有10000个Partition这个时间就会达到30s-50s。而且不要忘记这只是重新启动一个Controller花费的时间,在这基础上还要再加上前面说的选举Leader的时间 -_-!!!!!!

此外,在Broker端,对Producer和Consumer都使用了Buffer机制。其中Buffer的大小是统一配置的,数量则与Partition个数相同。如果Partition个数过多,会导致Producer和Consumer的Buffer内存占用过大。


tips
1. Partition的数量尽量提前预分配,虽然可以在后期动态增加Partition,但是会冒着可能破坏Message Key和Partition之间对应关系的风险。
2. Replica的数量不要过多,如果条件允许尽量把Replica集合内的Partition分别调整到不同的Rack。
3. 尽一切努力保证每次停Broker时都可以Clean Shutdown,否则问题就不仅仅是恢复服务所需时间长,还可能出现数据损坏或其他很诡异的问题。

3
Producer
Kafka的研发团队表示在0.8版本里用Java重写了整个Producer,据说性能有了很大提升。我还没有亲自对比试用过,这里就不做数据对比了。本文结尾的扩展阅读里提到了一套我认为比较好的对照组,有兴趣的同学可以尝试一下。

其实在Producer端的优化大部分消息系统采取的方式都比较单一,无非也就化零为整、同步变异步这么几种。

Kafka系统默认支持MessageSet,把多条Message自动地打成一个Group后发送出去,均摊后拉低了每次通信的RTT。而且在组织MessageSet的同时,还可以把数据重新排序,从爆发流式的随机写入优化成较为平稳的线性写入。

此外,还要着重介绍的一点是,Producer支持End-to-End的压缩。数据在本地压缩后放到网络上传输,在Broker一般不解压(除非指定要Deep-Iteration),直至消息被Consume之后在客户端解压。

当然用户也可以选择自己在应用层上做压缩和解压的工作(毕竟Kafka目前支持的压缩算法有限,只有GZIP和Snappy),不过这样做反而会意外的降低效率!!!! Kafka的End-to-End压缩与MessageSet配合在一起工作效果最佳,上面的做法直接割裂了两者间联系。至于道理其实很简单,压缩算法中一条基本的原理“重复的数据量越多,压缩比越高”。无关于消息体的内容,无关于消息体的数量,大多数情况下输入数据量大一些会取得更好的压缩比。

不过Kafka采用MessageSet也导致在可用性上一定程度的妥协。每次发送数据时,Producer都是send()之后就认为已经发送出去了,但其实大多数情况下消息还在内存的MessageSet当中,尚未发送到网络,这时候如果Producer挂掉,那就会出现丢数据的情况。

为了解决这个问题,Kafka在0.8版本的设计借鉴了网络当中的ack机制。如果对性能要求较高,又能在一定程度上允许Message的丢失,那就可以设置request.required.acks=0 来关闭ack,以全速发送。如果需要对发送的消息进行确认,就需要设置request.required.acks为1或-1,那么1和-1又有什么区别呢?这里又要提到前面聊的有关Replica数量问题。如果配置为1,表示消息只需要被Leader接收并确认即可,其他的Replica可以进行异步拉取无需立即进行确认,在保证可靠性的同时又不会把效率拉得很低。如果设置为-1,表示消息要Commit到该Partition的ISR集合中的所有Replica后,才可以返回ack,消息的发送会更安全,而整个过程的延迟会随着Replica的数量正比增长,这里就需要根据不同的需求做相应的优化。

tips
1. Producer的线程不要配置过多,尤其是在Mirror或者Migration中使用的时候,会加剧目标集群Partition消息乱序的情况(如果你的应用场景对消息顺序很敏感的话)。
2. 0.8版本的request.required.acks默认是0(同0.7)。

4
Consumer
Consumer端的设计大体上还算是比较常规的。
• 通过Consumer Group,可以支持生产者消费者和队列访问两种模式。
• Consumer API分为High level和Low level两种。前一种重度依赖Zookeeper,所以性能差一些且不自由,但是超省心。第二种不依赖Zookeeper服务,无论从自由度和性能上都有更好的表现,但是所有的异常(Leader迁移、Offset越界、Broker宕机等)和Offset的维护都需要自行处理。
• 大家可以关注下不日发布的0.9 Release。这帮货又用Java重写了一套Consumer。把两套API合并在一起,同时去掉了对Zookeeper的依赖。据说性能有大幅度提升哦~~

tips
强烈推荐使用Low level API,虽然繁琐一些,但是目前只有这个API可以对Error数据进行自定义处理,尤其是处理Broker异常或由于Unclean Shutdown导致的Corrupted Data时,否则无法Skip只能等着“坏消息”在Broker上被Rotate掉,在此期间该Replica将会一直处于不可用状态。

阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 美的家用烤箱 格兰仕电烤箱价格 大烤箱 烤箱图片 烤箱多少钱 美的烤箱 电烤箱报价 户外烧烤炉厂家 自动烧烤炉厂家 商用烧烤炉哪个牌子好 烧烤炉厂家直销 工业烤箱厂家 环保无烟烧烤炉 韩式烧烤炉厂家 用电烤箱怎样做面包 野炊烧烤炉 家用烤箱买什么样的好 烤全羊烧烤炉 电烧烤炉哪个牌子好 什么牌子烤箱 电烧烤炉厂家 无烟烧烤炉厂家 煤气烧烤炉多少钱 太阳能烧烤炉厂家 燃气烘烤箱 日式烧烤炉厂家 烤箱哪种好用 户外便携式烧烤炉 燃气烤箱品牌 燃气烧烤炉多少钱 订做烧烤炉 节能环保烧烤炉 水循环烧烤炉 小型烧烤炉多少钱一个 家用烧烤炉哪种好用 家用烧烤炉批发 烤烟型 混合烟和烤烟哪危害大 烤烟夹子批发 混合型烟和烤烟哪个危害大 活烤小奶狗