kafka 笔记七 kafka磁盘存储

1 零拷贝
kafka高性能,是多方面协同的结果,包括宏观架构、分布式
partition存储、ISR数据同步、以及无所不用其极”的高效利用磁盘/操作系统特性。
零拷贝并不是不需要拷贝,而是减少不必要的拷贝次数。通常是说在IO
读写过程中。

传统 IO
比如:读取文件, socket 发送
传统方式实现:先读取、再发送,实际经过 1~4 四次 copy。
1 、第一次:将磁盘文件,读取到操作系统内核缓冲区;
2 、第二次:将内核缓冲区的数据, copy application 应用程序的 buffer
3 、第三步:将 application 应用程序 buffer 中的数据, copy socket 网络发送缓冲区 ( 属于操作系统内核的缓冲区)
4 、第四次:将 socket buffer 的数据, copy 到网络协议栈,由网卡进行网络传输。

实际 IO 读写,需要进行 IO 中断,需要 CPU 响应中断 ( 内核态到用户态转换 ) ,尽管引入 DMA(Direct Memory Access,直接存储器访问 ) 来接管 CPU 的中断请求,但四次 copy 是存在 不必要的拷贝 ”的。
实际上并不需要第二个和第三个数据副本。数据可以直接从读缓冲区传输到套接字缓冲区。
kafka的两个过程:
1、网络数据持久化到磁盘 (Producer 到 Broker) 页缓存
2、磁盘文件通过网络发送(Broker 到 Consumer)零拷贝
数据落盘通常都是非实时的, Kafka 的数据并不是实时的写入硬盘,它充分利用了 现代操作系统分页存储来 利用内存提高 I/O 效率。
磁盘文件通过网络发送( Broker Consumer)
磁盘数据通过 DMA(Direct Memory Access ,直接存储器访问 ) 拷贝到内核态 Buffer直接通过 DMA 拷贝到 NIC Buffer(socket buffer) ,无需 CPU 拷贝。
除了减少数据拷贝外,整个读文件 ==> 网络发送由一个 sendfile 调用完成,整个过程只有两次上下文切换,因此大大提高了性能。
Java NIO对sendfile的支持就是FileChannel.transferTo()/transferFrom()。
fileChannel.transferTo( position, count, socketChannel);
把磁盘文件读取OS内核缓冲区后的fileChannel,直接转给socketChannel发送;底层就是sendfile。消费者从broker读取数据,就是由此实现。
具体来看, Kafka 的数据传输通过 TransportLayer 来完成,其子类 PlaintextTransportLayer 通过Java NIO 的 FileChannel transferTo transferFrom 方法实现零拷贝。
注: transferTo 和 transferFrom 并不保证一定能使用零拷贝,需要操作系统支持
Linux 2.4+ 内核通过 sendfile 系统调用,提供了零拷贝。
 
2 页缓存
页缓存是操作系统实现的一种主要的磁盘缓存,以此用来减少对磁盘 I/O 的操作。 具体来说,就是把磁盘中的数据缓存到内存中,把对磁盘的访问变为对内存的访问。
Kafka 接收来自 socket buffer 的网络数据,应用进程不需要中间处理、直接进行持久化时。可以使用mmap 内存文件映射。
Memory Mapped Files简称mmap ,简单描述其作用就是:将磁盘文件映射到内存 , 用户通过修改内存就能修改磁盘文
件。
它的工作原理是直接利用操作系统的 Page 来实现磁盘文件到物理内存的直接映射。完成映射之后你 对物理内存的操作会被同步到硬盘上(操作系统在适当的时候)。
 
通过 mmap,进程像读写硬盘一样读写内存(当然是虚拟机内存)。使用这种方式可以获取很大的I/O提升,省去了用户空间到内核空间复制的开销。
mmap 也有一个很明显的缺陷: 不可靠 ,写到 mmap 中的数据并没有被真正的写到硬盘,操作系统会在程序主动调用flush 的时候才把数据真正的写到硬盘。
Kafka 提供了一个参数 producer.type 来控制是不是主动 flush
如果 Kafka写入到mmap之后就立即flush 然后再返回 Producer 叫同步 (sync)
写入mmap之后立即返回Producer不调用flush 叫异步 (async)。
Java NIO 对文件映射的支持
Java NIO ,提供了一个 MappedByteBuffer 类可以用来实现内存映射。
MappedByteBuffer 只能通过调用 FileChannel map() 取得,再没有其他方式。
使用 MappedByteBuffer 类要注意的是
mmap的文件映射,在full gc时才会进行释放 。当 close 时,需要手动清除内存映射文件,可以反射调用sun.misc.Cleaner 方法。
当一个进程准备读取磁盘上的文件内容时:
1. 操作系统会先查看待读取的数据所在的页 (page) 是否在页缓存 (pagecache) 中,如果存在 ( 命中) 则直接返回数据,从而避免了对物理磁盘的 I/O 操作;
2. 如果没有命中,则操作系统会向磁盘发起读取请求并将读取的数据页存入页缓存,之后再将数据返回给进程。
如果一个进程需要将数据写入磁盘:
1. 操作系统也会检测数据对应的页是否在页缓存中,如果不存在,则会先在页缓存中添加相应的页,最后将数据写入对应的页。
2. 被修改过后的页也就变成了脏页,操作系统会在合适的时间把脏页中的数据写入磁盘,以保持数据的一致性。
对一个进程而言,它会在进程内部缓存处理所需的数据,然而这些数据有可能还缓存在操作系统的页缓存中,因此同一份数据有可能被缓存了两次。并且,除非使用Direct I/O 的方式, 否则页缓存很难被禁止。
当使用页缓存的时候,即使 Kafka 服务重启, 页缓存还是会保持有效,然而进程内的缓存却需要重建。这样也极大地简化了代码逻辑,因为维护页缓存和文件之间的一致性交由操作系统来负责,这样会比进程内维护更加安全有效。
Kafka 中大量使用了页缓存,这是 Kafka 实现高吞吐的重要因素之一。
消息先被写入页缓存,由操作系统负责刷盘任务。

 

3 顺序写入
操作系统可以针对线性读写做深层次的优化,比如预读 (read-ahead ,提前将一个比较大的磁盘块读入内存) 和后写 (write-behind ,将很多小的逻辑写操作合并起来组成一个大的物理写操作 )技术。
Kafka 在设计时采用了文件追加的方式来写入消息,即只能在日志文件的尾部追加新的消 息,并且也不允许修改已写入的消息,这种方式属于典型的顺序写盘的操作,所以就算 Kafka 使用磁盘作为存储介质,也能承载非常大的吞吐量。
mmap sendfile
1. Linux 内核提供、实现零拷贝的 API
2. sendfile 是将读到内核空间的数据,转到 socket buffer ,进行网络发送;
3. mmap 将磁盘文件映射到内存,支持读和写,对内存的操作会反映在磁盘文件上。
4. RocketMQ 在消费消息时,使用了 mmap。kafka 使用了 sendFile。
 
Kafka 速度快是因为:
1. partition 顺序读写,充分利用磁盘特性,这是基础;
2. Producer生产的数据持久化到broker,采用mmap文件映射,实现顺序的快速写入;
3. Customer从broker读取数据,采用sendfile,将磁盘文件读到OS内核缓冲区后,直接转到socket buffer进行网络发送。
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值