(转载)从Apache Kafka 重温文件高效读写

        文章转自《从Apache Kafka 重温文件高效读写》,地址:http://calvin1978.blogcn.com/articles/kafkaio.html!

        写的非常不错,转载下来!

0. Overview

卡夫卡说:不要害怕文件系统

它就那么简简单单地用顺序写的普通文件,借力于Linux内核的Page Cache,不(显式)用内存,胜用内存,完全没有别家那样要同时维护内存中数据、持久化数据的烦恼——只要内存足够,生产者与消费者的速度也没有差上太多,读写便都发生在Page Cache中,完全没有同步的磁盘访问。

整个IO过程,从上到下分成文件系统层(VFS+ ext3)、 Page Cache 层、通用数据块层、 IO调度层、块设备驱动层。 这里借着Apache Kafka的由头,将Page Cache层与IO调度层重温一遍,记一篇针对Linux kernel 2.6的科普文。

 

1. Page Cache

1.1 读写空中接力

Linux总会把系统中还没被应用使用的内存挪来给Page Cache,在命令行输入free,或者cat /proc/meminfo,"Cached"的部分就是Page Cache。

Page Cache中每个文件是一棵Radix树(又称PAT位树, 一种多叉搜索树),节点由4k大小的Page组成,可以通过文件的偏移量(如0x1110001)快速定位到某个Page。

当写操作发生时,它只是将数据写入Page Cache中,并将该页置上dirty标志。

当读操作发生时,它会首先在Page Cache中查找内容,如果有就直接返回了,没有的话就会从磁盘读取文件再写回Page Cache。

可见,只要生产者与消费者的速度相差不大,消费者会直接读取之前生产者写入Page Cache的数据,大家在内存里完成接力,根本没有磁盘访问。

而比起在内存中维护一份消息数据的传统做法,这既不会重复浪费一倍的内存,Page Cache又不需要GC(可以放心使用60G内存了),而且即使Kafka重启了,Page Cache还依然在。

 

1.2 后台异步flush的策略

这是大家最需要关心的,因为不能及时flush的话,OS crash(不是应用crash) 可能引起数据丢失,Page Cache瞬间从朋友变魔鬼。

当然,Kafka不怕丢,因为它的持久性是靠replicate保证,重启后会从原来的replicate follower中拉缺失的数据。

在内核3.2之前,由内核线程pdflush负责将有dirty标记的页面,发送给IO调度层。内核会为每个磁盘起一条pdflush线程,每5秒(/proc/sys/vm/dirty_writeback_centisecs)唤醒一次,根据下面三个参数来决定行为:

1. 如果page dirty的时间超过了30秒(/proc/sys/vm/dirty_expire_centisecs,单位是百分之一秒),就会被刷到磁盘,所以OS crash时最多丢30秒左右的数据。

2. 如果dirty page的总大小已经超过了10% (/proc/sys/vm/dirty_background_ratio) 的可用内存 (cat /proc/meminfo里 MemFree+ Cached - Mapped),则会在后台启动pdflush 线程写盘,但不影响当前的write(2)操作。增减这个值是最主要的flush策略调优手段。

3. 如果wrte(2)的速度太快,比pdflush还快,dirty page 迅速涨到 20% (/proc/sys/vm/dirty_ratio) 的总内存 (cat /proc/meminfo里的MemTotal),则此时所有应用的写操作都会被block,各自在自己的时间片里去执行对自己文件的flush操作,因为操作系统认为现在已经来不及写盘了,如果crash会丢太多数据,要让大家都冷静点。这个代价有点大,要尽量避免。在Redis2.8以前,Rewrite AOF就经常导致这个大面积阻塞,现在已经改为Redis每32Mb先主动flush一下了。

详细的文章可以看: The Linux Page Cache and pdflush
 

1.3 主动flush的方式

对于重要数据,应用需要自己触发flush保证写盘。

1. 系统调用fsync() 和 fdatasync()

fsync(fd)将属于该文件描述符的所有dirty page的写入请求发送给IO调度层。

fsync()总是同时flush文件内容与文件元数据, 而fdatasync()只flush文件内容与后续操作必须的文件元数据。元数据含时间戳,大小等,大小可能是后续操作必须,而时间戳就不是必须的。因为文件的元数据保存在另一个地方,所以fsync()总是触发两次IO,性能要差一点。

2. 打开文件时设置O_SYNC,O_DSYNC标志或O_DIRECT标志

