批量处理

传统消息中间件的消息发送和消费整体上是针对单条的。对于生产者而言,它先发一条消息,然后broker返回ACK表示已接收,这里产生2次rpc;对于消费者而言,它先请求接受消息,然后broker返回消息,最后发送ACK表示已消费,这里产生了3次rpc(有些消息中间件会优化一下,broker返回的时候返回多条消息)。而Kafka采用了批量处理:生产者聚合了一批消息,然后再做2次rpc将消息存入broker,这原本是需要很多次的rpc才能完成的操作。假设需要发送1000条消息,每条消息大小1KB,那么传统的消息中间件需要2000次rpc,而Kafka可能会把这1000条消息包装成1个1MB的消息,采用2次rpc就完成了任务。这一改进举措一度被认为是一种“作弊”的行为,然而在微批次理念盛行的今日,其它消息中间件也开始纷纷效仿。

客户端优化

这里接着批量处理的概念继续来说,新版生产者客户端摒弃了以往的单线程,而采用了双线程:主线程和Sender线程。主线程负责将消息置入客户端缓存,Sender线程负责从缓存中发送消息,而这个缓存会聚合多个消息为一个批次。有些消息中间件会把消息直接扔到broker。

日志格式

Kafka从0.8版本开始日志格式历经了三次变革:v0、v1、v2,Kafka的日志格式越来越利于批量消息的处理。

日志编码

如果了解了Kafka具体的日志格式(可以参考上图),那么你应该了解日志(Record,或者称之为消息)本身除了基本的key和value之外,还有一些其它的字段,原本这些附加字段按照固定的大小占用一定的篇幅(参考上图左),而Kafka最新的版本中采用了变成字段Varints和ZigZag编码,有效地降低了这些附加字段的占用大小。日志(消息)尽可能变小了,那么网络传输的效率也会变高,日志存盘的效率也会提升,从而整理的性能也会有所提升。

消息压缩

Kafka支持多种消息压缩方式(gzip、snappy、lz4)。对消息进行压缩可以极大地减少网络传输 量、降低网络 I/O,从而提高整体的性能。消息压缩是一种使用时间换空间的优化方式,如果对 时延有一定的要求,则不推荐对消息进行压缩。

建立索引,方便快速定位查询

每个日志分段文件对应了两个索引文件,主要用来提高查找消息的效率,这也是提升性能的一种方式。

分区

很多人会忽略掉这个因素,其实分区也是提升性能的一种非常有效的方式,这种方式所带来的效果会比前面所说的日志编码、消息压缩等更加的明显。分区在其他分布式组件中也有大量涉及,至于为什么分区能够提升性能这种基本知识在这里就不在赘述了。不过需要注意,一昧地增加分区并不能一直带来性能的提升。

一致性

绝大多数的资料在讲述Kafka性能优化的举措之时是不会提及一致性的东西的。我们所了解的通用的一致性协议如Paxos、Raft、Gossip等,而Kafka另辟蹊径采用类似PacificA的做法不是“拍大腿”拍出来的,采用这种模型会提升整理的效率。

顺序写盘

操作系统可以针对线性读写做深层次的优化,比如预读(read-ahead,提前将一个比较大的磁盘块读入内存) 和后写(write-behind,将很多小的逻辑写操作合并起来组成一个大的物理写操作)技术。Kafka 在设计时采用了文件追加的方式来写入消息,即只能在日志文件的尾部追加新的消 息,并且也不允许修改已写入的消息,这种方式属于典型的顺序写盘的操作,所以就算 Kafka 使用磁盘作为存储介质,它所能承载的吞吐量也不容小觑。

页缓存

为什么Kafka性能这么高?当遇到这个问题的时候很多人都会想到上面的顺序写盘这一点。其实在顺序斜盘前面还有页缓存(PageCache)这一层的优化。

页缓存是操作系统实现的一种主要的磁盘缓存,以此用来减少对磁盘 I/O 的操作。具体 来说,就是把磁盘中的数据缓存到内存中,把对磁盘的访问变为对内存的访问。为了弥补性 能上的差异,现代操作系统越来越“激进地”将内存作为磁盘缓存,甚至会非常乐意将所有 可用的内存用作磁盘缓存,这样当内存回收时也几乎没有性能损失,所有对于磁盘的读写也 将经由统一的缓存。

