rhel6(内核版本2.6.32-220)上写性能回退的分析

转自:

http://hi.baidu.com/casualfish/item/8bd452a6463a83268919d39e


 最近在rhel6.2上进行产品性能测试的过程中,发现在同等硬件条件的情况下,相对于rhel 5.5出现了写性能回退的情况。在排除glibc和其他依赖库版本改变影响性能的可能性以后,可以肯定问题出现在新版本内核的某些改变上了。

背景介绍:

        通过在lkml上搜索查阅,初步判断为2.6.32内核将pdflush线程改为per-bdi-writeback引起,具体原因如下链接:http://thread.gmane.org/gmane.linux.kernel/1100217,pdflush与per bdi write-back介绍:http://lwn.net/Articles/326552/。pdflush是linux内核从出现就一直存在的缓存写回机制,也是操作系统教程中关于缓存和数据同步部分内容的标准实现。而per-bid-writeback则是最近出现的。lkml上关于page writeback的讨论:http://thread.gmane.org/gmane.linux.kernel/892761/focus=893269,从中可以看出akpm对于内核开发者如此轻率的将经过数千万台机器验证的pdflush机制替换有些不满,而我们也悲催地踩到这颗地雷了:(

我的分析调查如下,分析过程中使用ext3文件系统:
2.6.32内核bdi线程调用栈:
0xc04f2450 : do_writepages+0x0/0x30 [kernel]
0xc054db1b : writeback_single_inode+0xbb/0x270 [kernel]
0xc054dee1 : writeback_sb_inodes+0xc1/0x180 [kernel]
0xc054e02b : writeback_inodes_wb+0x8b/0x150 [kernel]
0xc054e313 : wb_writeback+0x223/0x310 [kernel]
0xc054e541 : wb_do_writeback+0x141/0x1d0 [kernel]
0xc054e612 : bdi_writeback_task+0x42/0x160 [kernel]
0xc04fe287 : bdi_start_fn+0x67/0xc0 [kernel]
0xc0475ea4 : kthread+0x74/0x80 [kernel]
0xc0409fff : kernel_thread_helper+0x7/0x10 [kernel]
2.6.32内核sync_supers线程调用栈:
0xc052c610 : sync_supers+0x0/0xb0 [kernel]
0xc04fd59e : bdi_sync_supers+0x2e/0x40 [kernel]
0xc0475ea4 : kthread+0x74/0x80 [kernel]

其中sync_supers对于ext3没有效果,ext3没有实现write_super函数,同时sync_supers线程可能会在将来的kernel中消失:http://thread.gmane.org/gmane.linux.kernel/1326181

值得一提的是kernel开发者对于新的writeback机制也在不断的改进中,最近的改变就是在3.2的内核中wufengguang(内核vfs新的预读功能也是他实现的)引进的bdi的io限流功能,这个功能解决了部分情况下新的writeback引发的性能问题,详见http://thread.gmane.org/gmane.linux.kernel.mm/68826

淘宝的工程师们在工作中也遇到了由于per bdi writeback引发的性能回退问题,详见这个链接

经过测试,使用这个patch并没有太大的性能改进,这个patch主要停止了对于单个inode的不断刷新写入的操作,保证了刷新操作的公平性。我也使用了http://thread.gmane.org/gmane.linux.kernel/1101387中的patch来减少元数据刷新的频率,性能也没有明显的改进。

问题分析:

1>使用blktrace工具研究block device的操作记录分析新的writeback策略对于性能的影响。
使用systemtap在block/blk-core.c文件中submit_bio处对最终调用block层写入的磁盘扇区位置进行追踪,明显可以看出2.6.18-194.el5内核的pdflush线程写入的数据比新版内核的fush线程写入的数据连续性要好,而连续性差就表明了磁盘会花费更多的时间在seek上,导致性能下降。由于我们的产品在写入的过程中会生成大量的小文件,而2.6.32-220的内核中对于这些文件产生的元数据生成的dirty pages也采取了和数据相同的刷新策略,大量的元数据修改引发了刷新时的性能问题。

2>在查看了writeback部分的代码,了解其逻辑之后,我使用了如下systemtap脚本来显示在一次writeback中操作了那些inode。

probe begin

{

        print("starting probe...\n")

}


probe kernel.function("wb_writeback")

{

        print("*****wb_writeback begin****\n")

}


probe kernel.function("wb_writeback").return

{

        print("**********end**************\n")

}


probe kernel.function("writeback_single_inode")

{

        printf("%s(%d) %d\n", execname(), pid(), $inode->i_ino)

}

经过测试重现后,发现在出现问题时每次writeback时操作的inode数目很多,结合之前得到的数据,这些不同的inode的脏页对应磁盘上的数据也不连续。而写入大文件后,每次操作的inode数量明显减少,而且也出现了更为合理的对writeback涉及的inode进行轮换写入的输出,更少的inode保证了数据在磁盘上更好的连续性。

3>为了查明问题的根本原因,我简化了产品的写模式,设计了以下测试用例:

使用iozone写入总量30GB的数据,有两种写入方式:

1.16MB的文件*1920个文件

2.2GB的文件*15个文件

结果在2.6.32-220上1所用的时间是2的2倍,在2.6.18-194中相差不大,使用latencytop进行观察,发现1中有90%以上的时间消耗在fsync上,通过查看源码,确认iozone会在文件写入完成,关闭之前进行fsync操作,进一步阅读ext3_sync_file代码和Documentation/filesystems/ext3.txt之后,发现新版内核为了保障数据安全性,将barrier作为了mount时的默认选项,barrier可以保证数据写入的顺序,但也在io调用路径上加入了不少等待,2.6.18-194中默认ext3挂载不使用barrier选项,难怪fsync性能相对于旧版内核有如此大的降低呢,重新指定nobarrier选项挂载,重新测试后,时间从相差1倍减小到20%。

进一步思考,由于产品生成了大量的小文件,每个文件写入都进行fsync,重叠的fsync+barrier严重的打乱了写入的连续性,这也印证了1中submit_bio记录的写入位置相当杂乱的情况,将文件大小增大之后,写入相同数据量的数据fsync的次数成比例减少,进而性能提升。而使用nobarrier之后仍有20%的性能差距,就是由于2中写入数据不连续造成的了。进一步使用产品进行测试,添加nobarrier选项后,单盘写入的性能恢复正常。

解决思路:

最终的解决办法也很清楚了,对于我们的产品,如果确定需要使用barrier选项,则将频繁修改的数据和批量写入的数据分离,放在不同的磁盘设备上面,同时增大写入文件的大小,减少对应fsync的调用次数,经过这样处理之后性能恢复正常。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值