Apache Kafka基准测试:三台普通机器上每秒写入2百万消息。kafka之所以速度快,这里主要从下面几方面进行分析。
1 写数据
1.1 顺序写磁盘
在顺序磁盘IO,速度可以比内存随机IO更快。
Kafka官方给出的测试数据(Raid-5, 7200rpm)
- 顺序I/O: 600MB/s
- 随机I/O: 100KB/s
顺序读写磁盘优势
- 磁盘顺序读写速度超过内存随机读写
- 使用磁盘可以避免JVM的GC操作,GC效率低,内存占用大
- 磁盘支持系统冷启动,内存不支持,会丢数据
顺序读写磁盘劣势
- 不支持随机删除数据
- 要么基于过期时间删除数据
- 要么基于partition文件大小删除数据
1.2 mmap(内存映射)
Kafka的数据并 不是实时的写入硬盘 ,它充分利用了现代操作系统 分页存储 来利用内存提高I/O效率。mmap在64位操作系统中一般可以表示20G的数据文件,它的工作原理是直接利用操作系统的Page来实现文件到物理内存的直接映射,完成映射之后对物理内存的操作会被同步到硬盘上(操作系统在适当的时候)。
mmap优势
- 通过mmap进程像读写硬盘一样读写虚拟机内存,不必关心内存的大小
- 极大提升I/O性能, 省去了用户空间和内核空间的两次拷贝开销,但增加了一内核空间的拷贝
mmap劣势
- mmap不可靠。写到mmap中的数据并没有被真正的写到硬盘,操作系统会在程序主动调用flush的时候才把数据真正的写到硬盘。 解决方法:Kafka提供了一个参数producer.type控制是不是主动flush,如果Kafka写入到mmap之后就立即flush然后再返回Producer叫 同步 (sync);写入mmap之后立即返回Producer不调用flush叫 异步 (async)。
- 不适合变长文件读写;不适合大量更新文件操作。不过这两个 劣势在kafka中都不存在。
2 读数据
2.1 零拷贝
传统网络文件传统,需要4次拷贝(两次cpu拷贝、两次DMA拷贝)、两次上下文切换,系统开销大。
硬盘-->内核buf-->用户buf-->内核buf(socket buf)-->NIC
kafka用了sendfile系统调用,极大提升了性能。
- 在内核版本2.1中,引入了sendfile系统调用,只需要三次拷贝(1次CPU拷贝、两次DMA拷贝)、0次上下文切换。
硬盘-->内核buf-->内核buf(socket buf)-->NIC
- 在内核版本2.4中,sendfile实现了更简单的方式,只需要2次拷贝(两次DMA拷贝)、0次上下文切换,做到真正意义上的cpu零拷贝。
硬盘-->内核buf-->NIC
3 Page Cache(页缓存)
Kafka还利用了操作系统本身的Page Cache来优化读写性能,就是利用操作系统自身的内存而不是JVM内存。
使用JVM内存有两个明显缺点:
- Object消耗:如果是使用Java 堆,Java对象的内存消耗比较大,通常是所存储数据的两倍甚至更多
- GC问题:随着JVM中数据不断增多,垃圾回收将会变得复杂与缓慢,使用系统缓存如Page Cache就不会有这问题
相比于使用JVM或in-memory cache等数据结构,利用操作系统的Page Cache更加简单可靠,有其独特优势:
- 操作系统层面的缓存利用率会更高,因为存储的都是紧凑的字节结构而不是独立的对象
- 操作系统本身也对于Page Cache做了大量优化,提供了 write-behind、read-ahead以及flush等多种机制
- 即使服务进程重启,系统缓存依然不会消失,避免了in-process cache重建缓存的过程
- 通过操作系统的Page Cache,Kafka的读写操作基本上是基于内存的,读写速度得到了极大的提升
4 网络IO优化
在很多情况下,系统的瓶颈不是CPU或磁盘,而是网络IO。数据压缩会消耗少量的CPU资源,不过对于kafka这样的大型分布式消息系统而言,网络IO更应该需要考虑。
4.1 批量发送
Kafka允许进行批量消息发送,先将消息缓存在buffer中,然后一次请求批量发送出去,比如可以指定缓存的消息达到某个量的时候发送,或者缓存了固定的时间后发送,这种策略大大减少了服务器端的I/O次数。
4.2 批量压缩
- 使用了批量压缩,即将多个消息一起压缩而不是单个消息压缩
- 通过压缩的形式传输并且在日志中保存,直到被消费者解压缩
- Kafka支持多种压缩协议,包括Gzip和Snappy压缩协议
5 文件分段+文件索引
5.1 文件分段
Kafka 的队列 topic 被分为多个区 partition, 每个 partition 又分为多个 segment,所以一个队列中的消息实际上是保存在 N 多个片段文件中,就是文件系统上的.log文件,这也非常符合分布式系统分区分桶的设计思想。通过分段的方式,每次文件操作都是对一个小文件的操作,非常轻便,同时也增加了并行处理能力。
5.2 文件索引
为了进一步的查询优化,Kafka又默认为分段后的数据文件建立了索引文件,就是文件系统上的.index文件。这种分区分段+索引的设计,不仅提升了数据读取的效率,同时也提高了数据操作的并行度。
参考
https://www.cnblogs.com/binyue/p/10308754.html