O_SYNC、O_DSYNC标志表示每次write后要等到flush完成才返回,效果等同于write()后紧接一个fsync()或fdatasync(),不过按APUE里的测试,因为OS做了优化,性能会比自己调write() + fsync()好一点,但与只是write()相比就慢很多了。注意此时write()还是先写到page cache的,read可以直接从page cache中获取数据。

O_DIRECT标志表示直接IO,完全跳过Page Cache。不过这也放弃了读文件时的Page Cache,必须每次读取磁盘文件,这是与O_SYNC的区别。而且要求所有IO请求长度,偏移都必须是底层扇区大小的整数倍。所以使用直接IO的时候一定要在应用层做好Cache。
 

1.4 Page Cache的清理策略

当内存满了,就需要清理Page Cache,或把应用占的内存swap到文件去。有一个swappiness的参数(/proc/sys/vm/swappiness)决定是swap还是清理page cache,值在0到100之间,设为0表示尽量不要用swap,这也是很多优化指南让你做的事情,因为默认值居然是60,Linux认为Page Cache更重要。

Page Cache的清理策略是LRU的升级版。如果简单用LRU,一些新读出来的但可能只用一次的数据会占满了LRU的头端。因此将原来一条LRU队列拆成了两条,一条放新的Page,一条放已经访问过好几次的Page。Page刚访问时放在新LRU队列里,访问几轮了才升级到旧LRU队列(想想JVM Heap的新生代老生代)。清理时就从新LRU队列的尾端开始清理,直到清理出足够的内存。

 

1.5 预读策略

根据清理策略,Apache Kafka里如果消费者太慢,堆积了几十G的内容,Cache还是会被清理掉的。这时消费者就需要读盘了。

内核这里又有个动态自适应的预读策略,每次读请求会尝试预读更多的内容(反正都是一次读操作)。内核如果发现一个进程一直使用预读数据,就会增加预读窗口的大小(最小16K,最大128K),否则会关掉预读窗口。连续读的文件,明显适合预读。

 

2. IO调度层

如果所有读写请求都直接发给硬盘,对传统硬盘来说太残忍了。IO调度层主要做两个事情,合并和排序。 合并是将相同和相邻扇区(每个512字节)的操作合并成一个,比如我现在要读扇区1,2,3,那可以合并成一个读扇区1-3的操作。排序就是将所有操作按扇区方向排成一个队列,让磁盘的磁头可以按顺序移动,有效减少了机械硬盘寻址这个最慢最慢的操作。

排序看上去很美,但可能造成严重的不公平,比如某个应用在相邻扇区狂写盘,其他应用就都干等在那了,pdflush还好等等没所谓,读请求都是同步的,耗在那会很惨。

所有又有多种算法来解决这个问题,其中内核2.6的默认算法是CFQ(完全公正排队),把总的排序队列拆分成每个发起读写的进程/线程 自己有一条排序队列,然后以时间片轮转调度每个队列,轮流从每个进程的队列里拿出若干个请求来执行(默认是4)。

在Apache Kafka里,消息的读写都发生在内存中,真正写盘的就是那条pdflush内核线程,因为都是顺序写,即使一台服务器上有多个Partition文件,经过合并和排序后都能获得很好的性能,或者说,Partition文件的个数并不影响性能,不会出现文件多了变成随机读写的情况。

如果是SSD硬盘,没有寻址的花销,排序好像就没必要了,但合并的帮助依然良多,所以还有另一种只合并不排序的NOOP算法可供选择。

题外话

另外,硬盘上还有一块几十M的缓存,硬盘规格上的外部传输速率(总线到缓存)与内部传输速率(缓存到磁盘)的区别就在此......IO调度层以为已经写盘了,其实可能依然没写成,断电的话靠硬盘上的电池或大电容保命......

延伸阅读:Kafka文件存储机制那些事 by 美团技术团队

 
文章持续修订,转载请保留原链接: http://calvin1978.blogcn.com/articles/kafkaio.html

时间: 2024-12-20 22:34:31

(转载)从Apache Kafka 重温文件高效读写的相关文章

Apache Kafka是分布式发布-订阅消息系统

转自: http://www.infoq.com/cn/articles/apache-kafka?utm_source=infoq&utm_medium=popular_links_homepage 简介 Apache Kafka是分布式发布-订阅消息系统.它最初由LinkedIn公司开发,之后成为Apache项目的一部分.Kafka是一种快速.可扩展的.设计内在就是分布式的,分区的和可复制的提交日志服务. Apache Kafka与传统消息系统相比,有以下不同: 它被设计为一个分布式系统,易

