Ceph--PG读写流程和迁移

Ceph–PG读写流程和迁移 <-- 详情请看这 目前还没太搞懂 都不一些函数引用 状态这些

1.详细剖析PG读写流程

1.1 消息接收与分发

OSD绑定的Public Messenger 收到客户端发送的读写请求后,通过OSD注册的回调函数——ms_fast_dispatch进行快速派发:

  • 基于消息(Messenger)创建一个op, 用于对消息进行跟踪,并记录消息携带的Epoch。
  • 查找OSD关联的客户端会话上下文,,将op加入其内部的waiting_on_map队列,获取OSDMap,,并将其与waiting_on_map队列的所有op进行逐一比较——如果OSD当前的OSDMap的Epoch不小于op所携带的Epoch,则进一步将其派发至OSD的op_shardedwq队列(OSD内部的工作队列);否则直接终止派发。
  • 如果会话的上下文的waiting_on_map不为空,说明至少存在一个op,其携带的Epoch比OSDMap更新,此时将其加入OSD全局session_waiting_for_map集合,该集合汇集了当前所有需要等待OSD更新完OSDMap之后才能继续处理的会话上下文;否则将对应的会话从session_waiting_for_map中移除。

上面有些概念我们先抛砖引玉,下面我们具体讲

1.2 do_request

do_request作为PG处理op的第一步,主要完成全局(PG级别的)检查:

  • Epoch——如果op携带的Epoch更新,那么需要等待PG完成OSDMap同步之后才能进行处理。

  • op能否被直接丢弃——一些可能的场景有:

    • op对应的客户端链接已经断开
    • 收到op时,PG当前已经切换到一个更新的Interval(OSD生成一个连续Epoch的间隔),后续客户端会重发。
    • op在PG分裂之前发送,后续客户端会重发。
  • PG自身的状态如果不为Active,op同样会被阻塞。

    PG内部维护了许多不同类型的重试队列,保证请求按顺序被处理。当对应限制解除之后,op会重新进入op_shardedwq,等待被PG执行。

1.3 do_op

do_op进行对象级别的检查:

1)按照op携带的操作类型,初始化op中各种标志位。

2)完成对op的合法性校验,不合法的情况包括:1.PG包含op所携带的对象;2.op之间携带可以并发执行的标志 ;3. 客户端权限不足;4. op携带对象名称、key或者命名空间长度超过最大限制(只有在FileStore下存在此限制)5.op对应客户端被纳入黑名单 6. op在集群被标记为Full之前发送 7. PG所在的OSD存储空间不足 8. op包含写操作并且企图访问快照对象 9. op包含写操作并且一次写入量太大(超过osd_max_write_size)。

3)检查op携带的对象是否不可读或者处于降级状态或者正在被scrub(读取数据对象并重新计算校验和),加入相应队列。

4)检查op是否为重发(基于op的repid在当前的Log中查找,如果找到说明为重发)。

5)获取对象上下文,创建OpContext对op进行跟踪,并通过execute_ctx真正开始执行op。

关于可用存储空间控制

Ceph使用四个配置项,mon_osd_full_ratiomon_osd_nearfull_ratioosd_backfill_full_ratio(OSD空间利用率大于此值,PG被拒绝以backfill方式迁入)、osd_failsafe_full_ratio(防止OSD磁盘被100%写满的最后一道屏障)。

每个OSD通过心跳机制周期性的检测自身空间利用率,并上报至Monitor。osd_backfill_full_ratio的存在意义是有些数据迁移是自动触发的,我们无法预料到自动数据平衡后数据会落到哪个磁盘,因此必须设置此项进行控制。

关于对象上下文 (原书134页图4-3)

对象上下文主要保存了对象OI(Object Info)和SS(Snap Set)属性。同时表明对象是否仍然存在。查找head对象上下文相对简单,如果没有在缓存中命中,直接在磁盘中读取即可。然而如果op直接操作快照或者对象克隆,这个过程将变得很复杂。其难点在于一个克隆对象可以对应多个快照,因此需要根据快照序列号定位到某个特定的克隆对象,然后通过分析其位于OI中的snap属性才能进一步判断快照序列号是否位于克隆对象之中。

1.4 execute_ctx

execute是真正执行op的步骤。它首先基于当前的快照模式,更新OpContext中的快照上下文(SnapContext)——如果是自定义快照模式,直接基于op携带的快照信息更新;否则基于PGPool更新。

为了保证数据的一致性,所以包含修改操作的PG会预先由Primary通过prepare_transcation封装为一个PG事务,然后由不同类型的PGBackend负责转化为OS(objectStore,笔者注)能够识别的本地事务,最后在副本间分发和同步。

1.5 事务准备