当一个进程准备读取磁盘上的文件内容时,操作系统会先查看待读取的数据所在的页 (page)是否在页缓存(pagecache)中,如果存在(命中)则直接返回数据,从而避免了对物 理磁盘的 I/O 操作;如果没有命中,则操作系统会向磁盘发起读取请求并将读取的数据页存入 页缓存,之后再将数据返回给进程。同样,如果一个进程需要将数据写入磁盘,那么操作系统也会检测数据对应的页是否在页缓存中,如果不存在,则会先在页缓存中添加相应的页,最后将数据写入对应的页。被修改过后的页也就变成了脏页,操作系统会在合适的时间把脏页中的 数据写入磁盘,以保持数据的一致性。

对一个进程而言,它会在进程内部缓存处理所需的数据,然而这些数据有可能还缓存在操 作系统的页缓存中,因此同一份数据有可能被缓存了两次。并且,除非使用 Direct I/O 的方式, 否则页缓存很难被禁止。此外,用过 Java 的人一般都知道两点事实:对象的内存开销非常大, 通常会是真实数据大小的几倍甚至更多,空间使用率低下;Java 的垃圾回收会随着堆内数据的 增多而变得越来越慢。基于这些因素,使用文件系统并依赖于页缓存的做法明显要优于维护一 个进程内缓存或其他结构,至少我们可以省去了一份进程内部的缓存消耗,同时还可以通过结构紧凑的字节码来替代使用对象的方式以节省更多的空间。如此,我们可以在 32GB 的机器上使用 28GB 至 30GB 的内存而不用担心 GC 所带来的性能问题。此外,即使 Kafka 服务重启, 页缓存还是会保持有效,然而进程内的缓存却需要重建。这样也极大地简化了代码逻辑,因为 维护页缓存和文件之间的一致性交由操作系统来负责,这样会比进程内维护更加安全有效。

Kafka 中大量使用了页缓存,这是 Kafka 实现高吞吐的重要因素之一。虽然消息都是先被写入页缓存,然后由操作系统负责具体的刷盘任务的。

零拷贝

Kafka使用了Zero Copy技术提升了消费的效率。前面所说的Kafka将消息先写入页缓存,如果消费者在读取消息的时候如果在页缓存中可以命中,那么可以直接从页缓存中读取,这样又节省了一次从磁盘到页缓存的copy开销。另外对于读写的概念可以进一步了解一下什么是写放大和读放大。

一个磁盘IO流程可以参考下图:

