我的侏罗纪

[原写于2012年4月]

大概是我上小学一年级的事,那时候从遵义市中心去往火车站的那条“华南路”都还没修完,但是南段已经开了一些小店,周末一家人上街,我在其中一个小店发现了有恐龙的图册卖,便要求妈买一本,妈说这画的什么呀,一群黑绿黑绿的东西,丑死了,不给买,我当然就拿出小孩常用的一哭二闹的办法,但是可惜不好使,爸妈都比较反对买画的动物这么怪异的书,最后没买成。

小男孩喜欢恐龙尤其是霸王龙,这简直是世界性的规律(我是指通常,排除个别小男孩有别的嗜好),也许小雄性动物们心底深处都崇拜强大的力量,而还有什么比史前第一巨兽更能代表原始的力量呢?与霸王龙相比,现代的大象简直太渺小太温和太无趣了。

96年,中考结束,整整三个月的暑假,一天下午我打开电视看厂台,正好在放《侏罗纪公园》,虽然是下半部(厂台没有节目预告,你通常只能逮住半截电影),但足以令我印象深刻、至今难忘了。瞧这拍的多好!恐龙像活的一样,不像那些劣质电影,用蒙了皮的模型来充数。

后来上了高中,我慢慢知道《侏罗纪公园》是小说改编的电影,原著的作者叫迈克尔.克莱顿(那时候我只认得他的中文翻译名),著名的“高科技文学”作家。之后我买了他写的《天外细菌》(那本书上写的作者是“米高基顿”,好港台的翻译),觉得情节环环相扣、引人入胜,真不错。

一眨眼97年的暑假。某天,我在西西弗书社看到一本《失落的世界》,哟,又是迈克尔.克莱顿写的,《侏罗纪公园》的正宗续集,我犹豫了一圈最后决定买了,回家以后天天下午搬个凳子在门口(三楼过道的门口,采光好)看得不亦乐乎,这科幻小说比当时国内的好看多了,不含说教,又有最炫的时尚科技——浑沌理论加基因技术,真是太棒了。

暑假快结束的一天,无意中看到同学万小博拿着一张VCD,我看了一眼封面,毕竟是盗版啊,封面很模糊,但是上面画的恐龙依然非常吸引我,《失落的世界》,哟,都拍成电影了,不错,我于是借回家了。但是,我家那时候没有VCD机(实在是想看,所以没有VCD机也借了),于是跑到唐yan家去看。一个在电影院盗拍的盗版碟能有什么效果,当然是黑乎乎的,但是那些巨大的活灵活现的恐龙已经让我们很满意了。
我真是着迷这些黑绿色的巨兽。那段时间正是十七八岁,一部叫《花季雨季》的小说也很畅销,里面有一句”十六岁是花季,十七岁是雨季“,所以我当时经常对同学开玩笑说:“猜猜十八岁是什么季?——侏罗纪!”。

一眨眼就到今年了(十几年怎么过得这么快),其实想想,我还没完整的看过《侏罗纪公园》的电影呢,再想想,连原著小说我一点也没看过,于是搞来一本《Jurassic Park》小说,完整看了一遍。
Michael Crichton 的想象力真是丰富,今天我们提基因技术可能都会被人说成“老土”,但是在1991年就能想到从琥珀里的远古时代的蚊子的肚子里提出恐龙的血,找出里面的DNA,复制出恐龙,难道不是异想天开吗?更绝的是,用这些“复活”的恐龙开一个公园,多么可爱的想法。还不止,公园开了,但是混沌理论又插了进来,在一个风雨交加的夜晚,游客停在了霸王龙展区边上,偏巧隔离电网停电了(真混沌),这个千万年前的顶级捕食者 T-rex,在黑夜中从牢笼里杀了出来......我只能说,真是太刺激了。
当我们还是小男孩的时候,多少次幻想过让巨大凶猛的恐龙突然出现在学校、道路、或者某栋楼的背后,想想都觉得酷。要知道,Michael Crichton写《Jurassic Park》的时候已经49岁,已然到了“知天命”之年,却还写出了这样一部小说,这是一个多么天真多么有想象力的作家啊!

