原文地址://www.greatytc.com/p/e0f23cb5b0e0
前言:讲解透彻,从存储到系统,硬件,网络细节综合考虑
问:讲讲kafka为什么这么快?
答:kafka的快是从底层设计,到充分利用硬件,系统,压缩等等特性,综合产生的结果。
要理解 kafka为什么这么快,要从以下几个方面如下:
1.磁盘读写原理
2.利用Pagecache+mmap
3.零拷贝
4.存储设计
5.批量读写
6.批量压缩
7.消息写入过程
8.消息读取过程
一 磁盘读写原理
磁盘的结构图:
图片
当需要从磁盘读取数据时,要确定读的数据在哪个磁道,哪个扇区:
首先必须找到柱面,即磁头需要移动对准相应磁道,这个过程叫做寻道,所耗费时间叫做寻道时间;
然后目标扇区旋转到磁头下,这个过程耗费的时间叫做旋转时间;
一次访盘请求(读/写)完成过程由三个动作组成
寻道(时间):磁头移动定位到指定磁道;
旋转延迟(时间):等待指定扇区从磁头下旋转经过;
数据传输(时间):数据在磁盘、内存与网络之间的实际传输
由于存储介质的特性,磁盘本身存取就比主存慢,再加上机械运动耗费,磁盘的存取速度往往是主存的几百分之一甚至几千分之一
怎么样才能提高磁盘的读写效率呢?
根据数据的局部性原理﹐有以下两种方法
预读或者提前读;
合并写——多个逻辑上的写操作合并成一个大的物理写操作中;
即采用磁盘顺序读写(不需要寻道时间,只需很少的旋转时间)。仅仅将数据追加到文件的末尾,不是在文件的随机位置来修改数据,磁盘顺序写的性能基本上可以跟写内存的性能相差无几。
实验结果:在一个6 7200rpm - SATA RAID-5 的磁盘阵列上线性写的速度大概是300M/秒,但是随机写的速度只有50K/秒,两者- 相差将近10000倍。
二 利用page cache+mmap
1.page cache
图片
执行free -m命令,看内存使用情况,大家都做过,有几个名词需要解释下:
Page Cache : 以 Page为单位,缓存文件内容.cached这列代表当前页缓存的占用量。
Buffer cache :磁盘等块设备的缓存.Buffers列代表块缓存的使用量.
数据读写的过程大致如下图:
Page Cache(os cache):【操作系统自己管理的缓存】以Page为单位,缓存文件内容。缓存在Page Cache中的文件数据,能够更快的被用户读取。同时对于带buffer的写入操作,数据在写入到Page Cache中即可立即返回,而不需等待数据被实际持久化到磁盘,进而提高了上层应用读写文件的整体性能。cached这列的数值表示的是当前的页缓存(page cache)的占用量,page cache文件的页数据,页是逻辑上的概念,因此page cache是与文件系统同级的
buffer cache:磁盘等块设备的缓冲,内存的这一部分是要写入到磁盘里的 。buffers列 表示当前的块缓存(buffer cache)占用量,buffer cache用于缓存块设备(如磁盘)的块数据。块是物理上的概念,因此buffer cache是与块设备驱动程序同级的。
Kafka 在写入磁盘文件的时候,可以直接写入这个 os cache 里,也就是仅仅写入内存中,接下来由操作系统自己决定什么时候把 os cache 里的数据真的刷入磁盘文件中。通过这一个步骤,就可以将磁盘文件 写性能 提升很多了,因为其实这里相当于是在写内存,不是在写磁盘,原理图如下:
image.png
图片
问:利用page cache的好处是什么呢?
答:
1.避免broker内存消耗.
2.避免GC问题.
3.应用重启数据不会丢失.
相比于JMM或者In-memory cache , page cache优势:
1.首先,操作系统层面的缓存利用率会更高,因为存储的都是紧凑的字节结构而不是独立的对象。
2.其次,操作系统本身也对于Page Cache做了大量优化,提供了write-behind、 read-ahead 以及 flush等多种机制.
3.再者,即使服务进程重启,系统缓存依然不会消失,避免in-process cache重建缓存的过程
2.什么是mmap
问:mmap 又是什么呢?有什么优势?
答:Mmap即是Memory Mapped Files内存文件映射。
大致意思如下图:
mmap:
mmap其实就是把物理上的磁盘文件的一些地址和page cache地址进行一层映射,使得进程像读写硬盘一样读写内存,定时有os帮助我们将数据刷写到磁盘,30 s.
三 、零拷贝
问:能详细讲解一下零拷贝吗?
答:零拷贝要想明白必须要了解linux,的用户态,内核态,以及磁盘的数据是如何通过网卡发送到消费者的。
从系统安全和保护的角度出发,在进行计算机体系结构设计时,处理机的执行模式一般分为两种:
分别称为内核模式(内核态)和用户模式(用户态)。
当处理机处于内核模式执行时,意味着系统除了可以执行一般指令外,还可以执行特权指令,即可以执行访问各种控制寄存器的指令、I/O指令以及程序状态字。
当处理机处于用户模式执行时,只能执行一般指令,而不允许执行特权指令。
这样做可以保护核心代码不受用户程序有意和无意的攻击。
显然,处理机在运行期间需要在内核模式和用户模式之前进行切换。
应用程序要将磁盘数据通过网卡发出去非零拷贝需要:
a.操作系统将数据从磁盘拷贝到内核区的 pagecache
b.用户程序将内核区的 pagecache拷贝到用户区缓存
c.用户程序将用户区的缓存拷贝到socket缓存中
d.操作系统将socket缓存中的数据拷贝到网卡的 buffer 上发送数据
大致流程图如下:
可以发现一次IO请求操作进行了2次上下文切换和4次系统调用,而同一份数据在缓存中多次拷贝,实际上对于拷贝来说完全可以直接在内核态中进行,也就是省去第2和第3步骤,变成这样:
kafka 集群经过良好的调优,数据直接写入 os cache 中,然后读数据的时候也是从 os cache 中读。相当于 Kafka 完全基于内存提供数据的写和读了,所以这个整体性能会极其的高。
四、 存储设计
1.topic进行分区 -->partition
2.partition为了方便超时删除等管理,又进一步划分segment
3.每个saement.又包括了index文件和 log 文件,可以二分查找快速定位数据.
4.segment 数据只允许追加的形式.
5.offset是连续的支持预读和批量写.
五 、批量发送
很多情况下,系统的瓶颈不是cpu,内存,磁盘io,而是网络IO。为了减少网络通信的次数,往往需要批量发送,kafka支持。
batch.size消息条数积累到该阈值,立即发送.
linger.ms不管消息有没有积累足够条数,超过该时间就立即发送
六 、压缩
对于消息队列很多情况下,系统的瓶颈不是cpu,内存,磁盘io,而是网络IO.
还有些公司是千兆网卡,随便多启动几个流任务就有可能把 kafka流量打满.
所以压缩对于kafka来说节省网络IO是很有必要的,虽然会消耗一定cpu.
广域网上传输的数据,节省带宽,就是节省钱!
如果每个消息都压缩,但是压缩率相对很低,所以Kafka使用了批量压缩,即将多个消息一起压缩而不是单个消息压缩
Kafka允许使用递归的消息集合,批量的消息可以通过压缩的形式传输并且在日志中也可以保持压缩格式,直到被消费者解压缩
Kafka支持多种压缩协议,包括Gzip和Snappy压缩协议
七、 消息写的过程
生产者发送批量压缩的数据到broker,broker通过MappedByteBuffer的map()函数映射其地址到你的虚拟内存地址。
接着就可以对这个MappedByteBuffer执行写入操作了,写入的时候他会直接进入PageCache中,
然后过一段时间之后,由os的线程异步刷入磁盘中,可以看上面的示意图。
上图中似乎只有一次数据拷贝的过程,他就是从PageCache里拷贝到磁盘文件里而已!这个就是你使用mmap技术之后,相比于传统磁盘IO的一个性能优化
八 消息读的过程
图片
读取数据的时候,会先判断page cache中是否存在,存在就可以直接从page cache中消费,所以消费实时数据就会速度快很多。
但是消费历史数据就不得不将历史数据重新加载到page cache,而且会污染掉page cache。
PageCache技术在加载历史数据的时候,还会将你加载的数据块的临近的其他数据块也一起加载到PageCache里去,这其实就是一个预读过程,对于需要连续读取历史数据的,也是性能的不小优化。