Kafka为什么这么快?的更多相关文章

  1. 为什么Kafka速度那么快

    Kafka的消息是保存或缓存在磁盘上的,一般认为在磁盘上读写数据是会降低性能的,因为寻址会比较消耗时间,但是实际上,Kafka的特性之一就是高吞吐率. 即使是普通的服务器,Kafka也可以轻松支持每秒 ...

  2. 为什么 Kafka 速度那么快?

    来源:cnblogs.com/binyue/p/10308754.html Kafka的消息是保存或缓存在磁盘上的,一般认为在磁盘上读写数据是会降低性能的,因为寻址会比较消耗时间,但是实际上,Kafk ...

  3. kafka速度为什么快

    1. kafka 使用了 分区.分布式.leader/followere 的方式.分布式让 kafka 排除了单点故障,分区和分区复制让数据不丢失2. kafka 使用 zero copy 技术 (基 ...

  4. 基于磁盘的Kafka为什么这么快

    专注于Java领域优质技术,欢迎关注 作者: Wyman 大数据手稿笔记 Kafka是大数据领域无处不在的消息中间件,目前广泛使用在企业内部的实时数据管道,并帮助企业构建自己的流计算应用程序.Kafk ...

  5. kafka 为什么那么快?

    Cache Filesystem Cache PageCache缓存 顺序写 由于现代的操作系统提供了预读和写技术,磁盘的顺序写大多数情况下比随机写内存还要快. Zero-copy 零拷⻉技术减少拷贝 ...

  6. Kafka为什么速度那么快?

    Kafka为什么速度那么快? Kafka的消息是保存或缓存在磁盘上的,一般认为在磁盘上读写数据是会降低性能的,因为寻址会比较消耗时间,但是实际上,Kafka的特性之一就是高吞吐率. 即使是普通的服务器 ...

  7. Kafka为什么速度那么快?该怎么回答

    Kafka的消息是保存或缓存在磁盘上的,一般认为在磁盘上读写数据是会降低性能的,因为寻址会比较消耗时间,但是实际上,Kafka的特性之一就是高吞吐率.即使是普通的服务器,Kafka也可以轻松支持每秒百 ...

  8. 【知识点】同样是消息队列,Kafka凭什么速度那么快?

    同样是消息队列,Kafka凭什么速度那么快? 作者 | MrZhangxd Kafka的消息是保存或缓存在磁盘上的,一般认为在磁盘上读写数据是会降低性能的,因为寻址会比较消耗时间,但是实际上,Kafk ...

  9. Kafka速度为什么那么快

    记录一下 Kafka速度为什么那么快 Kafka的消息是保存或缓存在磁盘上的,一般认为在磁盘上读写数据是会降低性能的,因为寻址会比较消耗时间,但是实际上,Kafka的特性之一就是高吞吐率. 即使是普通 ...

随机推荐

  1. [转帖]美团在Redis上踩过的一些坑-1.客户端周期性出现connect timeout

    美团在Redis上踩过的一些坑-1.客户端周期性出现connect timeout 博客分类: redis 运维 jedisconnect timeoutnosqltcp  转载请注明出处哈:http ...

  2. centos7双网卡绑定

    # 概念 服务器存在多块网卡时,可以通过bond来实现多块网卡并在一起使用: # 模式 mode 0:load balancing (round-robin) Support:需要Switch支持 & ...

  3. Python学习之路:列表(List)的append()、extend()与insert()方法

    相同点 这三种方法的作用都是为列表(List)添加值 它们的语法为: list.append(obj)list.extend(seq)list.insert(index,obj) #此处index为对 ...

  4. css3响应式布局教程—css3响应式

    响应式布局 一个网站能够兼容多个终端,并且在各个终端都可以很好展示体验. 媒体类型 在何种设备或者软件上将页面打开 123456789 all:所有媒体braille:盲文触觉设备embossed:盲 ...

  5. Python之路【第十六篇】:Python并发编程|进程、线程

    一.进程和线程 进程 假如有两个程序A和B,程序A在执行到一半的过程中,需要读取大量的数据输入(I/O操作), 而此时CPU只能静静地等待任务A读取完数据才能继续执行,这样就白白浪费了CPU资源. 是 ...

  6. Golang修改json文件的两种方法

    第三方包 go get -u github.com/tidwall/sjson bytes, _ := ioutil.ReadFile(jsonFile) value1, _ := sjson.Set ...

  7. 【数据结构】6.java源码ArrayList

    关于ArrayList的源码关注点 1.从底层数据结构,扩容策略2.ArrayList的增删改查3.特殊处理重点关注4.遍历的速度,随机访问和iterator访问效率对比 1.从底层数据结构,扩容策略 ...

  8. Oracle Round 函式 (四捨五入)

    Oracle Round 函式 (四捨五入)描述 : 傳回一個數值,該數值是按照指定的小數位元數進行四捨五入運算的結果.SELECT ROUND( number, [ decimal_places ] ...

  9. Array : 数组的常用方法

    数组常用方法 对象通用方法 1.toLocalString()  数组中的每个元素都会调用toLocalString(),然后以逗号隔开, 拼接为字符串: 2.toString() 数组中的每个元素都 ...

  10. PB 选择继承父类的注意事项

    1.父类的datewindow 的祖先类最好不是n_dw_single ,最好是n_dw_grid,n_dw_single  的标题行没有阴影.排序,没有行聚焦等功能(非常初始的).n_dw_grid ...