看完《Jurassic Park》原著,我忍不住去找找Michael Crichton写的其它书,顺带看了一眼他的介绍——啊,他已经于2008年去世了......噢,2008年,柏杨去世了,索尔仁尼琴去世了。我沿着恐龙的足迹,从侏罗纪一直穿越到现在,2012年,才发现,Michael Crichton早已去世了。
唉,太可惜了,以后谁来写这么充满孩子气这么精彩的科幻小说呢?

自食其果

[原写于2012年4月]

“教主”在吃苹果。
文弟: 教主又在很惬意的吃苹果呢
教主: 嗯
文弟: 每天都“自食其果”呢
教主: 。。。。
文弟: (跑到一边)我没苹果吃,我吃美国大杏仁
我: 你是“仁者得仁”
文弟: 嗯,这比“自食其果”好多了
我: 你也是“不成功,便成仁”
文弟: 。。。。

木名: 参加IDF只要报名就行吗?
文弟: 不,要买门票的,据说挺贵
我: 嗯,上次还给我发了个邮件,说是优惠价,400元
文弟: 400元!太贵了。。。。都可以去吃4次”松子“了
我: 唉,自从coly带着大家吃了几次松子,”吃松子“就变成一种硬通货了

mmap与writeback

[原写于2012年2月]

有很多系统读写大文件时用的是这个办法:将大文件mmap到内存,然后直接对内存读写。这样就化read/write为memcpy操作,代码开发上很简便。被修改的内存页由kernel负责挑个时间写入硬盘,程序员不用操心。

但是,最近一些使用了taobao kernel(基于redhat6-2.6.32)的机器,上面那些使用mmap的应用变慢了。我们上线查看,才发现mmap文件里有很多脏页,kernel的writeback机制就不停的将这些脏页写往硬盘,结果造成了大量的io(从iostat看,除了几秒的间歇,io util几乎保持在100%),而如果换回2.6.18内核,就没有这个问题(io util不超过20%,而且很稀疏)。

为了简化应用模型,我们做了一个mmap_press程序模拟应用的写操作,内容很简单,就是mmap一块256MB的内存,然后在256MB的范围内随机的写,一次写8个字节,一共写25亿次,在rhel5(kernel-2.6.18)上,这个程序运行只需要374秒,而在我们的内核上,则要805秒才能完成,慢了两倍多。再换upstream的内核,一样慢,这个问题应该是一直有的。

看来自从writeback改成per bdi的以后,脏页写回的力度是大大加强了。加强是有原因的:越快写回脏页,页面新数据丢失的肯能就越少。但问题是,现在writeback的太频繁了,结果消耗了大量io,拖慢了应用。

能不能找个办法通知writeback子系统,让它每隔60秒或两分钟才开始写脏页,这样至少很多相邻的脏页能被合并成一次io,可以变大量小io为几个大io,速度会快很多。于是我们找到了这个参数 /proc/sys/vm/dirty_expire_centisecs ,默认值是3000,即30秒,也就是说,脏页要过了30秒才会被写往硬盘....等一等,这和我们观察到的完全不一样啊!?我们从iostat看到的是io util一直保持在100%,只有几秒的停歇,几秒啊,不是30秒。

多猜无益,writeback子系统可是相当大的一块,于是我们联系了Intel的吴峰光,他第二天就给出了两个patch,我们将其移植到2.6.32内核后(12),效果很明显,writeback不再是不停的制造io,而是5~6秒的集中io以后,就停下来大约30秒(这次符合dirty_expire_centisecs参数的默认值了),然后再开始5~6秒的集中io,如此循环。我们重新跑mmap_press程序,耗费时间是390秒,已经非常接近2.6.18的速度了。

感谢吴峰光同学的帮助。

