CEPHFS 内部实现(一):概念篇

基础

CephFS致力于提供POSIX兼容的文件系统接口,为了实现这一目标,CephFS选择了以目录树的形式在内存管理和使用元数据信息。和S3的“伪目录”做对比,这种目录树的方式能更高效、直接地实现POSIX语义。
CephFS作为分布式文件系统,面临所有分布式系统都要着力解决的两个问题:海量数据,单点。面对海量元数据,单机内存容量显然无法满足要求,对此CephFS提供了多个Active MDS机制,将目录树划分成不同区域,分别在不同MDS上管理,MDS在检测到对端MDS故障后,能对故障MDS进行接管,解决单点问题,实现高可用。

由于目录树的结构特点,导致对目录树的并发操作需要一种互斥机制来保证一致性。举例来说,同时在同一个目录下新建文件,对于这个目录来说必须是顺序进行的,而在不同目录下创建文件时,对目标目录的修改是可以并行的,但同时也要确保目标目录以上直至根节点,在这个期间不会被其他线程修改,比如对一个目录节点进行修改时,这个目录及其全部父节点所在路径无法做快照,这是显而易见的。为此MDS中实现了自己的一套锁机制。

为了提高可靠性,MDS中每次针对目录树的修改都是先写日志,再修改内存目录树结构,最后将修改后的结果落盘。如果中途有MDS故障,再次启动或者被其他MDS接管时,重放日志即可恢复到故障前的目录树状态。

实现

目录树结构

目录树中三个关键数据结构是inodedentrydir,三者通过互相连接,共同组成了一个树形目录结构。

目录树的组织方式

Inode中存储的是具体文件/目录的元信息,例如mode,size等。inode号用来在metapool或datapool中查找对应的对象,在metapool中,每个对象是一个dir(分片),在datapool中,每个对象(头对象)是一个文件。

Dentry中存储的是文件/目录的名字,被用来连接inode和dir。

Dir通过一个items map记录自己管理的目录项(dentry),且拥有一个和自身连接的inode,通过inode可以继续向上回溯目录树。items map通过omap方式存储在metapool中。

目录树细节

inode

inode除记录元信息外,作为目录节点时还通过dirfrags管理本节点的分片(fragement),通过分片可以将一个大目录(应用层面)或者热点目录(应用层面)拆分成多个较小目录(实现层面),从而管理更大容量的目录或者在多个mds间实现负载平衡。

dentry

dentry在内存中有三种存在方式:primaryremotenull

  • primary dn表示一个正常的目录项:已连接到一个inode。
  • remote dn对应文件系统中的硬链接(hard link),文件系统中不允许dir有hard link,所以只有普通文件在mds内存中可能有对应的remote dn出现。一个inode只能有一个parent,但能有多个remote parent,parent指向的是primary dentry,remote parent指向的是remote dentry。
  • null dn是一个中间状态,表示dn还未和inode关联。和前两种dentry不一样,null dn不会存储在meta pool中。
dir

每个dir节点都是以<inodenum>.<fragment>的命名方式存在于meta pool中,通过CDir::fetch()读取metapool中的数据,填充自己的CDir::items,从而在内存初中创建相应的dentry和inode结构。

auth

单个Active MDS时,三种元数据结构都只有一份内存副本,位于Active MDS中,且都标记为auth,但当存在多个Active MDS时,每种元数据结构(inode、dentry、dir)都可能出现!auth的情况,!auth情况的出现是由于目录分裂(split)、备份(replicate)、迁移(export)引起。对于来自client的修改类请求,只有auth能处理,!auth节点只能处理非修改类请求。

master / slave request

