2016-10-26 更新start——————————————————————————————————————react
感谢 大水牛提出的疑问:对与本文中第二节,OSD恢复时经常使用的基本概念解析中对acting和up集合的描述问题,在研究ceph之初的时候我也不太懂acting和up集合的区别,而后查看资料从博客http://blog.csdn.net/changtao381/article/details/49125817引用了这部分概念的解释,也感谢这位博主的努力和付出。在这里面对acting和up集合的解释确实出现了一点点的小纰漏。这里更正一下。api
当@大水牛这位大神提出了对acting和up集合的质疑,而后我去查找了以前本身在分析ceph时抓取的一些log,去分析一下 acting和up集合。数组
log抓取的时机:杀掉一个osd.0进程,再从新启动这个进程,而后分析他的log,在这个log里加了一些日志供分析。截取部分日志以下:app
7fba68b79700 10 osd.0 pg_epoch: 27 pg[0.0( v 9'2 (0'0,9'2] local-les=26 n=1 ec=1 les/c 26/26 24/25/24) [2,1]/[2,1,0] r=2 lpr=25 pi=22-24/2 luod=0'0 crt=0'0 lcod 9'1 active+remapped] state<Started>: XYJ TEST advmap 1 Line 2: 2015-09-17 13:38:08.394649 7fba68b79700 10 osd.0 pg_epoch: 27 pg[0.0( v 9'2 (0'0,9'2] local-les=26 n=1 ec=1 les/c 26/26 24/25/24) [2,1]/[2,1,0] r=2 lpr=25 pi=22-24/2 luod=0'0 crt=0'0 lcod 9'1 active+remapped] state<Started/ReplicaActive/RepNotRecovering>: XYJ TEST exit Line 3: 2015-09-17 13:38:08.394674 7fba68b79700 10 osd.0 pg_epoch: 27 pg[0.0( v 9'2 (0'0,9'2] local-les=26 n=1 ec=1 les/c 26/26 24/25/24) [2,1]/[2,1,0] r=2 lpr=25 pi=22-24/2 luod=0'0 crt=0'0 lcod 9'1 active+remapped] state<Started/ReplicaActive>: XYJ TEST exit Line 4: 2015-09-17 13:38:08.394698 7fba68b79700 10 osd.0 pg_epoch: 27 pg[0.0( v 9'2 (0'0,9'2] local-les=26 n=1 ec=1 les/c 26/26 24/25/24) [2,1]/[2,1,0] r=2 lpr=25 pi=22-24/2 luod=0'0 crt=0'0 lcod 9'1 active+remapped] state<Started>: XYJ TEST exit Line 5: 2015-09-17 13:38:08.394740 7fba68b79700 10 osd.0 pg_epoch: 27 pg[0.0( v 9'2 (0'0,9'2] local-les=26 n=1 ec=1 les/c 26/26 24/25/24) [2,1]/[2,1,0] r=2 lpr=25 pi=22-24/2 luod=0'0 crt=0'0 lcod 9'1 active+remapped] state<Reset>: XYJ TEST Line 11: 2015-09-17 13:38:08.395107 7fba68b79700 10 osd.0 pg_epoch: 27 pg[0.0( v 9'2 (0'0,9'2] local-les=26 n=1 ec=1 les/c 26/26 27/27/24) [0,2,1]/[2,1,0] r=2 lpr=27 pi=22-26/3 crt=0'0 lcod 9'1 remapped NOTIFY] state<Reset>: XYJ TEST advmap Line 12: 2015-09-17 13:38:08.395172 7fba68b79700 10 osd.0 pg_epoch: 27 pg[0.0( v 9'2 (0'0,9'2] local-les=26 n=1 ec=1 les/c 26/26 27/27/24) [0,2,1]/[2,1,0] r=2 lpr=27 pi=22-26/3 crt=0'0 lcod 9'1 remapped NOTIFY] state<Reset>: XYJ TEST actmap Line 13: 2015-09-17 13:38:08.395198 7fba68b79700 10 osd.0 pg_epoch: 27 pg[0.0( v 9'2 (0'0,9'2] local-les=26 n=1 ec=1 les/c 26/26 27/27/24) [0,2,1]/[2,1,0] r=2 lpr=27 pi=22-26/3 crt=0'0 lcod 9'1 remapped NOTIFY] state<Reset>: XYJ TEST exit Line 14: 2015-09-17 13:38:08.395220 7fba68b79700 10 osd.0 pg_epoch: 27 pg[0.0( v 9'2 (0'0,9'2] local-les=26 n=1 ec=1 les/c 26/26 27/27/24) [0,2,1]/[2,1,0] r=2 lpr=27 pi=22-26/3 crt=0'0 lcod 9'1 remapped NOTIFY] state<Started>: XYJ TEST Line 16: 2015-09-17 13:38:08.395251 7fba68b79700 10 osd.0 pg_epoch: 27 pg[0.0( v 9'2 (0'0,9'2] local-les=26 n=1 ec=1 les/c 26/26 27/27/24) [0,2,1]/[2,1,0] r=2 lpr=27 pi=22-26/3 crt=0'0 lcod 9'1 remapped NOTIFY] state<Start>: XYJ TEST start with stary Line 18: 2015-09-17 13:38:08.395277 7fba68b79700 10 osd.0 pg_epoch: 27 pg[0.0( v 9'2 (0'0,9'2] local-les=26 n=1 ec=1 les/c 26/26 27/27/24) [0,2,1]/[2,1,0] r=2 lpr=27 pi=22-26/3 crt=0'0 lcod 9'1 remapped NOTIFY] state<Start>: XYJ TEST exit Line 20: 2015-09-17 13:38:08.395305 7fba68b79700 10 osd.0 pg_epoch: 27 pg[0.0( v 9'2 (0'0,9'2] local-les=26 n=1 ec=1 les/c 26/26 27/27/24) [0,2,1]/[2,1,0] r=2 lpr=27 pi=22-26/3 crt=0'0 lcod 9'1 remapped NOTIFY] state<Started/Stray>: XYJ TEST Line 100: 2015-09-17 13:38:08.411022 7fba68b79700 10 osd.0 pg_epoch: 27 pg[0.0( v 9'2 (0'0,9'2] local-les=26 n=1 ec=1 les/c 26/26 27/27/24) [0,2,1]/[2,1,0] r=2 lpr=27 pi=22-26/3 crt=0'0 lcod 9'1 remapped NOTIFY] state<Started/Stray>: XYJ TEST query Line 105: 2015-09-17 13:38:08.442529 7fba68b79700 10 osd.0 pg_epoch: 27 pg[0.0( v 9'2 (0'0,9'2] local-les=26 n=1 ec=1 les/c 26/26 27/27/24) [0,2,1]/[2,1,0] r=2 lpr=27 pi=22-26/3 crt=0'0 lcod 9'1 remapped NOTIFY] state<Started>: XYJ TEST flush Line 113: 2015-09-17 13:38:09.394743 7fba68378700 10 osd.0 pg_epoch: 28 pg[0.0( v 9'2 (0'0,9'2] local-les=26 n=1 ec=1 les/c 26/26 27/27/24) [0,2,1]/[2,1,0] r=2 lpr=27 pi=22-26/3 crt=0'0 lcod 9'1 remapped NOTIFY] state<Started>: XYJ TEST advmap 1 Line 114: 2015-09-17 13:38:09.394771 7fba68378700 10 osd.0 pg_epoch: 28 pg[0.0( v 9'2 (0'0,9'2] local-les=26 n=1 ec=1 les/c 26/26 27/27/24) [0,2,1]/[2,1,0] r=2 lpr=27 pi=22-26/3 crt=0'0 lcod 9'1 remapped NOTIFY] state<Started/Stray>: XYJ TEST exit Line 115: 2015-09-17 13:38:09.394794 7fba68378700 10 osd.0 pg_epoch: 28 pg[0.0( v 9'2 (0'0,9'2] local-les=26 n=1 ec=1 les/c 26/26 27/27/24) [0,2,1]/[2,1,0] r=2 lpr=27 pi=22-26/3 crt=0'0 lcod 9'1 remapped NOTIFY] state<Started>: XYJ TEST exit Line 116: 2015-09-17 13:38:09.394823 7fba68378700 10 osd.0 pg_epoch: 28 pg[0.0( v 9'2 (0'0,9'2] local-les=26 n=1 ec=1 les/c 26/26 27/27/24) [0,2,1]/[2,1,0] r=2 lpr=27 pi=22-26/3 crt=0'0 lcod 9'1 remapped NOTIFY] state<Reset>: XYJ TEST Line 117: 2015-09-17 13:38:09.395203 7fba68378700 10 osd.0 pg_epoch: 28 pg[0.0( v 9'2 (0'0,9'2] local-les=26 n=1 ec=1 les/c 26/26 27/28/28) [0,2,1] r=0 lpr=28 pi=22-27/4 crt=0'0 lcod 9'1 mlcod 0'0 inactive] state<Reset>: XYJ TEST advmap Line 118: 2015-09-17 13:38:09.395250 7fba68378700 10 osd.0 pg_epoch: 28 pg[0.0( v 9'2 (0'0,9'2] local-les=26 n=1 ec=1 les/c 26/26 27/28/28) [0,2,1] r=0 lpr=28 pi=22-27/4 crt=0'0 lcod 9'1 mlcod 0'0 inactive] state<Reset>: XYJ TEST actmap Line 119: 2015-09-17 13:38:09.395272 7fba68378700 10 osd.0 pg_epoch: 28 pg[0.0( v 9'2 (0'0,9'2] local-les=26 n=1 ec=1 les/c 26/26 27/28/28) [0,2,1] r=0 lpr=28 pi=22-27/4 crt=0'0 lcod 9'1 mlcod 0'0 inactive] state<Reset>: XYJ TEST exit Line 120: 2015-09-17 13:38:09.395293 7fba68378700 10 osd.0 pg_epoch: 28 pg[0.0( v 9'2 (0'0,9'2] local-les=26 n=1 ec=1 les/c 26/26 27/28/28) [0,2,1] r=0 lpr=28 pi=22-27/4 crt=0'0 lcod 9'1 mlcod 0'0 inactive] state<Started>: XYJ TEST Line 121: 2015-09-17 13:38:09.395324 7fba68378700 10 osd.0 pg_epoch: 28 pg[0.0( v 9'2 (0'0,9'2] local-les=26 n=1 ec=1 les/c 26/26 27/28/28) [0,2,1] r=0 lpr=28 pi=22-27/4 crt=0'0 lcod 9'1 mlcod 0'0 inac
能够看出pg0.0的集合变化,pg0.0是采用3副本模式。pg0.0的osd集合变化以下:函数
a.当丢失osd.0后 pg0.0 的集合变成了 [2,1]/[2,1] 因为acting和up集合相同因此只显示一个 [2,1]ui
b.当osd.0从新启动后将osd.0加入pg0.0 则变成 [2,1]/[2,1,0]。this
c.根据crush规则从新计算pg0.0 的osd集合,变成了 [0,2,1]/[2,1,0]spa
d.最后当数据恢复完成后,pg0.0的osd集合为[0,2,1]/[0,2,1]因为acting和up集合相同因此只显示一个[0,2,1].net
问题在于步骤c。在c中存在两个集合,一个是根据crush算出的集合[0,2,1],另外一个是临时的集合 [2,1,0]。须要肯定的是acting与up分别对应的是哪个集合? 重新来看下代码便可知晓, pg状态打印代码以下:线程
5347:打印pginfo的信息。
5348:打印up集合的信息。
5349:若是acting集合与up集合不相同则打印acting集合。
如今再去看上面的log,就很容易对应起来了,这里的up 对应的是crush根据当前的osdmap算出来的,acting集合为临时的pg集合。
因此你们在看第二节对名词解释的时候注意区分。
感谢@大水牛提出的意见
2016-10-26 更新end——————————————————————————————————————
集群中的设备异常(异常OSD的添加删除操做),会致使PG的各个副本间出现数据的不一致现象,这时就须要进行数据的恢复,让全部的副本都达到一致的状态。想知道如何来进行数据的恢复以前,先要了解OSD故障的种类。
1. OSD的故障种类:
故障A:一个正常的OSD 由于所在的设备发生异常,致使OSD不能正常工做,这样OSD超过设定的时间 就会被 out出集群。
故障B: 一个正常的OSD由于所在的设备发生异常,致使OSD不能正常工做,可是在设定的时间内,它又能够正常的工做,这时会添加会集群中。
2. OSD的故障处理:
故障A:OSD上全部的PG,这些PG就会从新分配副本到其余OSD上。一个PG中包含的object数量是不限制的,这时会将PG中全部的object进行复制,可能会产生很大的数据复制。
故障B:OSD又从新回到PG当中去,这时须要判断一下,若是OSD可以进行增量恢复则进行增量恢复,不然进行全量恢复。(增量恢复:是指恢复OSD出现异常的期间,PG内发生变化的object。全量恢复:是指将PG内的所有object进行恢复,方法同故障A的处理)。
须要全量恢复的操做叫作backfill操做。须要增量恢复的操做叫作recovery操做。
(引用自http://blog.csdn.net/changtao381/article/details/49125817,感谢该博主)
上一节当中已经讲述了PG的Peering的过程,Peering就是PG在副本所在的OSD发生变化时的状态演变过程。这一节也和Peering过程是分不开的,可是重点是来说述数据恢复过程当中涉及到的关键步骤。
先来解释一下基本的概念。
1. acting set 和 up set:每一个pg都有这两个集合,acting set中保存是该pg全部的副本所在OSD的集合,好比acting[0,1,2],就表示这个pg的副本保存在OSD.0 、OSD.一、OSD.2中,并且排在第一位的是OSD.0 ,表示这个OSD.0是PG的primary副本。 在一般状况下 up set 与 acting set是相同的。区别不一样之处须要先了解pg_temp。
2. pg_temp : 假设当一个PG的副本数量不够时,这时的副本状况为acting/up = [1,2]/[1,2]。这时添加一个OSD.3做为PG的副本。通过crush的计算发现,这个OSD.3应该为当前PG的primary,可是呢,这OSD.3上面尚未PG的数据,因此没法承担primary,因此须要申请一个pg_temp,这个pg_temp就还采用OSD.1做为primary,此时pg的集合为acting,pg_temp的集合为up。固然pg与pg_temp是不同的,因此这时pg的集合变成了[3,1,2]/[1,2,3]。当OSD.3上的数据所有都恢复完成后,就变成了[3,1,2]/[3,1,2]。
3.epoch:当集群中的OSD发生变化,则就会产生新的OSDmap,每一个OSDmap都对应一个epoch,epoch按着前后顺序单调递增。epoch越大说明OSDmap越新。
4.current_interval、past_interval:每一个PG都有interval。 interval 是一个epoch的序列,在这个interval内,可能存在多个epoch,可是PG的成员却不会改变。若是PG的成员发生变化,则会造成new interval。current是当前的序列,past是指过去的interval。
last_epoch_started:上次通过peering后的osdmap版本号epoch。
last_epoch_clean:上次通过recovery或者backfill后的osdmap版本号epoch。
(注:peering结束后,数据的恢复操做才刚开始,因此last_epoch_started与last_epoch_clean可能存在不一样)。
例如:
ceph 系统当前的epoch值为20, pg1.0 的 acting set 和 up set 都为[0,1,2]
osd.3失效致使了osd map变化,epoch变为 21
osd.5失效致使了osd map变化,epoch变为 22
osd.6失效致使了osd map变化,epoch变为 23
上述三次epoch的变化都不会改变pg1.0的acting set和up set
osd.2失效致使了osd map变化,epoch变为 24
此时致使pg1.0的acting set 和 up set变为 [0,1,8],若此时 peering过程成功完成,则last_epoch_started 为24
osd.12失效致使了osd map变化,epoch变为 25
此时若是pg1.0完成了recovery,处于clean状态,last_epoch_clean就为25
osd13失效致使了osd map变化,epoch变为 26
epoch 序列 21,22,23,23 就为pg1.0的past interval
epoch 序列 24,25,26就为 pg1.0的current interval
5.pg_log:pg_log是用于恢复数据重要的结构,每一个pg都有本身的log。对于pg的每个object操做都记录在pg当中。
__s32 op; 操做的类型
hobject_t soid; 操做的对象
eversion_t version, prior_version, reverting_to; 操做的版本
struct pg_info_t
{
spg_t pgid;
eversion_t last_update; //pg 最后一次更新的eversion
//recovery完成后的最后一个everson,也就是pg处于clean状态的最后一次操做的 eversion
eversion_t last_complete;
// last epoch at which this pg started on this osd 这个pg在这个osd上的最近的的开始的epoch,也就是最近一次peering完成后的epoch
epoch_t last_epoch_started;
// last user object version applied to store
version_t last_user_version;
eversion_t log_tail; // oldest log entry.
// objects >= this and < last_complete may be missing
hobject_t last_backfill;
interval_set<snapid_t> purged_snaps; //pg的要删除的snap集合
pg_stat_t stats;
pg_history_t history; //pg的历史信息
pg_hit_set_history_t hit_set; //这个是cache tie用的hit_set
}
1. 生成past_interval (要根据interval肯定每一个interval期间的osd集合)。
2.根据past_interval 选取参与peering过程的osd集合 build_prior。
3. 拉取prior集合中全部osd的pg_info。
4. 选取权威的osd。
5.拉取权威osd的log与info。与本地log合并。造成本地missing结构
6.拉取其余osd的log与info。与auth log对比,将差别log发送给peer。而且在本地造成peer_missing结构。
7.根据missing和peer_missing结构可知丢失数据定位。
8.peering处理成功,开始进行数据的恢复。
详细分析这个过程主要涉及到peering过程的准备与recovering的数据恢复过程。在peering准备过程如上图描述。
peering过程准备工做:
肯定参与peering过程的osd集合。
该集合中合并出最权威的log记录。
每个osd缺失而且须要恢复的object。
须要恢复的object能够从哪一个osd上进行拷贝。
4.1 恢复的前驱peering过程的准备工做:
在进行解析peering过程当中,确定和上一节讲述的内容是分不开的。可是这里先去除其余异常整理代码,直接从GetInfo处理函数讲起(PG::RecoveryState::GetInfo::GetInfo(my_context ctx))。
在GetInfo函数中先来看一下:
7372:调用generate_past_intervals()函数,生成past_interval序列。首先肯定查找interval的start_epoch(history.last_epoch_clean 上次恢复数据完成的epoch)和end_epoch(history.same_interval_since 最近一次interval的起始epoch)。肯定了start_epoch和end_epoch以后,循环这个两个版本间的全部osdmap,肯定pg成员变化的区间interval。
7379:根据past_interval生成prior set集合。肯定prior set集合,若是处于当前的acting和up集合中的成员,循环遍历past_interval中的每个interval,interval.last >= info.history.last_epoch_started、! interval.acting.empty()、interval.maybe_went_rw,在该interval中的acting集合中,而且在集群中仍然是up状态的。
7384:根据priorset 集合,开始获取集合中的全部osd的info。这里会向全部的osd发送请求info的req(PG::RecoveryState::GetInfo::get_infos())。发送请求后等待回复。
回复的处理函数:PG::RecoveryState::GetInfo::react(const MNotifyRec& infoevt)
主要调用了pg->proc_replica_info进行处理:1.将info放入peerinfo数组中。2.合并history记录。 在这里会等待全部的副本都回复info信息。进入下一个状态GetLog。
在GetLog中处理(PG::RecoveryState::GetLog::GetLog):
经过pg->choose_acting(auth_log_shard)选择acting集合和auth_osd.
向auth_osd发送查询 log的req。
choose_acting中主要进行了两项重要的措施:
find_best_info,查找一个最优的osd。
calc_replicated_acting,选择参与peering、recovering的osd集合。
在 find_best_info中查找最优的osd时,判断的条件的优先级有三个:最大的last_update、最小的log_tail、当前的primary。
在calc_replicated_acting中主要进行一下几种分析:
up集合中的成员。全部的成员都是加入到acting_backfilling中,若是是incomplete状态的成员或者 日志衔接不上的成员(cur.last_update<auth.log_tail)则添加到backfill中,不然添加到want成员中。
acting集合中的成员,该组内的成员不会添加到backfill中,因此只须要判断 若是状态是complete而且 日志可以衔接的上,则添加到want和acting_backfilling中。
其余prior中的osd成员 处理同acting一致。
通过这一步可知,acting_backfilling的成员(可用日志恢复数据,或者帮助恢复数据的成员),backfill的成员(只能经过其余的osd上pg的数据进行全量拷贝恢复),want的成员(一样在acting_backfill中,可是不一样于backfill的成员)。
以上在GetLog中的工做就都完成了,而后想best_osd发送log请求,等待best_osd回复。
best_osd回复PG::RecoveryState::GetLog::react(const GotLog&):
使用pg->proc_master_log()处理来自best_osd的log。
跳转到GetMissing的处理
在pg->proc_master_log()中
0281:调用merge_log函数,该函数对log进行合并,造成一个权威顺序完整的一个log。包括日志先后的修补,并且最重要的是修补的过程当中,统计了本地副本中须要恢复object的状况missing.add_next_event(ne)。这里已经开始统计missing结构了。
0284:保存来自best_log的oinfo到本地的peer-info数组中。
0296:须要对history信息进行合并。
0299:将missing结构统计到本地的peer_missing结构中。
这是一个很重要的处理过程,涉及到两个重要的点,
auth_log:一个是auth_log的合并,最大最权威的log,恢复数据要根据这里进行。
missing:另外就是合并log过程当中发现本地副本须要恢复的object集合。
omissing:auth_osd须要进行恢复的object集合。
接下来该进入GetMissing处理中(PG::RecoveryState::GetMissing::GetMissing(my_context ctx)):
循环遍历actingbackfill 成员,拉取全部成员的log和missing信息。等待回复。
在回复PG::RecoveryState::GetMissing::react(const MLogRec& logevt)中:
接收到其余osd发回的log信息而且进行处理,主要为pg->proc_replica_log处理。
在proc_replica_log中对peer_log进行修剪,丢弃那些不完整不可用的log。整理接收到的oinfo到peerinfo中,omissing到peer_missing中。接下来能够选择跳过NeedUpThru(由于 我不知道这个什么做用),直接来到active状态。
在active处理 PG::RecoveryState::Active::Active(my_context ctx):
在这里主要调用了pg->activate()处理。
这里首先进行了一些细节的处理,这些细节对于流程的控制起到很重要的做用,可是不是关键的流程,下面截取一些关键流程。
1769:这里主要由primary osd进行处理。发起流程。
1775:开始循环处理 replica osd,由于这些osd都添加在actingbackfill中。这些replica可能有缺失log,因此要进行log的修补。
1858:若是存在一些replica osd须要进行log的修补工做,则建立一个message用于传递修补的log。
1863:这里知道须要修补的log从last_update开始。使用copy_after拷贝以后的log。
1876:判断若是不是全部的object都修复了,则须要记录missing结构。
1878:循环遍历须要修补的log list。
1881:判断这个log记录的 object是否是还没修补呢。
1883:若是这个object还没来得及修补,则添加到pm中,记录这object须要被恢复。
1893:将这个用于修补log的message发送给对应的osd。等待回应。
上面一部分主要进行了每一个osd的差别log整理工做,而且将这个log组织在message中,准备发送给对应的osd。并且根据缺失log肯定了该osd须要恢复的object,放在pm的结构中。
对于每个peer osd 都进行了缺失log的整理工做,而且也整理了peer_missing 存放每一个osd须要恢复的object。如今知道了每一个osd须要恢复的object,可是不知道这些object须要从哪一个osd上拉取object数据,因此还须要肯定这些须要恢复的object能够在哪一个osd进行拉取数据。这部分处理能够看下这个图。
1. 首先这里都知道了pg->peer_missing结构,由于上面根据缺失log可知这些须要恢复的object,这些object记录在peermissing->missing结构中。
2.接下来结果转换将整理统计 哪些object须要恢复,固然将整理的结果添加到pg->missing_loc->needs_recovery_map结构中保存。这样能够按着object进行恢复。
3.知道了哪些object须要进行恢复,还要继续肯定这些object能够从哪些osd中拉取,将每一个object能够拉取数据的osd 集合使用 pg->Missing_loc->missing_loc保存。
通过上面的步骤能够获得一下条件:
1. 每一个osd须要恢复的object集合 ,保存在peer_missing结构中。
2. 此次recovery过程全部须要恢复的object,保存在need_recovery_map中。
3.这些须要恢复的object能够从哪些osd上拉取数据,保存在 missing_loc结构中。
接下来看代码是怎么实现的,由于前面已经说过了peermissing的组织,因此这里直接从第二条need_recovery_map结构组织开始(仍是pg->activate()):
1913:仍然循环处理 actingbackfill 列表中的全部的成员。
1917:若是是primary osd则添加primary osd的missing,这里使用add_active_missing()函数,就是将丢失的object直接添加到need_recovery_map中。
1922:这里是将其余osd的missing中记录的object添加到need_recovery_map中。
接下来就是要进行第三个结构missing_loc的组织了:
1940:先开始处理本地missing结构,使用add_source_info()函数处理。该函数处理主要是对比当前osd的missing列表和need_recovery列表,若是不在missing列表,却在need_recovery_map列表的object,能够从本osd上拉取数据,将这个osd记录起来missing_loc[soid].insert(fromosd)。
1941:开始循环处理其余的osd。
1950:处理其余osd上的missing结构,处理方法同1941。
这样三部分的数据都有了,下面就能够进行数据的恢复了。
这样等待全部的osd都发回了确认ack(差别日志发送给osd)信息,进入以下处理的函数中PG::RecoveryState::Active::react(const AllReplicasActivated &evt),这里主要调用了pg->on_activate(),该方法是由ReplicatedPG::on_activate()实现的。通过一系列的变化(上一节中详细讲述了),达到recovering状态。最后将这个pg添加到了osd->recovery_queue中,recovery_queue是一个工做队列,会有专门处理的线程来进行处理。该线程的主处理函数为void OSD::do_recovery(PG *pg, ThreadPool::TPHandle &handle),解决着调用ReplicatedPG::start_recovery_ops()。
9497:获取主osd上的missing 统计结果。
9499:获取主osd上缺失而且须要恢复的object数量。
9500:获取定位缺失数据中 缺乏数据源的object数量。
9507:若是 主osd上丢失的object 数量 与 没法定位数据源的object数量相同。也就是主osd上丢失的object暂时没法恢复。
9511:开始先恢复replicas osd上的数据。
9514:直到 replicas osd上数据全都恢复完毕,或者无数据能够恢复时。
9517:开始进行 primary osd上的数据恢复。
9520:若是前面两项都进行了数据恢复,可是仍然有object没有被恢复,则再次恢复replicas上缺失的object。
上面进行了三次恢复的操做,第一次恢复replicas副本,第二次恢复primary 副本,第三次恢复replicas副本,为了恢复数据的流控,在恢复的时候设置了阀值,每次恢复的上限就是阀值,超过阀值后,会将该pg从新添加到recovery_wq队列中,等待下次被处理,一样上面的三次恢复操做之间存在依赖关系,因此必须一个恢复完再尝试恢复第二个,若是恢复完第一个后,再也不进行第二个,第三的恢复,直接从新添加到recovery_wq中,直到第一个被恢复完成,再次添加到recovery_wq中,下次才会进行第二个恢复操做。
全部的数据恢复都要通过primary osd,
1. 若是primary osd出现数据丢失object,则由primary osd主动pull拉取replicas osd上的object数据。
2. 若是 replicas osd 上出现数据丢失object,则由primary osd 主动push 推送replicas osd上的 object数据。
3.若是primary osd 和 部分replica osd缺失object数据,则先由primary osd从正常的replica osd上拉取数据,进行本地恢复。下一次再把数据推送到须要恢复的osd上。
2016-11-05:更新start————————————————————————————————
恢复数据的过程,描述起来很复杂,较为难懂。这篇博客介绍的是数据恢复的大致过程,不少细节没有讲,每一个人的角度不一样,很难都兼顾获得,因此又从新整理了一个数据恢复的图。借此图说明在数据移动的过程当中,发生osd变化,而且伴有写操做的复杂状况,看ceph是如何处理的。这样你们就能更清楚明白的读懂ceph数据恢复过程。
1.写入object1。初始状态pg ,他由osd.0 、osd.一、osd.2组成的三副本形式,这时up集合为[0,1,2],acting集合为[0,1,2],acting_primary 为osd 0。这时该pg已经完成写入object1,那osd0,1,2上都保存这object1。这时pg处于interval0.
2.加入osd3。当集群添加osd3.有的pg上的数据会发生移动,刚巧这个pg的up由[0,1,2]变成[0,1,3],这时的acting也变成了[0,1,3],acting_primary 为osd 0。可是osd.3上没有object1,标记为虚线框黄底色的object1,就会发生数据移动,由osd.0 向 osd.3上拷贝数据(该过程是recovery或者backfill,肯定是哪种,要根据pglog来决定)。当拷贝数据的过程当中发生,数据写入。加入osd3后,pg的up集合由[0,1,2]变成[0,1,3],因此pg此时为interval 1。
3.写入object2。在写入pg的时候,数据会写三个副本,分别写到osd0,1,3中。osd0,1中有object1,2。osd3中只有object2,osd3中的object1还在数据恢复中。因为up集合没发生变化,因此此时pg仍然是interval 1
4.加入osd4。在osd3没有数据恢复完成,就加入了osd4,此时pg的up集合由[0,1,3]变成了[0,4,3]。因此pg进入到了interval 2。osd0保存了object1,object2。osd3保存了object2,须要恢复object1。osd4上须要恢复object1,object2。这时的acting集合仍然是[0,4,3]. acting_primary 为osd 0。pg又从新进入到数据恢复的过程,恢复osd4,3上的数据。
5.写入object3。在osd4,osd3上的数据恢复没有完成的时候,又写入object3,这个object3写入到osd0,4,3上。写入数据完成。此时 osd4仍然须要恢复object1,object2。osd3仍然须要恢复数据object1。
6.加入osd5。在osd4,osd3上的数据没有恢复完成前,又加入了osd5 引发了pg的up集合变化,pg的up集合由[0,4,3]变成[5,4,3],因为up集合变化,因此进入interval 3。可是这时osd5,osd4,osd3上都有数据要恢复,在选取acting集合的时候要借鉴interval 2 中的acting集合,为了能恢复数据,这时的acting集合为[0,4,5,3],在肯定acting_primary osd的时候,若是pg进入recoving状态 则选择osd5为acting_primary osd,若pg进入backfill状态则选择osd0为acting_primary osd。假设咱们这时是backfill的pg。则选举osd0为acting_primay。acting_primary 的做用就是用来处理客户端的请求的。此时osd3,osd4,osd5都有object须要恢复,从新开始恢复数据。
7.写入object4。在osd4,osd5,osd3 都有数据须要恢复,可是osd0为acting_primary,因此osd0 接收数据分红4份,分别写入osd0,osd3,osd4,osd5。此时osd0上有 object1,object2,object3,object4。osd3上有object2,object3,object4,须要恢复object1。osd4上有object3,object4,须要恢复object1,object2。osd5上只有object4,须要恢复object1,object2,object3。
8.等待数据恢复完成。当数据恢复完成时,会发送事件给pgmonitor,此时会从新发起pg的peering过程。可是此时up集合为[5,4,3],因为数据恢复完成不须要借鉴interval2,因此acting集合为[5,4,3] ,在acting集合中会选举osd5为acting_primary,将osd0踢出pg。此后由osd5负责 处理客户端的请求。
若有错误,请批评指正。欢迎留言。
2016-11-05:更新end————————————————————————————————