大概看了一下吴峰光patch的注释:之前writeback在回写完一个文件后,会从头再查找一遍脏页,如果有脏页则继续回写;现在改成,回写到文件尾后,直接停下来,直到脏页expire(也就是30秒后了)再开始从头检查脏页并回写(这是我对patch的解释,肯定有纰漏之处,有兴趣的同学还是直接看一下patch)。原来如此,咱们的mmap操作有大量的随机写,产生了大量分散的脏页,writeback每次从头检查文件都发现脏页,结果每次都要从头开始回写,就这么不停的转着圈的回写,造成io几乎一直保持在100%。

但我还是有一个疑问:应用写内存只是一个内存操作,writeback写脏页只是一个硬盘操作,为什么硬盘操作会拖慢内存操作呢?最后万能的马涛同学给出了答案:应用对页面的写会触发内核的page_mkwrite操作,这个操作里面是会lock_page的,如果page正在被写往硬盘,那这时候它已经被writeback给lock了,page_mkwrite的lock_page会被阻塞,结果应用写内存的操作就顿住了,所以,越是频繁的writeback,越是会拖慢应用对mmap内存的写操作。

ext4 bigalloc + inline data 测试

[原写于2012年2月]

先前我以为对文件系统来说,读写文件时跑得快才是关键,mkfs和fsck的速度不用操心。直到几天前,线上的运维同学反映:集群里某台(或者悲剧一点,某几台)机器如果宕机了,如果不能迅速重启,可能会对其它服务器带来额外的压力,因为外部流量还在,而“重启”过程包括了对突然断电的磁盘做fsck,所以,fsck的速度也很重要。

于是我找了个台式机(没办法,我们组的开发机服务器没有上T的硬盘),上面配了coly自己买的 2T 硬盘,测了一下ext4如果带上 nojournal + bigalloc + inline_data 后fsck的速度。

硬盘: 希捷Barracuda LP 2TB 5900转 32MB(ST32000542AS)
CPU: Core 2 Duo E8400 3.00GHz 1333MHz FSB (2 cores)
内存: 2GB 800MHz DDR2
mkfs命令:mke2fs -m 0 -C $CLUSTER_SIZE -I 4096 -O ^has_journal,^resize_inode,^uninit_bg,extent,meta_bg,flex_bg,bigalloc,inline_data $DEVICE

用工具 dir_tree 程序创建树状目录结构和文件:

./dir_tree -m /test/ -d 7 -l 4 -n 5 -f 5 -S 64m -s 24576 -t cd
./dir_tree -m /test/ -d 7 -l 4 -n 5 -f 5 -S 64m -s 24576 -t cf

7个总目录,4层目录结构,每个目录下有5个子目录,每个文件64MB(这是某种程度上模仿hadoop的线上环境),总共5470个目录,21875个文件,大约占掉1.4T空间

用不同的cluster size来分别格盘并创建文件,看fsck -f运行的时间。测试结果如下:

cluster size (KB) NR of inode (inode table) time (seconds)
4 122093568 5484
64 30517408 1366
128 15258704 682
256 7629352 339
512 3815136 168
1024 1907352 84

中间这一列“NR of inode“就是mkfs完成后默认的总inode数,反映了inode table的大小。

从测试结果看,随着cluster size越来越大,fsck的速度越来越快。原因是大cluster占用的元数据更少——更少的block group,更少的inode table。
普通的ext4(就是不带bigalloc+inline_data)要90多分钟,而如果用1MB大小的cluster(bigalloc),则不到2分钟就检查结束了。

overlay filesystem

[原写于2012年2月]

需求大概是这样:在一个linux系统上,想跑多个不同应用,这些应用由不同的运维来操作,为了避免互相干扰,希望运维只能看见自己的文件,而看不见别的应用的文件信息。一个常用解决办法就是干脆装多个虚拟机,但是,虚拟机对我们来说偏“重”,比如,多个应用公用的一些动态链接库(比如 libc.so)和配置文件(比如 hosts.conf)就复制了多份,如果原先一个系统在运行时系统文件占了500M的cache,那么现在装了4台虚拟机,就有2G的cache被重复占用了。

