作者简介
唐杰
目前在Xilinx从事DC架构,曾任职于Fusion-IO,LSI,希捷等公司。
阿呆的话
上周在ssdfans微信群里(加nanoarch为微信好友,你也能进群),冬瓜哥提出SSD应该支持原子写之后,各路诸侯纷纷现身,为这个话题讨论得不可开交,着眼点从MySQL数据库的需求,纵向延伸到到SSD Flash层面的操作。得道大师唐杰不愿卷入纷争,在百忙中,抽出时间写出这篇文章,以正视听。
原子写概念
原子写的概念是和数据库的ACID (原子性,一致性,独立性,持久性)的概念相关的,需要保证数据库系统的事务的语义。
这里面的基本语义就是一组属于同一事务的地址不连续的写需要能够保证原子性,就是在这一组写入操作中,只有两种情况:
-
数据全部被写入有效
-
数据全部没有写入
不存在部分写入的情况。
原子写在固态存储实现
对于没有支持原子写的持久存储层,数据库需要使用自身的机制来保证事务的原子性。这里面就是数据库的日志。对于日志的机理这里不再多说。对于基于固态存储来实现的事务的框图如下:
如何在固态存储的闪存转换层中实现这个机制,因为NAND 闪存的特性,闪存转换层都是基于日志结构文件系统,在这个机制中没有更新,只有数据的追加操作。因此,需要在闪存转换层中增加一个标记来说明一个最小的写入操作是否属于一个原子写,并且有没有被持久化写入。
如下图所示:
在闪存转换层写入的队列中,468 块属于一个原子写, 在写入的开始时,单个数据块的标记是0,只有最后一个写入的数据块的标志为1.因此,这个标记的内容是001.
实现这个机制要保证:属于一个原子写的所有的数据块在写入的队列中必须是连续的,不能被其他的不属于本原子写的写入插入。因此,很容易可以通过判断一个序列中的最后一个数据库的标记是否为1来判断原子写是否成功。
但是这里就提出了一个挑战,我们如何保证在这个写入队列中属于一个原子写的数据块在最后一个数据块被写入之前不会被垃圾回收,同时在崩溃恢复时没有完成的原子写的数据块被移除队列。
因此,在上图所示,我们需要延迟映射表的更新,在最后一个数据块写入之前,并不会更新映射表的内容。
这样在恢复的过程中,只需要判断写入队列的最后一个写入操作的标记是否为为1.如果是0的话,表明最后一个原子写没有被完全写入,直接回滚到上一个标记为1的数据写入。
总结一下,在闪存转换层中实现原子写的三个要点:
-
增加标志位
-
延迟映射表的更新
-
崩溃恢复的回滚。
为什么固态存储可以比较方便的实现原子写,主要原因还是固态存储的闪存转换层本身就是基于日志结构文件系统,因此不需要更多的额外的代价。
参考内容:
http://web.cse.ohio-state.edu/~zhang/hpca11-submitted.pdf