转自:
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机制替换有些不满,而我们也悲催地踩到这颗地雷了:(
其中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来减少元数据刷新的频率,性能也没有明显的改进。
问题分析:
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的调用次数,经过这样处理之后性能恢复正常。