目录
CPU、内存和硬盘的速度差异以及缓存
-
CPU 和内存的速度差异:
- CPU 访问寄存器非常快,几乎没有时延,而访问内存则需要大约100-200个时钟周期。为了弥补这一速度差异,CPU 集成了多层高速缓存(L1, L2, L3),其中访问速度比访问主内存快得多,通常在1-30个时钟周期内完成。
-
内存和硬盘的速度差异:
- CPU 访问硬盘的速度非常慢,大约需要几千万个时钟周期。这种差异是由于硬盘是机械设备,而内存则是电子存储器。
-
缓存的作用:
- 在CPU内部的高速缓存(L1, L2, L3)和操作系统引入的页高速缓存(PageCache)都旨在解决不同层次之间的速度差距问题。
- 高速缓存存放了CPU频繁访问的数据,而PageCache则存储了硬盘中的热点数据,这些数据被频繁地访问或者需要快速读取到内存中
PageCache 的基本原理和工作流程
PageCache(页缓存)是Linux操作系统中一种用来缓存文件系统数据的机制。它的主要作用是在内存中缓存从硬盘文件读取的数据,以提高文件访问速度和系统性能。
-
缓存数据块:
- PageCache以页(Page)为单位管理内存中的缓存。每个页的大小通常是4KB,与硬盘上文件数据块(Block)的大小相对应。当应用程序请求读取文件时,内核首先检查是否能够在PageCache中找到对应的页,如果找到,则直接从内存中返回数据,避免了对硬盘的实际读取操作。
-
缓存管理:
- PageCache的大小是动态调整的,它会根据系统内存的空闲情况来扩展或缩减。当系统空闲内存较多时,PageCache可以占用更多的页来缓存文件数据,从而提高文件读取的速度。当系统内存不足时,PageCache会自动释放部分页,以释放内存供其他程序使用,这样可以有效地平衡系统的内存需求和性能要求。
-
读取和写入操作:
- 当需要从文件中读取数据时,如果数据已经缓存在PageCache中,内核会直接将数据从Page拷贝到用户空间缓冲区中,而无需进行实际的磁盘I/O操作。如果数据不在PageCache中,则内核会先将相应的文件数据块读取到一个空闲的Page中,然后再将数据传输到用户空间。
- 对于写入操作,内核会先将数据写入到相应的Page中,然后根据需要将被修改的Page定期刷新(或者称为回写)到相应的文件中,以确保数据的持久性和一致性。
-
性能优化:
- PageCache的引入显著提高了文件系统的性能,特别是在频繁读写大文件时,通过减少实际的磁盘访问次数,大大缩短了I/O操作的响应时间,提高了系统整体的响应速度和吞吐量。
Kafka中,如何利用PageCache来高效写入数据
1. Kafka 客户端发送数据
-
消息累积到 ProducerBatch:
- Kafka 客户端将每条消息根据 TopicPartition 放入发送缓冲区(RecordAccumulator)的不同分区中。如果多条消息的 TopicPartition 相同,它们会被放到同一个分区中。
- ProducerBatch 是批量发送的单位,其大小由配置参数
batch.size
控制,默认为 16384 bytes。
-
形成数据发送请求:
- 一旦某个 ProducerBatch 达到了足够的时间(
linger.ms
)或者大小(batch.size
),就会形成一个数据发送请求。
- 一旦某个 ProducerBatch 达到了足够的时间(
-
发送数据请求到 Broker 的 Leader 副本:
- 发送请求将数据发送到 TopicPartition 的 Leader 副本所在的 Broker 节点的 socket receive buffer 中。
2. Broker 端接收数据
-
数据接收到 Broker 的 socket receive buffer:
- Broker 的网络设备中的 socket receive buffer 接收到数据。
- 这些数据可以直接在内核空间完成落盘操作,而不需要将数据读取到应用进程缓冲区(用户缓冲区)。
-
使用 MMAP 内存映射技术:
- Broker 端使用 Memory Mapped Files(MMAP)技术来将 socket receive buffer 中的数据映射到内核空间的 PageCache 中,避免了数据在内核空间和用户空间之间的多次拷贝。
- MMAP 允许将内核空间和用户空间的虚拟地址映射到同一物理地址,减少数据拷贝次数和内存占用。
-
数据写入到 PageCache:
- 数据在经过 CPU Copy 后,直接写入到内核的 PageCache 中,实现了零拷贝的效果,提升了写入效率。
3. 数据异步落盘机制
-
异步写入磁盘:
- 一旦数据写入到 Broker 的 PageCache 中,Kafka 就会向 Producer 客户端发送消息已成功发送的反馈,因为数据始终在内存中拷贝,所以发送速度很快。
- 操作系统会利用后台的异步刷盘机制,使用 DMA Copy 技术将数据异步写入到硬盘上,从而实现持久化存储。
-
顺序写入和 Segment 文件:
- Kafka 采用顺序写入的方式来优化磁盘性能,这种方式可以减少磁盘的寻道时间和旋转次数,提高写入效率。
- 每个 Kafka 分区被划分成多个 Segment 文件,新的消息会追加到分区的末尾。
-
刷盘策略参数:
- 操作系统通过几个参数来控制刷盘时机,例如
dirty_writeback_centisecs
控制脏数据刷新进程的运行间隔,dirty_expire_centisecs
控制何时将旧数据刷入磁盘,以及dirty_background_ratio
和dirty_ratio
控制异步和同步刷盘的触发条件。
- 操作系统通过几个参数来控制刷盘时机,例如
整个过程通过使用 MMAMP 技术和操作系统的高效刷盘机制,使得 Kafka 在处理大量数据时能够实现高性能和低延迟的数据写入和持久化存储。
MMAP调用原理
1. MMAP 的调用过程
-
Kafka 客户端发送数据到 Broker 端的 Socket Receive Buffer:
- Kafka 客户端首先将数据发送到 Broker 端的 socket receive buffer 中,这是第一次数据拷贝。
-
用户进程调用 mmap() 方法:
- 用户进程通过调用
mmap()
系统调用,将希望访问的文件区域映射到其地址空间。这一过程涉及用户空间到内核空间的切换,上下文从用户态切换为内核态。
- 用户进程通过调用
-
建立内存地址映射:
mmap()
方法在 PageCache(内核的页缓存)和用户进程的内存地址空间之间建立了一个直接的映射关系。这意味着,虽然用户进程感觉到在操作自己的内存,实际上操作的是内核中的数据缓存。
-
上下文切换:
- 完成映射后,上下文再次从内核态切换回用户态,
mmap()
调用返回。此时,用户进程可以通过常规的内存访问方式来读写这些映射区域,无需再进行数据拷贝。
- 完成映射后,上下文再次从内核态切换回用户态,
-
用户进程调用 write() 方法:
- 当用户进程需要将数据写入到映射的区域时,它调用
write()
方法,这导致数据从用户空间拷贝到内核写缓冲区(PageCache),这是第二次数据拷贝。
- 当用户进程需要将数据写入到映射的区域时,它调用
-
异步写入到硬盘:
- 数据写入到 PageCache 后,操作系统会利用后台的异步刷盘机制(DMA Copy 技术),将数据异步写入到硬盘上,这是第三次数据拷贝。
2. 性能优势和资源利用效率
-
减少数据拷贝次数:
- 使用 MMAP 技术,数据在传输和存储过程中减少了一次 CPU 拷贝,即避免了将数据从用户缓冲区拷贝到内核读缓冲区(PageCache)的开销。
-
减少内存空间的使用:
- 由于使用了虚拟内存的特性,内核空间的写缓冲区与用户空间的缓冲区共享同一个物理地址空间,因此节省了一半的内存空间。
-
系统调用和上下文切换次数:
- 虽然使用了
mmap()
和write()
方法,导致了两次系统调用和两次上下文切换,但相对于传统的数据拷贝方式,这种开销是可以接受的,因为在整个数据传输过程中减少了数据拷贝次数。
- 虽然使用了