先强调,本文所说的flashcache是指facebook的那款开源软件,不是指“快速设备”。
同事先是反映flashcache创建的设备被fdisk和parted等工具分区后,在/dev/目录下没有出现新设备(通常,对/dev/sdb设备分区后,会出现/dev/sdb1,/dev/sdb2等子设备)
查了一下资料,multi-device系列的设备(包括flashcache和linux下的软raid)都不支持原始的分区方式,只能用lvm创建logic volume来代替分区。
后来问了一下同事,原来是想在flashcache上创建Oracle ASM,按照这上面说的步骤试了一下
create diskgroup data external redundancy disk '/dev/raw/raw1','/dev/raw/raw2';
Oracle居然说/dev/raw/raw1设备不在"discovery set“里,没办法,只好
chown oracle:dba /dev/mapper/cachedev
后改用/dev/mapper/cachedev,但是又报错说ASM加入的单盘不能超过2048G,即2T,而我的flashcache虚拟设备是3T。
查到这个资料,ASM还真有这个限制,于是,耍了个滑头,重新创建flashcache,把它分成两个1.5T的虚拟设备,再:
create diskgroup data external redundancy disk '/dev/mapper/cachedev1','/dev/mapper/cachedev
这次还报错,说
ERROR at line 1:
ORA-15018: diskgroup cannot be created
ORA-15130: diskgroup "DATA" is being dismounted
于是尝试删除"DATA"这个diskgroup:
drop diskgroup DATA;
又报错:
ERROR at line 1:
ORA-15039: diskgroup not dropped
ORA-15001: diskgroup "DATA" does not exist or is not mounted
我骂人的心都有了,又说”DATA“已经dismouted了,又说它不存在,搞什么?
最后也没成功。Oracle ASM + flashcache,够呛,还是lvm + flashcache吧,或者干脆不用卷管理,直接用文件系统。
前年买kindle,是我自己贴的膜,新手狗屎运,居然贴的挺好,没有气泡也没有灰尘。于是最近又信心满满的给老婆的新手机贴了个膜,但是这次糗了,一串细小灰尘造成了好几个气泡。
我: 唉,看来大屏还是不好弄啊
老婆:我先凑合着用吧
我: 等过几天我再买个新膜,再试一次,这个贴膜很有市场的,我好好练练
老婆:嗯,练好了去地铁口摆个摊,赚点外快
我: 我看地铁有人打横幅写的是“祖传贴膜”
爸: 嗯,咱们这个也是祖传贴膜——从我这里传下来就是这个贴法,就是这么多气泡
如果一台服务器上跑很多应用,对资源的隔离当然是不言而喻的,但是很多时候,你即使在一台机器上就跑一个应用,也可能需要资源控制。比如,你的服务器是提供在线查询服务的,数据放在硬盘上,部分热数据在内存中,一切顺利,但是,某天你在这台服务器上scp数据文件过来时,发现磁盘被拷文件的操作压得喘不过气,以至于查询服务都受到了影响,那这时该怎么办?当然,我们可以用scp -l 来限制拷贝的带宽,避免对磁盘的巨大压力,但如果同步数据不是用的scp,是用户自己的某个工具或者是自己写的某个程序,那么还需要去改程序吗?难道每个用户开发的程序都要带上控制带宽和资源使用率的功能?
现在比较建议的解决办法是用cgroup来隔离资源,而到io层面,常用的就是iothrottle。iothrottle已经在公司的生产环境中使用,最近运维考虑扩大使用范围,所以针对运维和DBA的一些疑问,我做了一些iothrottle的测试,不是什么高深的测试方法,但是确实能回答使用者的一些问题,测试报告见
希望能给大家一些帮助,和信心
问题都来自 pamirs 的信,感谢他对bigalloc特性的关注。
问:文件分配的时候,ext4文件系统请求的时候是按照blocks来请求,但是ext4文件系统自己会按照cluster来分配(这个的理解是否正确呢?)
答:应该说,不太准确,事实上, ext4文件系统请求分配磁盘空间的时候也是按照cluster来请求的了,ext4_ext_map_blocks() 函数是用来检查文件的逻辑位置对应的物理块的,里面申请新磁盘空间的调用是:
ar.len = EXT4_NUM_B2C(sbi, offset+allocated);
......
newblock = ext4_mb_new_blocks(handle, &ar, &err);
ar.len的单位是cluster,所以实际上申请空间就是以cluster为单位
问:如果cluster为32K(8个blocks), 那么对于一个3k的文件来说,一次就分配了一个cluster,这样就有7个blocks不能在被其他文件使用了。group desc中的block_bitmap中将cluster对应的bit位置1,那么后续这个文件在增加的时候,是否按照增加的blocks数量,以及当前该文件占用的cluster,计算出已经占用的cluster中有多少blocks是空闲的,还可以继续分配呢?
答:是的,需要一些琐碎的计算,这些计算的代码都放在了函数get_implied_cluster_alloc()里
问:ext4_group_desc中的bg_free_blocks_count_lo字段表示当前group中有多少空闲的blocks,使用了bigalloc之后,该字段的意义并没有发生变化吧?好像没有看到有新增一个字段来表示free_clusters数量,这个是否不需要保存这样的变量?通过bitmap查询得知呢?
答:bg_free_blocks_count_lo的含义实际是空闲的cluster的数量,看这个函数
ext4_free_group_clusters_set被调用的地方就知道了。这个变量名比较误导,Ted在添加bigalloc特性的时候更多的想到的是完成google gfs所需要的功能,所以代码上没有那么完备和整洁。
想测一测Cubieboard最多能支撑多少tcp长链接(当然,是比较空闲的链接)。
我的cubieboard上安装的是linaro(其实就是linux),首先得调节单个进程打开的句柄数(修改/etc/security/limit.conf),然后还要修改sysctl里tcp_mem等网络参数(具体配置可以参考这里 1, 2) 然写了一套简单的TCP链接客户端和服务端的代码。Server端用epoll模型; Client端创建500个链接然后把fd放入数组里,每个connection每5秒发送128字节。
开始测试时,在cubieboar上我分别对5个端口启动了5个Server,客户端(我的笔记本)则是用脚本不停的创建Client实例。
测试结果:cubieboard可以支撑到10万个tcp长链接,网络流量稳定保持在3.75MB/s,此时系统内存还有大约300MB free(总共1G),CPU idle在20%左右
cubieboard只有网口没有网卡(也就是没有network controller),所有的收包解包都由CPU完成,所以能支持的流量比较有限,不过撑个10万的闲链接还是没问题的。
还想了解一下cubieboard对https的支撑能力,于是我又在cubieboard上装了tengine(在arm上编译还挺顺利,连warning都没有,编译时间4分钟24秒),配上https签名(参加这里)再用ab压力测试,ab压测脚本是:
ab -n 20000 -c 20 -k url
测试结果:访问https的QPS为755, 访问http的QPS为1269
看来用cubieboard搭个小网站是绰绰有余了,一个小的聊天服务器兴许也可以。
====== 2013.1.28 ======
应 @pythonee 同学的要求,测试了200个并发:
ab -n 20000 -c 200 -k url
测试结果:访问https的QPS为858,访问http的QPS为1741,毕竟是单核,提升不明显,压测的时候CPU idle已经为0了
我另外还试了一下把openssl的签名长度改为512bit,以减轻CPU的压力,再测https,QPS可以到959,当然,签名长度再短,QPS也不可能高于http的1741了
皓庭同学在给一台测试机做压力的时候出现了kernel panic:
<4>[ 223.202997] RIP: 0010:[<ffffffff81194e79>] [<ffffffff81194e79>] iput+0x69/0x70
......
<4>[ 223.210475] [<ffffffffa007de6d>] ext4_free_io_end+0x2d/0x40 [ext4]
<4>[ 223.210864] [<ffffffffa007e02c>] ext4_end_aio_dio_work+0xac/0xf0 [ext4]
<4>[ 223.211277] [<ffffffffa007df80>] ? ext4_end_aio_dio_work+0x0/0xf0 [ext4]
<4>[ 223.211693] [<ffffffff8108cc50>] worker_thread+0x170/0x2a0
<4>[ 223.212033] [<ffffffff810925c0>] ? autoremove_wake_function+0x0/0x40
<4>[ 223.212425] [<ffffffff8108cae0>] ? worker_thread+0x0/0x2a0
<4>[ 223.227238] [<ffffffff81092256>] kthread+0x96/0xa0
<4>[ 223.241935] [<ffffffff8100c10a>] child_rip+0xa/0x20
<4>[ 223.256518] [<ffffffff810921c0>] ? kthread+0x0/0xa0
<4>[ 223.271085] [<ffffffff8100c100>] ? child_rip+0x0/0x20
重现的环境和步骤都很简单:只要在redhat linux-2.6.32-279.14.1 kernel上进一个使用ext4的目录一跑:
stress --cpu 2 --io 2 --vm 1 --vm-bytes 128M --hdd 2 --timeout 1d
几分钟后就出现。
这个stress工具果然是神器。
由于重现步骤简单,我也没有太多需要绞尽脑汁的猜测和bug重现工作,就是加trace_printk一点一点调试,最后找到了原因:
stress测试工具是用DIRECT_IO发起的aio(异步IO)请求,在ext4里对于DIRECT_IO请求,都会用ext4_init_io_end初始化一个ext4_io_end_t结构,初始化时调用igrab对该inode里面的i_count进行递增;
struct inode *igrab(struct inode *inode)
{
spin_lock(&inode_lock);
if (!(inode->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE)))
__iget(inode);
else {
/*
* Handle the case where s_op->clear_inode is not been
* called yet, and somebody is calling igrab
* while the inode is getting freed.
*/
printk("%s: %lx\n", __func__, inode->i_state);
inode = NULL;
}
spin_unlock(&inode_lock);
return inode;
}
这些aio结束后,会调用ext4_free_io_end进而调用iput把inode的i_count递减,等到i_count递减到0时,就用iput_final把该inode置为I_CLEAR,意思是该inode可被清除了(当内存不够的时候会把这种I_CLEAR的inode占的内存空间回收)。
但是,如果一个文件已经被rm了,即已经调用了generic_delete_inode,那么inode就已经置上了I_FREEING,此时再调用igrab时,由于第一个判断无效,inode里的i_count就不会被加1了,i_count会一直保持0!等到aio结束后,iput被调用了多次,第一次触发iput_final把inode置上I_CLEAR,第二次就坏了,触发BUG_ON
void iput(struct inode *inode)
{
if (inode) {
BUG_ON(inode->i_state == I_CLEAR);
if (atomic_dec_and_lock(&inode->i_count, &inode_lock))
iput_final(inode);
}
}
因为一般对同一个inode,调了iput_final就不能再调iput了,因为你已经final了、没了。
说来说去,就是igrab似乎不该做那个判断,或者,干脆不要用igrab,用别的计数方式。翻了一下upstream,得,又已经被fix了,commit f7ad6d2e9201a6e1c9ee6530a291452eb695feb8, Ted两年前就已经fix了,抛弃了igrab改为自己管理计数。这个fix backport到rhel-2.6.32-279 kernel还需要一些修改,好在有文卿帮我review代码。
====== 2013.1.18 ======
我们打在rhel 2.6.32-279内核的修复patch是这个 https://github.com/taobao/taobao-kernel/blob/master/patches.taobao/ext4-handle-writeback-of-inodes-which-are-being-reed.patch
很不好意思,patch的标题写错了,还请忽略标题,我回头再改,patch内容是对的。