BTRFS设计简介

来源:互联网 发布:touch捏脸数据女 编辑:程序博客网 时间:2024/05/16 11:31

当前linux文件系统事实上的标准是extN(如ext3/ext4),但extN只是发展了数十年的文件系统的一个延续,设计上缺少一些新的特性来满足当前IT系统的需求,如对大存储容量的支持,高可靠性(冗余支持,鲁棒性),方便的volume管理技术等。btrfs设计之初就是为了成为下一代linux文件系统而努力的,其许多特性可以称之为“现代”: 高效的snapshot与clone,文件系统与volume管理概念整合。 这些在企业级应用中经常出现的重要特性,成为了btrfs天然自带的一部分。可能很多人会联想到solaris上的zfs,我们可以简单地认为btrfs就是linux版的zfs。本文将主要讲解btrfs设计以及实现上的一些重要特性。


核心算法

Ohad Rodeh在他的论文[1]中通过修改B+tree结构,设计出了一种具备我们期望的文件系统原型。而btrfs就是根据这个原型由Chris Mason(原Oracle linux kernel团队负责人,目前在FusionIO)来实现完成的。btrfs设计的核心结构是一种COW(copy-on-write) friendly B+tree:

  1. COW指的是整个btrfs内部不存在overwrite,即对任何数据(meta和data)的修改,都是将更新完的数据写入到新的磁盘block中。COW的方式有一些好处:良好的事务性,对snapshot和clone的友好支持
  2. B+tree是一种重要的数据结构,被广泛应用在文件系统,数据库索引等诸多重要技术上。此处略过B+tree的介绍,先看看COW如何工作的:

figure_1

当COW发生时,所有指向修改节点的节点也要COW,那么对于B+tree来说,leaf chain(B+tree为了支持range lookup,把所有leaf都连接起来)就会造成整棵tree的COW,这显然是不能忍受的。那么Ohad Rodeh的改进包括:

  1. 取消leaf chain,这样虽然不利于range lookup,但是COW就不需要修改整棵tree的结构
  2. 传统B+tree的rebalance是自下而上(bottom-up)的方式,也就是说,当insert或者remove操作发生时,split或者merge首先发生在 leaf上,然后自下而上进行传递的;而Ohad Rodeh提出了自上而下(top-down)的方式。简而言之,在insert或者remove操作之前,search路径的遍历过程中,一旦发现需要merge(当前是insert操作)或者split(当前是remove操作)的节点就主动进行预处理,然后继续向下遍历。这种主动rebalance的方式使整棵树的平衡性更好,且所有操作在一次遍历就可以完成
  3. 通过辅助的free-space map来管理block的ref-count。Ref-count的方式比传统的bitmap方式更加节约存储空间,更高效地生成/删除快照和克隆,但却给普通的修改操作带来了额外的性能问题(由于采用COW方式,每次更新都需要同时修改block的引用计数)


实现细节


数据管理

  1. 所有节点(node和leaf)大小都为一个完整的extent. Extent代表磁盘上一组连续的block(一个block大小为4KB),extent是btrfs最小的分配单位。一般来说,读写一块extent只需要一次IO
  2. 所有meta与data信息都是由 B+tree来进行管理的 ;所有B+tree的root都由root-tree来管理(除了chunk-tree),而super block指向root-tree的根节点
  3. 所有extent都由extent-tree管理,负责分配与回收extent,以及ref-count管理,ref-count机制不应用在extent-tree和root-tree上,这两类tree直接分配新extent,释放旧extent,旧的extent在checkpoint commit之后回收。对于ref-count方式的回收是通过遍历extent-tree,并且支持跨越多个checkpoint来完成一次遍历,使得回收task粒度足够细
  4. 所有meta和data信息都采用fs-tree来管理。文件系统相关的meta信息(如inode,dentry等)都放在fs-tree中。这里一棵fs-tree对应一个物理分区或者一个snapshot或者一个clone;每个leaf(单个extent)直接包含meta的数据,而file data的数据则通过指针指向另外分配的extent(对于小文件直接放在leaf extent中,有助于减少磁盘碎片)
  5. 所有extent都记录checksum,由checksum-tree管理
  6. btrfs将物理磁盘划分为多个chunk(如大小为256M),每个chunk分配的时候,可以标记为meta或者data chunk(这种划分不是绝对的,必要时候可以互相转换),这样meta和data不会混在一起存储。另外chunk-tree维护了logical chunk到physical chunk的映射,所有的存储分配都是基于logical chunk的(这部分由前面所说的extent-tree来负责完成)。同时physical chunk被分成多个group,目的是为了支持RAID
  7. btrfs还维护了一棵relocation-tree,目的是为了支持online的磁盘碎片整理。所谓relocation是基于chunk来操作的(而不是extent),relocation会把需要处理的chunk中的所有extent以及对应的reference找出来,重新分配到新的chunk中

figure_2


COW语义

  1. 所有更新操作都采用COW,如果每次修改都写到磁盘则开销太大,因此全部修改先写在内存中,周期性完成checkpoint,这样可以延迟分配磁盘上的block,有利于优化分配策略,支持批量写入等。
  2. 一次文件的修改操作往往会修改多棵B+tree,事务语义通过COW来完成,自下而上修改,最终通过super block指向新的root-tree来完成一次事务,如果失败(如系统crash),那么super block还是指向旧的root-tree,不会破坏原始数据

并发访问

为了提高并发访问,采用了一种叫做lock coupling的算法[2],具体描述如下:

  1. 查询操作用读锁,插入/删除操作用写锁
  2. 锁住当前节点前必须先锁住其父节点,且必须是相同语义的锁
  3. 查询操作:当前节点必须锁上其孩子节点之后才能释放
  4. 插入操作:当前节点释放锁必须满足:当前节点有足够空间(至少允许插入一个新索引)不会导致split且锁住其孩子节点
  5. 删除操作:当前节点释放锁必须满足:不引发merge且锁住其孩子节点,空节点只有在当前事务全部完成之后才被回收

快照与克隆

snapshot,clone都支持写操作;产生snapshot或clone时,ref-count采用了延迟修改的方式,只修改root节点的所有direct children的ref-count,其余ref-count在checkpoint时来修改完成

对Fsync的支持

对于数据库这样的应用来说,由于需要write-ahead logging,会频繁发生fsync操作,而由于我们的写操作都是COW,所以会造成大量的冗余,同时fsync也会造成频繁commit整棵tree。因此每棵fs-tree都有一棵log-tree来记录fsync请求到磁盘(临时数据),当整棵fs-tree commit checkpoint的时候,再把数据合入进来;而一旦系统crash,log-tree还可以负责replay log


结尾

btrfs功能很强大,该有的都有了,估计文件系统做到这个份上已经到极致了;设计很优雅,全部通过B+tree一种核心结构来完成。但是缺点是性能比较差,虽然对meta的操作非常方便,但IO性能一般;发展了五年,还不是很稳定,进展缓慢;没有大规模的商业应用,只能算半成品。不过无论如何,btrfs代表了现代文件系统的发展方向,相信未来linux社区会有更多突破性的工作展现出来。


References

1. OhadRodeh(2008).  B-trees, Shadowing, andClones. TOS Volume 3 Issue 4, 2008 Article No. 2
2. OhadRodeh(2012). BTRFS: The Linux B-tree Filesystem. IBM Research Report
3. R. Bayer and M. Schkolnick. 1977. Concurrency of operations on b-trees. Acta Informatica 9,1–21.
4. BTRFS source: git://git.kernel.org/pub/scm/linux/kernel/git/mason/linux-btrfs.git
5. BTRFS wiki