kafka的设计基于一种非常简单的指导思想:不是要在内存中保存尽可能多的数据,在需要时将这些数据刷新(flush)到文件系统,而是要做完全相反的事情。所有数据都要立即写入文件系统中持久化的日志中,但不进行刷新数据的任何调用。实际中这样做意味着,数据被传输到os内核的页面缓存中了,os随后会将这些数据刷新到磁盘。
大家普遍为“磁盘很慢”,因而人们都对持久化(persistent structure)结构能够提供说得过去的性能抱有怀疑态度。实际上,同人们的期望值相比,磁盘可以说是既很慢又很快,这取决决于磁盘的使用方式。设计的很好的磁盘结构可以和网络一样快。在一个由6个7200rpm的sata硬盘组成的raid-5磁盘阵列上,线性写入(linear write)的速度大约是600mb/秒,但随机写入却只有100k/秒,其中的差距接近6000倍。
kafka并没有在内存中创建缓冲区,然后再向磁盘write的方法,而是直接使用了pagecache。
os在文件系统的读写上已经做了太多的优化,pagecache就是其中最重要的一种方法.
由于这些因素,使用文件系统并依赖于pagecache页面缓存要优于自己在内存中维护一个缓存或者什么其他别的结构。
当写操作发生时,它只是将数据写入page cache中,并将该页置上dirty标志。
当读操作发生时,它会首先在page cache中查找内容,如果有就直接返回了,没有的话就会从磁盘读取文件再写回page cache。
可见,只要生产者与消费者的速度相差不大,消费者会直接读取之前生产者写入page cache的数据,大家在内存里完成接力,根本没有磁盘访问。而比起在内存中维护一份消息数据的传统做法,这既不会重复浪费一倍的内存,page cache又不需要gc(可以放心使用大把内存了),而且即使kafka重启了,page cache还依然在。
不能及时flush的话,os crash(不是应用crash) 可能引起数据丢失;
内核线程pdflush负责将有dirty标记的页面,发送给io调度层。内核会为每个磁盘起一条pdflush线程,每5秒(/proc/sys/vm/dirty_writeback_centisecs)唤醒一次,根据下面三个参数来决定行为:
/proc/sys/vm/dirty_expire_centiseconds
:如果page dirty的时间超过了30秒(单位是10ms),就会被刷到磁盘,所以crash时最多丢30秒左右的数据。
/proc/sys/vm/dirty_background_ratio
:如果dirty page的总大小已经超过了10%的可用内存(cat /proc/meminfo里 memfree+ cached - mapped),则会在后台启动pdflush 线程写盘,但不影响当前的write(2)操作。增减这个值是最主要的flush策略里调优手段。
/proc/sys/vm/dirty_ratio
:如果wrte(2)的速度太快,比pdflush还快,dirty page 迅速涨到 10%的总内存(cat /proc/meminfo里的memtotal),则此时所有应用的写操作都会被block,各自在自己的时间片里去执行flush,因为操作系统认为现在已经来不及写盘了,如果crash会丢太多数据,要让大家都冷静点。这个代价有点大,要尽量避免。在redis2.8以前,rewrite aof就经常导致这个大面积阻塞,现在已经改为redis每32mb先主动flush()一下了。
如对本文有疑问, 点击进行留言回复!!
HBase Filter 过滤器之FamilyFilter详解
去 HBase,Kylin on Parquet 性能表现如何?
如何找到Hive提交的SQL相对应的Yarn程序的applicationId
网友评论