Kafka那么快的原因:
- 使用partition并行处理
- 批处理、数据压缩
- 顺序写磁盘
- mmap内存映射
- 零拷贝
理解Topic和Partition,Partition的并行处理
在 Kafka 中,Topic 是一个存储消息的逻辑概念,可以认为是一个消息集合。
每个 Topic 可以划分多个分区partition(每个 Topic 至少有一个partition),同一 Topic 下的不同分区包含的消息是不同的。每个消息在被添加到分区时,都会被分配一个 offset,它是消息在此分区中的唯一编号,Kafka 通过 offset 保证消息在分区内的顺序,offset 的顺序不跨分区,即 Kafka 只保证在同一个分区内的消息是有序的。
不同Partition可以位于不同的机器,就算在同一个机器上也可以位于不同的磁盘。磁盘的io可比内存的io慢得多,不同的磁盘可以并行处理。
批处理、数据压缩
相比较于内存和磁盘,网络的io效率更低了。
kafka在一个批处理中积累多条读写记录,并通过压缩算法来优化。
顺序写磁盘
新的消息不断追加到 partition 的末尾,这个就是顺序写。
但是,kafka期待操作系统顺序写入磁盘,操作系统只能尽量顺序写(如果遇到磁盘坏道或者磁道被占用)。
机械盘顺序写比随机写性能高:磁头不需要反复寻址。
固态盘顺序写比随机写性能高:闪存不能直接覆盖,需要寻找新的空间来写数据并将原来的数据标记失效。当写命令找不到新空间时,需要的等一次垃圾回收。随机写会比顺序写造成更多的空间碎片,所以顺序写性能比随机写高!
kafka并不是实时写入磁盘,见mmap内存映射。
mmap内存映射
- 进程启动映射过程,并在虚拟地址空间中为映射创建虚拟映射区域
- 调用内核空间的系统调用函数mmap(不同于用户空间函数),实现文件物理地址和进程虚拟地址的一一映射关系
- 进程发起对这片映射空间的访问,引发缺页异常,实现文件内容到物理内存(主存)的拷贝
用内存读写取代I/O读写,pdflush默认要30s才写入磁盘一次。
两空间的各自修改操作可以直接反映在映射的区域内。
通过各自对映射区域的改动,达到进程间通信和进程间共享的目的。
但凡是需要用磁盘空间代替内存的时候,mmap都可以发挥其功效。
kafka为了快,可以不设置同步flush。为了保证系统的一致性:按照指定的级别写入集群副本(内存中),只要集群有一个副本还活着,kafka就能恢复。
零拷贝
Java NIO中FileChannel.transferTo方法,将数据从FileChannel对象传送到可写的字节通道(如Socket Channel等)。在内部实现中,由native方法transferTo0()来实现,它依赖底层操作系统的支持。在UNIX和Linux系统中,调用这个方法将会引起sendfile()系统调用。
kafka的零拷贝采用了linux的sendFile方法,DMA引擎直接把数据从内核缓冲区传输到网卡设备,有2次copy操作。
Netty的零拷贝
疑问点:很多博客中都说java的nio中transfer方法不是真正意义上的零拷贝,有3次copy的行为但是也说是通过操作系统的sendfile方法实现的。linux在2.1版本引入了sendfile此时copy3次(通过socketBuffer中转),2.4版本增强了sendfile此时copy2次。