master和slave用于标识一个mds间请求中的请求方和接收方。发起方为mater(mdr->is_master()),接受方为slave(mdr->is_slave()

实现数据一致性最简单的办法是每次操作对整个目录树加锁,这种方式实现简单,但在性能上显然无法接受。为了实现对目录树中数据结构更高效的互斥操作,mds中设计了12个锁,每个锁管理一部分元数据信息,每个锁有自己的状态,分属于不同类型的状态机,状态机定义了每个锁存在的状态,以及每个状态下client能获得的锁权限和能进行的操作。
锁的类型及所属状态机如下:

struct LockType {
  int type;
  const sm_t *sm;
  explicit LockType(int t) : type(t) {
    switch (type) {
    case CEPH_LOCK_DN: // 管理dentry
    case CEPH_LOCK_IAUTH: // 管理mod,uid,gid等信息
    case CEPH_LOCK_ILINK: // 管理link属性
    case CEPH_LOCK_IXATTR: // 管理扩展属性
    case CEPH_LOCK_ISNAP: // 管理快照信息
    case CEPH_LOCK_IFLOCK: // 文件锁相关
    case CEPH_LOCK_IPOLICY: // 管理layout、quota等信息
      sm = &sm_simplelock;
      break;
    case CEPH_LOCK_IDFT: // 管理分片信息
    case CEPH_LOCK_INEST: // 管理目录递归统计信息,如文件个数等
      sm = &sm_scatterlock;
      break;
    case CEPH_LOCK_IFILE: //对于目录则是管理本层目录的统计信息,对于普通文件则是管理文件大小等
      sm = &sm_filelock;
      break;
    case CEPH_LOCK_DVERSION:
    case CEPH_LOCK_IVERSION:
      sm = &sm_locallock;
      break;
    default:
      sm = 0;
    }
  }
};

其中CEPH_LOCK_DVERSIONCEPH_LOCK_DN是dentry使用的两个锁,其余都是inode使用的锁。dir中没有锁。

对锁共有4种操作:加读锁(rdlock),加写锁(wrlock),加远程写锁(remote_wrlock),加互斥锁(xlock)。每个锁会用到哪些操作根据所属状态机类型区分,总的来说,rdlock用于共享读,xlock用于独占资源,wrlock是针对目录进行的优化锁操作,remote_wrlock则是在处理rename操作中可能会用到的一种加锁方式(因为处理rename请求的MDS是dest-dn为auth的节点,可能会和src-inode的auth不不是同一个MDS上,这时需要在dest-dn的auth节点上对src-inode进行remote_wrlock)。
MDS每次操作目录树时都会先确定好需要锁定的资源(即锁类型)和要进行的操作,然后通过Locker::acquire_locks()集中进行加锁,如果中途碰到某个锁无法加成功,则这个mdr被挂起,直到对应资源可用后再被重新执行。

  • sm_locallock
    locallock sm(sate machine) 有两个分别用于dn version和inode version管理的锁。这两个锁都是为了在本地mds中维护version的一致性。而其他三种状态机则是为了在全部Active mds中维护资源的一致性。
    来自客户端的修改请求处理前都会对相应资源进行xlock,在没有明确要求对version进行加锁时,Locker会根据mdr是否为master为相应的version locker默认进行wrlockxlock。比如创建新文件时会对新文件的dentry进行xlock,这个过程中mds会自动对dentry 的version lock加锁。
    对version lock进行xlockwrlock区别在于xlock在同一个MDS上是互斥的,wrlock是可以重复加的。
    locallock sm 是四种状态机中最简单的一种,它只有一个状态,是否可加锁是通过计数方式进行判断,不能加锁时并不会对锁进行状态调整。

  • sm_simplelock
    simplelock sm中的锁只能进行rdlockxlock两种操作,这和通常的共享/互斥概念是一致的。两种操作都会根据sm判断是否可成功加锁,不满足加锁条件时会根据状态机进行调整。

    simple sm在auth和!auth节点上加锁

  • sm_scatterlock
    scatterlock sm有两个锁:IDFT用于目录分片管理,INEST用于目录递归统计信息管理,两者都可以进行rdlockwrlock,但INEST lock多了在!auth上进行remote_wrlock的操作。相比simple lock sm,scatter sm多了MIX状态,提供多个副本同时写入的功能。举例来说,假设两个Active MDS,某个inode关联的目录分裂成两个分片分别位于两个MDS上,在两个分分片下各自创建新文件,这时如果INEST锁在MIX状态,则rstat信息会在两个MDS上各自更新(各自+1),当有读请求需要读取inode的rstat信息时,由于需要对INEST进行rdlock,这时会先把两个MDS上分散的数据整合到inode的auth节点上,然后加锁才会成功,最后客户端读到正确数据。

    scatter sm在auth和!auth上加锁

  • sm_filelock
    filelock sm只有一个类型的锁:IFILE。
    对于目录IFILE管理本层目录的统计信息(INEST管理的是递归信息),可以进行rdlockwrlockremote_wrlock。三种加锁行为和scatterlock sm一样。
    对于文件,IFILE管理每个client对文件的读写及范围。可以进行rdlockxlock,这两种加锁行为和simplelock sm一样。

lease

在client端元数据依然有dirinodedn三种形式,但比mds中要简单得多,dn不再有三种形态,而是只有两种:有inode关联,没有inode关联。dentry lease被用来表示dn在client端的有效性。
dentry lease存在的必要性可能有这样两个原因:

  1. 有inode关联的dn可以通过inode的cap来判断dn有效性,但没有inode关联的dn就无法借助inode的cap来判断了
  2. 在read dir的时候,可以不用获取dir下各个节点的inode
loner

CInode::calc_ideal_loner()可以看出loner是指在所有client中,当只有一个client在请求写权限时,这个client就被当做loner。
根据src/mds/locks.c中状态机的定义,只有simplelock sm和filelock sm有loner的概念,作为loner的client对元数据具有全部权限(读/互斥),非loner的client则没有任何权限,因此也就无法操作元数据,这本质上是加锁过程产生的一个结果。加锁简单来说是为了保证数据一致性,具体来说是mds根据状态机收回其他client的cap,如果client端有脏数据,则先更新数据,再将cap授予给请求操作的client这样一个过程。拿simplelock sm举例,当一个client需要对一个文件修改owner时,请求发到mds后,mds对inode的AUTH lock进行xlock,当xlock成功时,表明之前(如果有的话)授予给其他client的对于AUTH的cap已经被收回,这时mds可以对inode进行修改,修改完后要进行drop locks,如果自从xlock后,再没有其他client请求这个inode的cap,那么这时发起请求的这个client就是loner,AUTH lock进入到LOCK_EXCL状态,根据状态机定义这个client现在对AUTH具有全部权限,这也和loner的字面意思也相符。如果在xlock成功后,请求处理完成前,又有其他client请求inode的写cap(CEPH_CAP_GEXCL),那么当第一个请求处理完,drop locks时发现其他节点也在请求写,这时不满足loner条件,锁的状态不再进入到LOCK_EXCL,二是根据状态机定义和各个client对cap的请求,重新计算状态,根据得到的状态将cap发送给第一个请求中的client,至此,修改文件owner的那个请求完成。

cap

client端针对每个inode都有独立的cap,表示是否可以相应元数据进行读写。从状态机定义可以看出,scatter sm只有sync状态下允许client读,其他状态下client没有任何权限,这和scatter sm下的两个锁作用也相符合:IDFT管理目录分片和INEST管理目录递归统计,这两种信息对于client来说只能读取,client不应该去尝试修改,因为这两种信息是在mds中进行维护的。对于simplelock sm,client对每种资源(对应一种锁类型)有读和互斥两个权限,符合常理。对于filelock sm,SHAREDEXCL用来维护对stats(对目录)和size(对普通文件)这些资源的互斥,CEPH_CAP_FILE_RDCEPH_CAP_FILE_WR用来管理普通文件的读取和写入,CEPH_CAP_FILE_CACHECEPH_CAP_FILE_BUFFER用来管理普通文件的缓冲读写能力(对应本地文件系统中的Cache和Buffer功能)。

client range

根据filelock sm可以看出对普通文件是支持多个client下同时读写的,比如LOCK_MIX状态下,每个client都能拥有CEPH_CAP_GRD|CEPH_CAP_GWR权限。相应的在MDS端,client range结构用来记录每个client当前的读写范围,当有其他client改变了文件的size后,相应client range要更新。

inline data

当一个文件过小时(不超过client_max_inline_size)把数以inode的attr(blustore中最终就是rocksdb的key-val)形式存在。

pin和freeze

pin表示元数据正在被引用,这时无法对目录进行迁移、分裂操作,需要进行迁移或者分裂时需要先freeze目标子树,freeze后相关的元数据节点无法被pin,也就无法被修改。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容