apache kafka 采用页缓存技术、顺序写入磁盘等技术来提升性能。在顺序读写的情况下,磁盘的顺序读写速度和内存相差无几,PageCache是系统级别的缓存,它把尽可能多的空闲内存当作磁盘缓存使用来进一步提高IO效率;所谓的零拷贝是指将数据直接从磁盘文件复制到网卡设备中,而不需要经由应用程序之手
预备知识一: 页缓存(Page Cache)
缓存能提高I/O性能是基于以下2个重要的原理:
- CPU访问内存的速度远远大于访问磁盘的速度(访问速度差距不是一般的大,差好几个数量级)
- 数据一旦被访问,就有可能在短期内再次被访问(临时局部原理)
- 文件一般存放在硬盘(机械硬盘或固态硬盘)中,CPU 并不能直接访问硬盘中的数据,而是需要先将硬盘中的数据读入到内存中,然后才能被 CPU 访问
- 读写硬盘的速度比读写内存要慢很多,为了避免每次读写文件时,都需要对硬盘进行读写操作,Linux 内核使用 页缓存(Page Cache) 机制来对文件中的数据进行缓存。
- 为了提升对文件的读写效率,Linux 内核会以页大小(4KB)为单位,将文件划分为多数据块。当用户对文件中的某个数据块进行读写操作时,内核首先会申请一个内存页(称为 页缓存 )与文件中的数据块进行绑定。
- 页缓存,也称为磁盘缓存,是计算机随机存取存储器(RAM)的一个区域,用于保存并可能修改存储在硬盘或其他永久存储设备上的数据。
- 页缓存,也称为磁盘缓存,是计算机随机存取存储器(RAM)的一个区域,用于保存并可能修改存储在硬盘或其他永久存储设备上的数据。
- 当从文件中读取数据时,如果要读取的数据所在的页缓存已经存在,那么就直接把页缓存的数据拷贝给用户即可。否则,内核首先会申请一个空闲的内存页(页缓存),然后从文件中读取数据到页缓存,并且把页缓存的数据拷贝给用户。
- 当向文件中写入数据时,如果要写入的数据所在的页缓存已经存在,那么直接把新数据写入到页缓存即可。否则,内核首先会申请一个空闲的内存页(页缓存),然后从文件中读取数据到页缓存,并且把新数据写入到页缓存中。对于被修改的页缓存,内核会定时把这些页缓存刷新到文件中。
预备知识二:「写缓存」常见的有3种策略
- 不缓存(nowrite) :: 也就是不缓存写操作,当对缓存中的数据进行写操作时,直接写入磁盘,同时使此数据的缓存失效
- 写透缓存(write-through) :: 写数据时同时更新磁盘和缓存
- 回写(copy-write or write-behind) :: 写数据时直接写到缓存,由另外的进程(回写进程)在合适的时候将数据同步到磁盘
预备知识三:通用页缓存流程
# DMA direct memory access:直接存储器访问,也就是直接访问RAM,不需要依赖CPU的负载
# CPU :中央核心处理器,主要用于计算,如果用于拷贝就太浪费资源
磁盘文件 ==DMAcopy=> 页缓存 ==CPUcopy=> 用户空间缓存 ==CPUcopy=> Socket缓存 ==DMAcopy=>> 网卡
页缓存减少了连续读写磁盘文件的次数,操作系统自动控制文件块的缓存与回收生命周期,用访问RAM的缓存代替访问磁盘区域的机制,增强查询效率。
Linux操作系统中的vm.dirty_background_ratio参数用来指定当脏页数量达到系统内存的百分之多少之后就会触发pdflush/flush/kdmflush等后台回写进程的运行来处理脏页,一般设置为小于10%的值即可,但不建议设置为0.与这个参数对应的还一个vm.dirty_ratio参数,它用来指定当脏页数量达到系统内存的百分之多少之后就不得不开始对脏页进行处理,在此过程中,新的I/O请求会被阻挡直至所有脏页被冲刷到磁盘中。
kafka页缓存技术如何实现
Kafka中大量使用了页缓存,
- 消息都是先被写入页缓存,然后由操作系统负责具体的刷盘任务
- 在Kafka中同样提供了同步刷盘及间断性强制刷盘(fsync)的功能,可以通过log.flush.interval.message、log.flush.interval.ms等参数来控制。
- 同步刷盘可以提高 消息的可行性,防止由于机器掉电等异常造成处于页缓存而没有及时写入磁盘的消息丢失。
- 一般不建议做同步刷盘,刷盘任务就应交由操作系统去调配,消息的可靠性应该由多副本机制来保障,而不是由同步刷盘这种严重影响性能的行为来保障
MMFile (Memory Mapped File):
- (简称 mmap)也被翻译成内存映射文件 ,在 64 位操作系统中一般可以表示 20G 的数据文件,它的工作原理是直接利用操作系统的 Page 来实现文件到物理内存的直接映射。
- 完成映射之后你对物理内存的操作会被同步到硬盘上(操作系统在适当的时候)。
- 通过 mmap,进程像读写硬盘一样读写内存(当然是虚拟机内存),也不必关心内存的大小,有虚拟内存为我们兜底。
kafka的零拷贝是什么意思?
场景描述:把磁盘中的某个文件内容发送到远程服务器上,那么他必须经过几个拷贝过程
- 从磁盘中去读取目标文件的内容拷贝到内核缓冲区中
- 把内核缓冲区的数据拷贝到用户空间的缓冲区中
- 在应用程序中调用write()方法把用户空间缓冲区的数据拷贝到内核空间的socket Buffer中
- 把在内核模式下的socket Buffer中的数据赋值到网卡缓冲区,
- 最后网卡缓冲区再把数据传输到目标服务器上。
在这个过程中我们发现数据从磁盘到最终发送出去要经历4次拷贝,而在这4次拷贝过程中,有两次拷贝是浪费的,
1. 从内核空间拷贝到用户空间
2. 从用户空间再次拷贝到内核空间。
所谓的零拷贝就是把这两次多余的拷贝忽略掉。应用程序可以直接把磁盘中的数据从内核中直接传输到socket.
零拷贝是一种为了解决数据从内核缓存到用户缓存的CPU拷贝产生的性能消耗的技术。
原理:当数据从磁盘经过DMA copy到页缓存(内核缓存)后,为了减少CPU拷贝的性能损耗,操作系统会将该内核缓存与用户层进行共享,减少一次CPU copy过程,同时用户层的读写也会直接访问该共享存储,本身由用户层到Socket缓存的数据拷贝过程也变成了从 内核到内核的CPU拷贝过程,更加的快速。
磁盘文件 ==DMAcopy=> 【页缓存并共享作为用户空间缓存】 ==CPUcopy=> Socket缓存 ==DMAcopy=>> 网卡
kafka的顺序读写
- 硬盘是机械结构,每次读写都会“寻址”,其中寻址是一个“机械动作”,是最耗时的;顺序I/O比随机I/O快,为了提高读写硬盘的速度,Kafka 就是使用顺序 I/O
- 在顺序读写的情况下,磁盘的顺序读写速度和内存相差无几;
顺序写磁盘就是在一个磁道连续的写入,数据都排在一起,分布在连续的磁盘扇区,主需要一次寻址就能找到对应的数据,而kafka本身的数据是不需要删除数据的,是已追加的方式写到磁盘,所以这样就能保证磁盘数据连续紧凑,同时kafka是以segment log flie进行分段存储的,每次访问磁盘文件的时候只需要寻址最后一个segment file的磁盘空间,能够保证写入和读取的效率。