怎样才能让系统文件只占一份cache呢?我们首先想到这么个主意:把linux系统装到ext4上,然后做4个snapshot("快照“),这4个snapshot分别mount到4个目录,4个运维chroot到这4个目录里,然后就自己干自己的,干扰不到别人的文件。由于ext4 snapshot的实现机制是让同一个物理block被映射到不同的文件系统里,所以我们觉得,这一个4k的物理block应该就只占4k的cache。

(也许有人要说,这么费劲干嘛,直接把系统常用的动态链接库做4个软链接出来,给4个运维用不就行了?这样做有两个问题,第一,动态链接库以及各种系统文件很多,不可能一一做软链接;第二,也是关键的一点,如果其中一位运维错误操作,例如覆盖写了某个系统文件,那么其他的运维就歇菜了,因为软链接实际指向的是同一个实际文件。)

于是开始考察ext4的snapshot。ext4目前是没有snapshot功能的,但是Amir Goldstein已经开发好了对应的patch,但是目前还没有被收入mainline。粗略看了一下,Amir的patch目前只支持readonly的snapshot,于是我发邮件问“如果改成writable snapshot,代码量大不大?“,Amir回帖表示代码量不大;另外还有别人回帖,推荐不用ext4而是用device mapper提供的thin provision的internal snapshot(1,2),这样就不用依赖于某一个文件系统(就是如果咱们以后不用ext4了,也可以继续做snapshot)。

鉴于ext4 snapshot不支持writable snapshot,且有7000行的改动之多,且目前都没有进mainline的计划,而device mapper的thin provision已经进了3.2 kernel,且只有5000行改动,且支持writable snapshot,所以。。。转而又考察thin provision。考察基本顺利,做snapshot没有问题,snapshot写入没问题,最后chroot然后编译kernel测试速度也没问题,但是,最后发现一个郁闷的事情:这些snapshot被mount以后,公用的文件在不同的文件系统里各自都要占一份cache,也就是说,明明是一个4k物理block,mount到4个不同的文件系统,就占4 x 4k的内存cache了!

难道是device mapper的问题?于是再试了一下ext4的snapshot,甚至btrfs的snapshot,都一样!这就是vfs的特性:只要是inode不同,即使这些inode指向的是同一个物理block,那么它们的cache都是各自独有的,不共享。

我把这个事儿告诉了coly

coly: (石化片刻)唉,我们之前想漏了,snapshot根本不能解决这个问题。。。太郁闷了,测了快两周才发现

我: 想开一点吧,还好是测出来的,而不是上线了才发现——到时候运维找过来“你们的这个方案好像不省内存啊”,然后还得解释,还得回滚,就更被动更狼狈了

coly: 噢,你这样一说,我舒坦多了

最后还是马涛同学给出了一个方案——overlay fs,能把两个目录(甭管是什么文件系统的两个目录,是目录就行)“叠合”成一个文件系统,而这个新文件系统的inode其实还是原来目录里的那个,但是视图已经是“叠合”后的了。

比如,有两个目录,其中一个目录dir1有两个文件,是:

./ab (ino:14)
./cd (ino:16)

另一个目录dir2有三个文件,是:

./apple (ino:23)
./banana (ino:27)
./lemon (ino:31)

最后用

mount -t overlayfs overlayfs -olowerdir=/dir1,upperdir=/dir2 /test/

建立的新文件系统/test/里看上去是这样:

./ab (ino:14)
./cd (ino:16)
./apple (ino:23)
./banana (ino:27)
./lemon (ino:31)

注意,inode还是那些inode,但是他们“凑一块儿了”,而且,这个新文件系统是可写的,即使覆盖写了某个文件,也只影响upperdir(例子里的dir2)的内容,而lowerdir(例子里的dir1)没有任何影响。这样,我们就可以把linux系统根目录当成lowerdir,而每个运维自己的系统当成 upperdir ,某个运维的错误操作就不会影响其他人了。

感谢马涛同学的推荐,目前这个还没有进mainline的overlay fs非常契合我们的应用。

1 11 12 13 14