最近,由于项目需要,在做关于flashcache的一些工作,主要涉及模块组织、元数据管理及数据分布、读写流程分析、数据在磁盘和cache(SSD)之间的调度、缺点及可优化方向等一些方面的分析研究。
特别是,要深入下去的话,会涉及到整个Linux系统栈的各个层次,从文件系统、磁盘缓存、通用块层、驱动层,以及DM的工作流程(细节),也遇到了很多问题,像DM层基于split_bio如何做拆分,在拆分中的边界问题等,不可能一下子解决,也趁此机会,记录下心里的困惑。
flashcache,是facebook技术团队开发的新开源项目,主要目的是用SSD硬盘来缓存数据以加速MySQL的一个内核模块。可以看到,它最初是用来做数据库加速,但同时,它也被作为通用的缓存模块而设计,能够用于任何搭建在块设备上的应用程序。
工作原理
基于Device Mapper,它将快速的SSD硬盘和普通的硬盘映射成一个带缓存的逻辑块设备,作为用户操作的接口。用户直接对这个逻辑设备执行读写操作,而不直接对底层的SSD或者普通硬盘操作。如果对底层的这些块设备操作,那么会失去作为一个整体提供的缓存功能。
内核层次
flashcache,它是通过在文件系统和块设备驱动层中间增加一缓存层次实现的,这里不得不提到DM层的映射机制。由于DM是作为虚拟的块设备驱动在内核中被注册的,它不是一个真实的设备驱动,不能完成bio的处理,因此,它主要是基于映射表对bio进行分解、克隆和重映射,然后,bio到达底层真实的设备驱动,启动数据传输。在Devicemapper中,引入了target_driver,每个target_driver由target_type类型描述,代表了一类映射,它们分别用来具体实现块设备的映射过程。通过调用某一target_driver的map方法,来映射从上层分发下来的bio,也即是,找到正确的目标设备,并将bio转发到目标设备的请求队列,完成操作。flashcache_target就是这样一个新的target_driver(作为一个新的映射类 型,target_type是必须的),以模块化的方式加入到了DM层。
逻辑架构
从源代码层次分析,可以将flashcache分为这个四个模块,调度模块(也称‘读写模块’)、逻辑处理模块(也称“读写后处理模块”)、底层存储模块、以及后台清理模块,它们都是基于SSD Layout实现的,构建在SSD布局(后面会分析)之上。
其中,调度模块,在代码中对应flashcache_map映射函数,它是flashcache缓存层次数据入口,所以到达逻辑设备的读写请求,最终都会经过DM层的处理,通过flashcache_map进入调度模块。称之为“调度”,主要是指,接收到数据后,它会根据bio请求的读写类型、是否命中缓存等因素,选择不同的处理分支,如flashcache_read/write或者flashcache_uncached_io,在read和write中会选择是flashcache_read_hit/miss还是flashcache_write_hit/miss。经过不同分支的读写,会调用底层存储模块来完成磁盘或cache的数据读写。
逻辑处理模块,在代码中对应flashcache_io_callback,它在调度模块通过底层存储模块执行数据读写 操作完成后回调执行,所以说它是“读写后处理模块”,它是采用状态机实现的,根据调度模块中的读写类型进行后续的处理,如读未命中情况下,磁盘读完成后,回调到逻辑处理模块,由它负责将从磁盘读取的数据写回到SSD,或者写未命中情况下,写SSD完成后,回调到逻辑处理模块执行元数据的更新,再有就是对调度模块中读写操作的错误进行处理。
底层存储模块,主要提供了两种方式来完成真实的数据读写,一是由DM提供的dm_io函数,它最终还是通过submit_bio的方式,将由调度模块处理过的bio提交到通用块层,进行转发到真实的设备驱动,完成数据读写;另外,一种方式,kcopyd,是由内核提供的一种底层拷贝函数,主要负责脏块的写回(从SSD到磁盘),会引起元数据的更新。
而后台清理模块,是针对每个set进行数据清理,它会基于两种策略对脏块做回收:(1)set内脏块超过了阈值;(2)脏块超过了设定的空闲时间,即fallow_delay,一般是15分钟,在15分钟没有被操作 则会被优先回收。要注意的是,并没有单独的线程在后台做定期空闲块回收,必须由IO操作触发,如果长时间没有对某set操作,则其中的脏数据很长期保持, 容易危害数据安全。
源代码布局
两个工作队列。结合devicemapper代码,特别是dm.c可以知道,在调用flashcache_create工具创建flashcache设备时,会调用flashcache_ctl函数,执行创建工具,它会创建一工作队列_delay_clean,主要负责对整个cache设备的脏块清理,由flashcache_clean_set在特定条件下调用(见代码),通过flashcache_clean_all执行对所有sets的扫描与清理。 另外一个工作队列,_kq_xxx(记不清了),在flashcache_init中,由flashcache模块加载时执行,通过对5个job链表进行处理,执行元数据的更新与完成处理函数、读磁盘后的SSD写入、以及对等待队列的处理,主要就是负责读写后的处理工作隶属于逻辑处理模块,即“读写后处理 模块”,由磁盘或SSD读写后不同情况下被调度。
调度的时机可以看flashcache_map函数,处理逻辑则主要在函数flashcache_io_callback内部判断,the same block的等待队列是否为空,如果不为空,则同样会调用flashcache_do_handler,执行对等待队列的处理。
数据调度
对读,接收到bio,首先,根据bio->bi_sector,即硬盘的扇区号,得到SSD上的set。其次,在set内查找是否命中,如果命中,则将硬盘的扇区号转换为SSD的扇区号,然后将此bio向SSD提交,进行读取;如果未命中,则首先向硬盘驱动提交bio,从硬盘读数据,读取完成后,由回调函数启动回写SSD操作,将bio的扇区号转换为SSD的=扇区号,然后向SSD驱动程序提交,将硬盘读取的数据写入SSD。对写,同文件系统页缓冲,并不直接写入硬盘,而是写入 SSD,同时,保持一个阀值,一般为20%,在脏块数目达到此数值时,写回磁盘。
模拟实验
不是每个人都有SSD/PCI-EFlash的硬件,所以这里可以给大家一个构建虚拟混合存储设备的小技巧,这样即使是在自己的笔记本上,也可以轻松的模拟Flashcache的试验环境,而且随便折腾。
首先,我们可以用内存来模拟一个性能很好的Flash设备,当然这有一个缺点,就是主机重启后就啥都没了,不过用于实验测试这应该不是什么大问题。用内存来模拟块设备有两种方法,ramdisk或者tmpfs+loop device。由于ramdisk要调整大小需要修改grub并重启,这里我们用tmpfs来实现。
|
|
tmpfs,临时文件系统,是一种基于内存的文件系统,它和虚拟磁盘ramdisk比较类似像,但不完全相同,和ramdisk一样,tmpfs可以使用RAM,但它也可以使用swap分区来存储。而且传统的ramdisk是个块设备,要用mkfs来格式化它,才能真正地使用它;而tmpfs是一个文件系统,并不是块设备,只是安装它,就可以使用了。tmpfs是最好的基于RAM的文件系统。因为tmpfs是直接建立在VM之上的,您用一个简单的mount命令就可以创建tmpfs文件系统了。
解决了cache设备,还需要有disk持久设备。同样的,可使用普通磁盘上的文件来虚拟成一个loop device。
|
|
这样我们就有了一个快速的设备/dev/loop0,一个慢速的磁盘设备/dev/loop1,可以开始创建一个Flashcache混合存储设备了。
|
|
Ok,检查一下,就可以开始做一些模拟测试啦。
|
|
sysctl命令被用于在内核运行时动态地修改内核的运行参数,可用的内核参数在目录/proc/sys中。它包含一些TCP/ip堆栈和虚拟内存系统的高级选项, 这可以让有经验的管理员提高引人注目的系统性能。用sysctl可以读取设置超过五百个系统变量。
-n:打印值时不打印关键字;
-e:忽略未知关键字错误;
-N:仅打印名称;
-w:当改变sysctl设置时使用此项;
-p:从配置文件“/etc/sysctl.conf”加载内核参数设置;
-a:打印当前所有可用的内核参数变量和值;
-A:以表格方式打印当前所有可用的内核参数变量和值。
来自: http://man.linuxde.net/sysctl
我是dd一个ssd.img和一个disk.img,然后fio进行测试,效果比非flashcache设备提高3-4倍。
本文总阅读量 次
本文由 Yu Zhang 发表于 Yu Zhang's Blog ,采用署名-非商业性使用-禁止演绎 3.0进行许可。
非商业转载请注明作者及出处。商业转载请联系作者本人。