转自 http://blog.tao.ma/?p=58node
这篇文章是淘宝内核组的刘峥同窗在内部技术论坛上发表的一篇文章,可是因为刘峥同窗目前没有blog,征得本人赞成,贴在个人blog上,若是你们喜欢,请去新浪微博关注他。:)性能
日前线上在升级到Ext4文件系统后出现应用写操做延迟开销增大的问题。形成这一问题的根源目前已经查明,是因为Ext4文件系统的一个新特性——Delay Allocation形成的。(后面简称delalloc)测试
在详细分析这一问题以前,先来介绍一下Ext4文件系统的delalloc特性。这一特性简要归纳起来就是将之前在buffer IO中每次写操做都会涉及的磁盘块分配过程推迟到数据回写时再进行。咱们知道,在进行Buffer Write时,系统的实际操做仅仅是为这些数据在操做系统内分配内存页(page cache)并保存这些数据,等待用户调用fsync等操做强制刷新或者等待系统触发定时回写过程。在数据拷贝到page cache这一过程当中,系统会为这些数据在磁盘上分配对应的磁盘块。优化
而在使用delalloc后,上面的流程会略有不一样,在每次Buffer Write时,数据会被保存到page cache中,可是系统并不会为这些数据分配相应的磁盘块,仅仅会查询是否有已经为这些数据分配过磁盘块,以便决定后面是否须要为这些数据分配磁盘块。在用户调用fsync或者系统触发回写过程时,系统会尝试为标记须要分配磁盘块的这些数据分配磁盘块。这样,文件系统能够为这些属于同一个文件的数据分配尽可能连续的磁盘空间,从而优化后续文件的访问性能(由于传统机械硬盘顺序读写的性能要比随机读写好不少)。操作系统
了解完delalloc特性的工做过程后,咱们开始分析线上遇到的问题。线上应用的I/O模式能够简化为一个单线程追加写操做的程序,每秒写入二、3M数据,写操做后等待系统自动将数据回写到磁盘。在使用delalloc后,每次Buffer Write操做,系统都会去查询数据是否分配了磁盘块,这一过程须要得到一把读锁 (i_data_sem)。因为这时尚未触发回写操做,所以能够顺利获取i_data_sem,系统完成数据拷贝工做,并返回。因为仅仅是内存拷贝的过程,因此这一操做速度至关快。当系统开始进行回写操做时,系统会成批为数据分配磁盘块,这一过程一样须要获取i_data_sem,而且须要加写锁以保证数据的一致性。因为使用delalloc后,须要分配的磁盘块比nodelalloc状况下多不少(nodelalloc状况下每5秒文件系统会提交日志触发回写;delalloc状况下,系统会在约每30秒左右触发一次回写),所以这一延迟时间较长。若是这时应用程序进行一次Buffer Write,则该操做在尝试得到i_data_sem时会等待上述磁盘块分配完成。由此形成写操做等待很长时间,从而影响应用程序的响应延迟。线程
在上面的分析中已经提到,delalloc是将屡次磁盘块分配的过程合并到一次中来进行,那么是否真如预想的那样,delalloc的平均延迟会小于nodelalloc的状况呢?咱们使用fio来作以下测试:设置bs=4k,单线程每秒追加写入5M,程序运行3分钟,咱们来看一下最后fio对延迟的统计结果:日志
delalloc:
lat (usec): min=2 , max=193466 , avg= 5.86, stdev=227.91blog
nodelalloc:
lat (usec): min=3 , max=16388 , avg= 7.00, stdev=28.92内存
从上面的统计结果看,写操做的平均延迟:打开delalloc后为5.86us,关闭delalloc后为7.00us;最小延迟delalloc为2us,nodelalloc为3us;可是最大延迟delalloc为193.466ms,nodelalloc下仅为16.388ms。可见delalloc确实将多个写操做请求集中到了一块儿来进行。所以在提供较低平均延迟的状况下,会形成某次写操做的延迟较大。rem
经过上面的分析能够看到,目前会受到Ext4的delalloc特性影响的应用必须具有以下条件:
0. Buffer IO
1. 写操做过程当中会涉及磁盘块的分配,主要是记录日志这类追加写操做;
2. 每次写操做后没有刷新数据,而是等待系统自动进行回写;
3. 对延迟有较高要求。
解决方法:关闭delalloc1. mount -t ext4 -o remount,nodelalloc /${dev} /${mnt};2. 编辑/etc/fstab中相关mount项,添加nodelalloc挂载参数