【译】使用Apache Kafka构建流式数据平台(1)

前言:前段时间接触过一个流式计算的任务,使用了阿里巴巴集团的JStorm,发现这个领域值得探索,就发现了这篇文章--Putting Apache Kafka To Use: A Practical Guide to Building a Stream Data Platform(Part 1).在读的过程中半总结半翻译,形成本文,跟大家分享. 最近你可能听说很多技术名词,例如"流式处理"."事件数据"以及"实时"等,与之相关的技术有Kafka.S

弹性集成Apache Mesos与Apache Kafka框架

本文讲的是弹性集成Apache Mesos与Apache Kafka框架,[编者的话]本文由Mesosphere公司的Derrick Harries和Kafka项目代码提交者Joe Stein合作撰写,介绍了如何将Mesos与Kafka集成以简化海量流数据的管理和配置工作. Kafka和Mesos是非常知名和成功的Apache项目,它们每个都获得了大型社区的支持,还有Confluent和Mesosphere这样的公司各自围着它们构筑生态系统.最近,两家公司合作使Kafka成为Mesosphere

Apache指南: .htaccess文件使用手册

因为在一个国外的空间的根文件夹下看到这个.htaccess,搞不懂是干什么的,在落伍论坛找到一篇文章,先转过来放着,以后再慢慢研究,嘿嘿..      Apache指南: .htaccess文件  .htaccess文件提供了针对目录改变配置的方法.    * .htaccess文件    * 工作原理和使用方法    * 使用.htaccess文件的场合    * 指令的生效    * 认证举例    * 服务器端包含举例    * CGI举例    * 疑难解答  top  .htacces

Apache Kafka的代码实例

前提: 已经配置好kafka.若未安装,可以参照[Apache Kafka]安装升级指南 已在eclipse里面安装scala插件.Eclipse Kepler中在Help->Eclipse Markectplace中搜索Scala,然后安装即可. 使用maven构建kafka测试project在eclipse中. 创建topic:在kafka的安装目录下执行bin/kafka-create-topic.sh --zookeeper 192.168.20.99:2181 --replica 1

什么时候该用RabbitMQ ,什么时候该用 Apache Kafka

人类如何做出决策? 在日常生活中,情感总是短路因素,导致在复杂或压倒性的决定上扣动扳机.但对于做有长期影响,复杂决策,的表意识,不可能是纯粹的冲动.高性能的人通常使用断路器,"本能," "直觉," 或其他的情绪,只有一次他们的表意识,潜意识吸收了作出决定所需的所有事实. 今天有很多的消息传递技术, 数不清的 ESBs, 和近100 iPaaS供应商在市场上. 自然,这导致了有关如何为您的需要-特别是那些已经投资在一个特定的选择,选择正确的消息传递技术的问题 .我们批

Apache Kafka 0.8.2.1 发布,消息发布订阅系统

Apache Kafka 0.8.2.1 发布,该版本修复了 4 个关键 bug: [KAFKA-1919] - Metadata request issued with no backoff in new producer if there are no topics [KAFKA-1952] - High CPU Usage in 0.8.2 release [KAFKA-1971] - starting a broker with a conflicting id will delete

Apache Kafka:大数据的实时处理时代

在过去几年,对于 Apache Kafka 的使用范畴已经远不仅是分布式的消息系统:我们可以将每一次用户点击,每一个数据库更改,每一条日志的生成,都转化成实时的结构化数据流,更早的存储和分析它们,并从中获得价值.同时,越来越多的企业应用也开始从批处理数据平台向实时的流数据数据平台转移.本演讲将介绍最近 Apache Kafka 添加的一些系统架构,包括 Kafka Connect 和 Kafka Streams,并且描述一些如何使用它们的实际应用体验. 注:本文由王国璋在 QCon 北京 201

Neha Narkhede: 借助Apache Kafka实现大规模流处理

在QCon纽约2016大会上,Neha Narkhede在演讲"借助Apache Kafka实现大规模流处理"中介绍了Kafka Streams,这是Kafka用于处理流式数据的新特性.据Narkhede介绍,因为"无界(unbounded)"数据集随处可见,所以流处理越来越流行.那不再是一个像机器学习那样的小众问题. Narkhede首先介绍了数据操作的基本编程范式: 请求/响应周期 批处理 流处理然后,Narkhede提供了一个来自零售领域的流处理实例:销售和发