大数据-71 Kafka 高级特性 物理存储 磁盘存储特性 如零拷贝、页缓存、mmp、sendfile

本文涉及的产品
云原生大数据计算服务 MaxCompute,5000CU*H 100GB 3个月
云原生大数据计算服务MaxCompute,500CU*H 100GB 3个月
日志服务 SLS,月写入数据量 50GB 1个月
简介: 大数据-71 Kafka 高级特性 物理存储 磁盘存储特性 如零拷贝、页缓存、mmp、sendfile

点一下关注吧!!!非常感谢!!持续更新!!!

目前已经更新到了:

Hadoop(已更完)

HDFS(已更完)

MapReduce(已更完)

Hive(已更完)

Flume(已更完)

Sqoop(已更完)

Zookeeper(已更完)

HBase(已更完)

Redis (已更完)

Kafka(正在更新…

章节内容

上节我们完成了如下内容:


日志删除 日志清理

基于时间删除、基于日志大小、基于偏移量

日志压缩、压缩细节、清理器配置

c82a8ce818e2ab0600b97214c80c0ef6_3e9b7caaff0e4438bc42d69f2fffc40f.png

磁盘存储

零拷贝

Kafka高性能,是多方面协同的结果,包括宏观架构、分布式Partition存储、ISR数据同步、以及“无所不用其极”的高效利用磁盘、操作系统特性。

零拷贝并不是不需要拷贝,而是减少不必要的拷贝次数。通常是说在IO读写过程中。

Nginx的高性能中也有零拷贝的身影。


传统IO

比如:读取文件,Socket发送

传统实现方式:先读取、再发送、实际经过1-4次Copy

buffer = File.read
Socket.send(buffer)

第一次:将磁盘文件,读取到操作系统内核缓冲区

第二次:将内核缓冲区的数据,Copy到Application应用程序的Buffer

第三次:将Application应用程序Buffer中的数据,Copy到Socket网络发送缓冲区(数据操作系统内核的缓冲区)

第四次:将Socket Buffer的数据,Copy到网络协议栈,由网卡进行网络传输。

8e945207d7028536b9360e0afb97f99b_331fdde755994915bd51cb2babb15a44.png

实际IO读写,需要进行IO中断,需要CPU响应中断(内核态到用户态转换),尽管引入 DMA(Direct Memory Access,直接存储器访问)来接管CPU的中断请求,但四次copy是存在不必要拷贝的。

实际上并不需要第二个和第三个副本,数据可以直接从读缓存区传输到套接字缓存。


Kafka的两个过程:


网络数据持久化到磁盘(Producer到Broker)

磁盘文件通过网络发送(Broker到Consumer)

数据落盘通常都是非实时的,Kafka的数据并不是实时写入磁盘,它充分利用了现代操作系统分页存储来利用内存提高IO效率。

磁盘文件通过网络发送

Broker到Consumer

磁盘数据通过DMA拷贝到内核Buffer,直接通过DMA拷贝到NIC Buffer(Socket Buffer),无需CPU拷贝。

除了减少数据的拷贝外,整个读文件=>网络发送由一个sendfile调用完成,整个过程只有两次上下文切换,因为大大提高了性能。

JavaNIO对sendfile的支持就是FileChannel.transferTo()、transferFrom()。fileChannel.transferTo(position, count, socketChannel);

把磁盘文件读取OS内核缓冲区后的fileChannel,直接转给socketChannel发送,底层就是sendfile。消费者从broker读取数据,就是由此实现。

具体来看,Kafka的数据传输通过TransportLayer来完成,其子类PlaintextTransportLayer通过JavaNIO的FileChannel的transferTo和transferFrom实现零拷贝。

注:

  • transferTo 和 transferFrom 并不保证一定能使用零拷贝,需要操作性系统支持
  • Linux2.4+ 内核通过sendfile系统调用,提供了零拷贝
  • 页缓存

页缓存是操作系统实现的一种主要磁盘缓存,以此来减少对磁盘的IO操作。具体来说,就是把磁盘中的数据存到闪存中,把对磁盘访问变为内存访问。

Kafka接收来自SocketBuffer的网络数据,应用进程不需要中间处理、直接进行持久化时。可以使用mmap内存文件映射。


Memory Mapped Files

简称mmap,简单描述的作用是:将磁盘文件映射到内存,用户通过修改内存就能修改磁盘文件。它的工作原理是直接利用操作系统的Page来实现磁盘文件到物理内存的直接映射。完成映射之后你对物理内存的操作会被同步到磁盘上(操作系统在合适的时候完成)。

11454fed553261acc9e88e7a55e613e9_c39f9b184f034ddb8aa89977b10a3667.png

通过mmap,进程读写硬盘一样读写内存(当然是虚拟机内存),使用这种方式可以获取很大的IO提升,省去了用户空间到内核空间复制的开销。

mmap也有一个很明显的缺陷:不可靠,写到mmap中的数据并没有真正的写入到磁盘中,操作系统会在程序主动调用flush的时候才会把数据真正写入到硬盘。


Kafka提供了一个 producer.type 来控制是不是主动flush。

如果Kafka写入到mmap之后就立即flush然后再返回Product叫同步(sync)

写入mmap之后立即返回Producer不调用flush叫做异步(async)。

JavaNIO对文件映射支持

JavaNIO,提供了MappedByteBuffer类可以实现内存映射,MapperByteBuffer只能通过调用FileChannel的map()取得。再没有其他方式。

FileChannel.map() 是抽象方法,具体实现是在FileChannel.map()可自行查看JDK源码,其map0()方法就是调用了Linux内核的mmap的API。

使用MappedByteBuffer类要注意的是:

mmap的文件映射,在 full gc时才会进行释放。当close时,需要手动清除内存映射文件,可以反射调用sun.misc.Cleaner方法。

当一个进程准备读取磁盘上的文件内容时:


操作系统会先查看待读取的数据所在的页(page)是否在页缓存中(pagecache)中,如果存在(命中)则直接返回数据,从而避免了物理磁盘的IO操作。

如果没有命中,则操作系统会向磁盘发起读取请求并将读取的数据存入入页缓存,之后再将数据返回给进程。

如果一个进程需要将数据写入磁盘:


操作系统也会检测数据对应的页是否在页缓存中,如果不存在,则会先在页缓存中添加相应的页,最后将数据写入对应的页。

被修改过后的页也就变成了脏页,操作系统会在合适的时间把脏页中的数据写入磁盘,以保持数据的一致性。

对一个进程而言,它会在进程内部缓存处理所需的数据,然而这些数据有可能还缓存在操作系统的页缓存中,因此同一份数据可能被缓存了两次。并且,除非使用DirectIO的方式,否则页缓存很难被禁止。

当使用页缓存的时候,即使Kafka服务重启,页缓存还是会保持有效,然而进程内的缓存却需要重建。这样也极大的简化了代码逻辑,因为维护页缓存和文件之间的一致性交由操作系统负责,这样会比进程内维护更加完全有效。

Kafka中有大量使用了页缓存,这是Kafka实现高吞吐的重要因素之一,消息先被写入页缓存,由操作系统负责刷盘任务。


顺序写入

操作系统可以针对线性读写做深层次的优化,比如预读(Read-ahead,提前将一个比较大的磁盘快读入内存)和后写(write-behind,将很多消的逻辑写操作合并起来组成一个大的物理操作)技术。

3a580e87419c8523d0b4f06e54f6a381_9cd3ae3c57f145ab81879ae8e04bbc35.png

Kafka在设计时采用了文件追加的方式来写入消息,即只能在日志文件的尾部追加新的消息,并且也不允许修改已写入的消息,这种方式属于典型的顺序写盘的操作,所以就算Kafka使用磁盘作为存储介质,也能承载非常大的吞吐量。


mmap 和 sendfile

Linux内核提供,实现零拷贝的API

sendfile 是将读到内核空间的数据,转到SocketBuffer,进行网络发送。

mmap将磁盘文件映射到内存,支持读和写,对内存的操作会反映在磁盘文件中。

RocketMQ在消费消息时,使用了mmap

Kafka使用了sendfile

Kafka速度快是因为

partition顺序读写,充分利用磁盘特性,这是基础。

producer生产的数据持久化到broker,采用mmap文件映射,实现顺序的快速写入

customer从broker读取数据,采用sendfile,将磁盘文件读到OS内核缓冲区中,直接转到SocketBuffer进行网络发送。


目录
相关文章
|
1月前
|
消息中间件 监控 数据可视化
大数据-79 Kafka 集群模式 集群监控方案 JavaAPI获取集群指标 可视化监控集群方案: jconsole、Kafka Eagle
大数据-79 Kafka 集群模式 集群监控方案 JavaAPI获取集群指标 可视化监控集群方案: jconsole、Kafka Eagle
47 2
|
1月前
|
消息中间件 关系型数据库 MySQL
大数据-117 - Flink DataStream Sink 案例:写出到MySQL、写出到Kafka
大数据-117 - Flink DataStream Sink 案例:写出到MySQL、写出到Kafka
121 0
|
1月前
|
消息中间件 分布式计算 NoSQL
大数据-104 Spark Streaming Kafka Offset Scala实现Redis管理Offset并更新
大数据-104 Spark Streaming Kafka Offset Scala实现Redis管理Offset并更新
38 0
|
1月前
|
消息中间件 存储 分布式计算
大数据-103 Spark Streaming Kafka Offset管理详解 Scala自定义Offset
大数据-103 Spark Streaming Kafka Offset管理详解 Scala自定义Offset
75 0
|
1月前
|
消息中间件 分布式计算 监控
大数据-78 Kafka 集群模式 集群的应用场景与Kafka集群的搭建 三台云服务器
大数据-78 Kafka 集群模式 集群的应用场景与Kafka集群的搭建 三台云服务器
59 6
|
1月前
|
存储 缓存 分布式计算
大数据-89 Spark 集群 RDD 编程-高阶 编写代码、RDD依赖关系、RDD持久化/缓存
大数据-89 Spark 集群 RDD 编程-高阶 编写代码、RDD依赖关系、RDD持久化/缓存
41 4
|
1月前
|
消息中间件 存储 druid
大数据-156 Apache Druid 案例实战 Scala Kafka 订单统计
大数据-156 Apache Druid 案例实战 Scala Kafka 订单统计
37 3
|
1月前
|
消息中间件 druid 大数据
大数据-153 Apache Druid 案例 从 Kafka 中加载数据并分析(二)
大数据-153 Apache Druid 案例 从 Kafka 中加载数据并分析(二)
29 2
|
1月前
|
消息中间件 大数据 Kafka
大数据-77 Kafka 高级特性-稳定性-延时队列、重试队列 概念学习 JavaAPI实现(二)
大数据-77 Kafka 高级特性-稳定性-延时队列、重试队列 概念学习 JavaAPI实现(二)
26 2
|
1月前
|
消息中间件 分布式计算 druid
大数据-153 Apache Druid 案例 从 Kafka 中加载数据并分析(一)
大数据-153 Apache Druid 案例 从 Kafka 中加载数据并分析(一)
49 1
下一篇
无影云桌面