针对多副本,因为每个副本保存的对象完全相同,所以由Primary生成的PG事务也可以直接作为每个副本的本地事务直接执行。引入纠删码之后,每个副本保存的都是独一无二的分片,所以需要对原始对象的整体操作(对应PG操作)和每个分片操作(对应OS事务)加以区分。

本节介绍如何基于op生成PG级别的事务,这个过程通过prepare_transaction完成。

  1. 通过do_osd_ops生成原始op对应的PG事务。
  2. 如果op针对head对象操作,通过make_writable检查是否需要预先执行克隆操作。
  3. 通过finish_ctx检查是否需要创建或者删除snapdir对象,生成日志,并更新对象的OI及SS属性。

下面我们具体介绍相关流程:

1.5.1 do_osd_ops

  1. 检查write操作携带的trancate_seq,并和对象上下文保存的truncate_seq比较从而判定客户端执行write操作和trimtrunc/truncate操作的真实顺序,并对write操作涉及的逻辑地址范围进行修正。
  2. 检查本地写入逻辑地址范围是否合法——例如我们当前限制对象大小不超过100GB。(对应osd_max_object_size)。
  3. 将write对象转化为PGTransaction中的事务。
  4. 如果是满对象写(包括新写或者覆盖写),或者为追加写并且之前存在数据校验和,则重新计算并更新OI中数据校验和,作为后续执行Deep Scrub的依据;否则清除校验和。在OpContext中积累本次write修改的逻辑地址范围以及其它统计(例如写操作次数、写入字节数),同时更新对象大小。

1.5.2 make_writable

如果op针对head对象进行修改

  1. 判断head对象是否需要执行克隆:取对象当前的SnapSet,和OpContext中SnapContext 中内容进行比较——如果SnapSet中最新的快照序列号比SnapContext中最新的快照序列号小,说明自上次快照之后,又产生新的快照。此时不能直接对head对象进行修改,而是需要先执行克隆(默认为全对象克隆)。如果SnapContext携带了多个新的快照序列号,那么所有比SnapSet中更新的快照序列号都将关联至同一个克隆对象。

这里有一个特殊情况——如果当前操作为删除head对象,并且该对象自创建之后没有经历任何修改(此时SnapSet为空),也需要该head对象正常执行克隆后再删除,后续将创建一个snapdir对象来转移这些快照及相关的克隆信息。

  1. 创建克隆对象,需要同步更新SS属性中相关信息:
    • clones集合中记录当前克隆对象中最新快照序列号。
    • clone_size集合中更新当前克隆对象的大小——因为默认使用全对象克隆,所以克隆对象大小为执行克隆时head对象head对象的实时大小。
    • clone_overlap集合中记录当前克隆对象与前一个克隆对象之间的重合部分。
  2. 为克隆对象生成一条新的、独立的日志,更新op中日志版本号。
  3. 最后,基于SnapContext更新对象SS属性中快照信息。

1.5.3 finish_ctx

顾名思义,finish_ctx完成事务准备阶段最后的清理工作。

1)如果创建head对象并且snapdir对象存在,则删除snapdir对象,同时生成一条删除snapdir对象的日志;如果删除head对象并且对象仍然被快照引用,则创建snapdir对象,同时生成一条创建snapdir对象的日志,并将head对象的OI和SS属性用snapdir’对象转存

2)如果对象存在,则更新对象OI属性——例如version、last_reqid、mtime等;进一步,如果是head对象,同步更新其SS属性。

3)生成一条op操作原始对象的日志,并追加至现有的OpContext中的日志集合中。

4)在OpContext关联的对象上下文中应用最新的对象状态和SS上下文。

1.6注册回调函数

PG事务准备后,如果是纯粹的读操作,如果是同步读(对于多副本),op已经执行完毕,此时可以直接向客户端应答;如果是异步读(针对纠删码),则将op挂入PG内部的异步读队列,等待异步读完成之后再向客户端应答。

如果是写操作,则注册如下几类回调函数:

  • on_commit——执行时,向客户端发送写入完成应答;
  • on_success——执行时,进行与Watch\Notify相关的处理;
  • on_finish——执行时,删除OpContext。
1.7事务分发与同步

事务的分发与同步由Primary完成,具体而言是通过RepGather实现的。RepGather被提交到PGBackend,后者负责将PG事务转化为每个副本之间的本地事务之后再进行分发。

对于纠删码而言,当涉及覆盖写时,如果改写的部分不足一个完整条带(指写入的起始地址或者数据长度没有进行条带对齐),则需要执行RMW,这期间需要多次执行补齐读、重新生成完整条带并重新计算校验块、单独生成每个副本的事务并构造消息进行分发(Write)、同时在必要时执行范围克隆和对PG日志进行修正,以支持Peering期间的回滚操作。

你可能感兴趣的:(ceph,分布式,c++)