ceph的數據存儲之路(8) -----pg 的數據恢復過程

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——————————————————————————————————————

 

PG 的數據恢復過程

   集羣中的設備異常(異常OSD的添加刪除操做),會致使PG的各個副本間出現數據的不一致現象,這時就須要進行數據的恢復,讓全部的副本都達到一致的狀態。想知道如何來進行數據的恢復以前,先要了解OSD故障的種類。

 

 

1、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操做。

 

 

2、OSD恢復時經常使用的基本概念解析:

(引用自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

  }

 

3、數據的恢復過程的簡要流程描述以下:

      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處理成功,開始進行數據的恢復。

 

4、數據恢復的具體流程:

 

詳細分析這個過程主要涉及到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————————————————————————————————

相關文章
相關標籤/搜索