日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

zblock 结构_zfs raidz结构详解

發(fā)布時(shí)間:2024/9/3 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 zblock 结构_zfs raidz结构详解 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

直接進(jìn)入主題,幾個(gè)重點(diǎn):

1、RAIDZ是和ZFS密切配合的一種RAID模型,RAIDZ在接收數(shù)據(jù)時(shí)是由ZFS指定一個(gè)可變長的數(shù)據(jù)流。根據(jù)這個(gè)數(shù)據(jù)流的大小不同,RAIDZ在存儲(chǔ)時(shí)也會(huì)有不同。

2、RAIDZ相對(duì)于傳統(tǒng)RAID,沒有嚴(yán)格的blocksize概念,如果數(shù)據(jù)流小,甚至可以是1扇區(qū)的blocksize。

同時(shí)相對(duì)于傳統(tǒng)RAID,也沒有一個(gè)標(biāo)準(zhǔn)的校驗(yàn)?zāi)J?#xff0c;雖然比較像RAID5,但假如是1扇區(qū)的IO,就更像RAID1了。

3、RAIDZ也可以支持多重冗余,內(nèi)部稱之為RAIDZ_P(即通常提到的RAIDZ,支持1塊硬盤掉線)、RAIDZ_Q(支持2塊盤同時(shí)掉線,如同RAID6)、 RAIDZ_R(支持3塊盤同時(shí)掉線)

4、RAIDZ的IO地址是帶有校驗(yàn)的地址值,不同于傳統(tǒng)RAID校驗(yàn)(傳統(tǒng)RAID的校驗(yàn)區(qū)域?qū)τ谖募到y(tǒng)而言是不可見的)

5、RAIDZ_P的校驗(yàn)位置在每次IO的位置相對(duì)一致,但為了負(fù)載均衡,約定,如果IO首地址是偶數(shù)1M內(nèi)(即offset / 1M為偶數(shù)),校驗(yàn)在數(shù)據(jù)的最前面;如果IO首地址是奇數(shù)1M內(nèi),校驗(yàn)插入數(shù)據(jù)流,在第一個(gè)扇區(qū)(從0開始計(jì)數(shù))。此規(guī)則僅適用于RAIDZ_P,不適用于RAIDZ_Q,RAIDZ_Q

6、RAIDZ約定,一次IO一定是校驗(yàn)數(shù)+1的整數(shù)倍,比如RAIDZ_P一次IO下來如果是3扇區(qū),最后會(huì)有一個(gè)SKIP扇區(qū)(因此,才會(huì)有5中校驗(yàn)要交換的做法),zfs為了保證空間再分配時(shí)不至于出現(xiàn)孔洞,所以在申請(qǐng)空間時(shí),就必須滿足是(nparity + 1)的整數(shù)倍,就樣的好處在于,任意申請(qǐng)的空間,重用時(shí),至少都是夠最小運(yùn)算模式的。

比如:RAIDZ一段連續(xù)的空間中間,釋放了6個(gè)扇區(qū),如果再重用時(shí)只用了5個(gè),那剩下的1個(gè)還是會(huì)浪費(fèi)掉,無法分配。如果是RAIDZ2或RAIDZ3,這種問題就更突出了。反正無法避免浪費(fèi),為了運(yùn)算簡潔,干脆在每次申請(qǐng)時(shí)就按整塊的處理,確保無論如何釋放,都不會(huì)在下一次IO時(shí)出現(xiàn)浪費(fèi)。

7、為了保證IO高效,zfs一次寫入IO時(shí),會(huì)優(yōu)先以vdev為單位連續(xù)寫入,所以,會(huì)很不像1扇區(qū)為條帶大小的RAID5,具體見結(jié)構(gòu)描述示例:假設(shè)有5塊硬盤組成RAIDZ,分別是DISK1,DISK2,DISK3,DISK4,DISK5順序也按此排列:

情況一:如果一次IO大小為1扇區(qū),RAIDZ VDEV的offset地址為X,則(x/5)先計(jì)算出在哪個(gè)條帶,再通過(x % 5)得到開始盤序,在同一條帶上再向后挪一個(gè)磁盤(可能會(huì)返回disk1),這2個(gè)扇區(qū)一個(gè)是數(shù)據(jù),一個(gè)是校驗(yàn)(此情況RAIDZ無需填充),就完成了此次IO的存儲(chǔ)

示例:如果x為10,位于偶數(shù)1M內(nèi),設(shè)數(shù)據(jù)為D,校驗(yàn)為P,則數(shù)據(jù)會(huì)存儲(chǔ)在:

disk1disk2disk3disk4disk5

sec#001234

sec#156789

sec#21011P=DD

示例:如果x位于奇數(shù)1M內(nèi),設(shè)數(shù)據(jù)為D,校驗(yàn)為P,則數(shù)據(jù)會(huì)存儲(chǔ)在:

disk1disk2disk3disk4disk5

sec#51201234

sec#51356789

sec#5141011DP=D

情況二:如果一次IO大小為2扇區(qū),RAIDZ VDEV的offset地址為X,設(shè)"+"表示異或

示例:如果x為10,位于偶數(shù)1M內(nèi),設(shè)數(shù)據(jù)為D1,D2,校驗(yàn)為P,則數(shù)據(jù)會(huì)存儲(chǔ)在:

disk1disk2disk3disk4disk5

sec#001234

sec#156789

sec#21011P=D1+D2D1D2

sec#3SKIP

示例:如果x位于奇數(shù)1M內(nèi),設(shè)數(shù)據(jù)為D1,D2,校驗(yàn)為P,則數(shù)據(jù)會(huì)存儲(chǔ)在:

disk1disk2disk3disk4disk5

sec#51201234

sec#51356789

sec#5141011D1P=D1+D2D2

sec#515SKIP

情況三:如果一次IO大小為5扇區(qū),RAIDZ VDEV的offset地址為X,設(shè)"+"表示異或

示例:如果x為10,位于偶數(shù)1M內(nèi),設(shè)數(shù)據(jù)為D1,D2,D3,D4,D5,校驗(yàn)為P,則數(shù)據(jù)會(huì)存儲(chǔ)在:

disk1disk2disk3disk4disk5

sec#001234

sec#156789

sec#21011P=D1+D3+D4+D5D1D3

sec#3D4D5P=D2D2SKIP

sec#4

示例:如果x位于奇數(shù)1M內(nèi),設(shè)數(shù)據(jù)為D1,D2,D3,D4,校驗(yàn)為P,則數(shù)據(jù)會(huì)存儲(chǔ)在:

disk1disk2disk3disk4disk5

sec#51201234

sec#51356789

sec#5141011D1P=D1+D3+D4+D5D3

sec#515D4D5D2P=D2SKIP

sec#516

情況四:如果一次IO大小為6扇區(qū),RAIDZ VDEV的offset地址為X,設(shè)"+"表示異或

示例:如果x為10,位于偶數(shù)1M內(nèi),設(shè)數(shù)據(jù)為D1,D2,D3,D4,D5,校驗(yàn)為P,則數(shù)據(jù)會(huì)存儲(chǔ)在:

disk1disk2disk3disk4disk5

sec#001234

sec#156789

sec#21011P=D1+D3+D5+D6D1D3

sec#3D5D6P=D2+D4D2D4

sec#4

示例:如果x位于奇數(shù)1M內(nèi),設(shè)數(shù)據(jù)為D1,D2,D3,D4,D5,校驗(yàn)為P,則數(shù)據(jù)會(huì)存儲(chǔ)在:

disk1disk2disk3disk4disk5

sec#51201234

sec#51356789

sec#5141011D1P=D1+D3+D5+D6D3

sec#515D5D6D2P=D2+D4D4

sec#516

源碼主要位于module\zfs\vdev_raidz.c,涉及分配規(guī)則的函數(shù)為vdev_raidz_map_alloc(),仔細(xì)對(duì)源碼解讀、注釋后的結(jié)果如下:/*

*?Divides?the?IO?evenly?across?all?child?vdevs;?usually,?dcols?is

*?the?number?of?children?in?the?target?vdev.

*

*?Avoid?inlining?the?function?to?keep?vdev_raidz_io_start(),?which

*?is?this?functions?only?caller,?as?small?as?possible?on?the?stack.

*/

/*

*分配原則是需要在所有子vdev之間平均分配IO,dcols是目標(biāo)vdev中的子節(jié)點(diǎn)數(shù)。

*避免內(nèi)聯(lián)函數(shù)以保持vdev_raidz_io_start(),它是這個(gè)函數(shù)只有調(diào)用者,在堆棧上要盡可能小。

by:張宇

*/

noinline?static?raidz_map_t?*

vdev_raidz_map_alloc(zio_t?*zio,?uint64_t?unit_shift,?uint64_t?dcols,

uint64_t?nparity)

{

raidz_map_t?*rm;

/*?The?starting?RAIDZ?(parent)?vdev?sector?of?the?block.?*/

/*?在父vdev上的扇區(qū)編號(hào),其實(shí)就是RAIDZx這個(gè)vdev,DVA中標(biāo)注的扇區(qū)號(hào)*/

uint64_t?b?=?zio->io_offset?>>?unit_shift;

/*?The?zio's?size?in?units?of?the?vdev's?minimum?sector?size.?*/

/*一次IO的字節(jié)大小,其實(shí)就是RAIDZx這個(gè)vdev,一次IO的有效數(shù)據(jù)大小(不包含校驗(yàn),扇區(qū)數(shù)*每扇區(qū)字節(jié)數(shù))*/

uint64_t?s?=?zio->io_size?>>?unit_shift;

/*?The?first?column?for?this?stripe.?*/

/*條帶的第一列,是用父vdev的扇區(qū)編號(hào)對(duì)vdev數(shù)(raid成員數(shù))取余的結(jié)果*/

uint64_t?f?=?b?%?dcols;

/*?The?starting?byte?offset?on?each?child?vdev.?*/

/*計(jì)算每個(gè)子vdev的起始字節(jié)位置,用父vdev的扇區(qū)號(hào)簡單地除以"子vdev數(shù)量"*/

uint64_t?o?=?(b?/?dcols)?<

uint64_t?q,?r,?c,?bc,?col,?acols,?scols,?coff,?devidx,?asize,?tot;

/*

*?"Quotient":?The?number?of?data?sectors?for?this?stripe?on?all?but

*?the?"big?column"?child?vdevs?that?also?contain?"remainder"?data.

*/

/*q表示共占用多少完整行(以每個(gè)扇區(qū)為行高)*/

q?=?s?/?(dcols?-?nparity);

/*

*?"Remainder":?The?number?of?partial?stripe?data?sectors?in?this?I/O.

*?This?will?add?a?sector?to?some,?but?not?all,?child?vdevs.

*/

/*r表示除去整數(shù)行外,不足一行部分,還剩多少io扇區(qū)(僅計(jì)數(shù)據(jù),不計(jì)校驗(yàn))*/

r?=?s?-?q?*?(dcols?-?nparity);

/*?The?number?of?"big?columns"?-?those?which?contain?remainder?data.?*/

/*尾部扇區(qū)數(shù),加上可能的校驗(yàn)的大小---如果尾部扇區(qū)數(shù)為0,表示正好湊整N行,就不用另加校驗(yàn)扇區(qū)了。*/

bc?=?(r?==?0???0?:?r?+?nparity);

/*

*?The?total?number?of?data?and?parity?sectors?associated?with

*?this?I/O.

*/

/*表示算上校驗(yàn)的完整扇區(qū)總數(shù)*/

tot?=?s?+?nparity?*?(q?+?(r?==?0???0?:?1));

/*?acols:?The?columns?that?will?be?accessed.?*/

/*?scols:?The?columns?that?will?be?accessed?or?skipped.?*/

/*??acols:需要存取的io列數(shù)?*/

/*??scols:加上可能的skip后的io列數(shù)?*/

/*如果io扇區(qū)數(shù)量不必要?jiǎng)佑盟衯dev,則沒必要所有列都處理*/

if?(q?==?0)?{

/*?Our?I/O?request?doesn't?span?all?child?vdevs.?*/

acols?=?bc;

scols?=?MIN(dcols,?roundup(bc,?nparity?+?1));

}?else?{

acols?=?dcols;

scols?=?dcols;

}

ASSERT3U(acols,?<=,?scols);

rm?=?kmem_alloc(offsetof(raidz_map_t,?rm_col[scols]),?KM_SLEEP);

rm->rm_cols?=?acols;

rm->rm_scols?=?scols;

rm->rm_bigcols?=?bc;

rm->rm_skipstart?=?bc;//表示skip扇區(qū)默認(rèn)位置,放在最后,這是RAIDZ列的位置順序號(hào),表示rm->rm_col[XXX].中的XXX

rm->rm_missingdata?=?0;

rm->rm_missingparity?=?0;

rm->rm_firstdatacol?=?nparity;//默認(rèn)第一個(gè)數(shù)據(jù)塊區(qū)在校驗(yàn)后(但后面為了均衡,會(huì)可能置換)

rm->rm_datacopy?=?NULL;

rm->rm_reports?=?0;

rm->rm_freed?=?0;

rm->rm_ecksuminjected?=?0;

asize?=?0;

for?(c?=?0;?c?

col?=?f?+?c;//f是io的第一列,再求從第一列開始,依次向后

coff?=?o;?//io起始o(jì)ffset

if?(col?>=?dcols)?{?//如果到了列尾,折到下一行

col?-=?dcols;

coff?+=?1ULL?<

}

rm->rm_col[c].rc_devidx?=?col;

rm->rm_col[c].rc_offset?=?coff;

rm->rm_col[c].rc_data?=?NULL;

rm->rm_col[c].rc_gdata?=?NULL;

rm->rm_col[c].rc_error?=?0;

rm->rm_col[c].rc_tried?=?0;

rm->rm_col[c].rc_skipped?=?0;

if?(c?>=?acols)?//如果不足一行,且skip部分的扇區(qū)

rm->rm_col[c].rc_size?=?0;

else?if?(c?

rm->rm_col[c].rc_size?=?(q?+?1)?<

else

rm->rm_col[c].rc_size?=?q?<

asize?+=?rm->rm_col[c].rc_size;//asize等于除去skip的IO字節(jié)數(shù)(包括校驗(yàn))

}

ASSERT3U(asize,?==,?tot?<

rm->rm_asize?=?roundup(asize,?(nparity?+?1)?<

rm->rm_nskip?=?roundup(tot,?nparity?+?1)?-?tot;//skip扇區(qū)數(shù)

ASSERT3U(rm->rm_asize?-?asize,?==,?rm->rm_nskip?<

ASSERT3U(rm->rm_nskip,?<=,?nparity);

for?(c?=?0;?c?rm_firstdatacol;?c++)//為校驗(yàn)分配內(nèi)存

rm->rm_col[c].rc_data?=?zio_buf_alloc(rm->rm_col[c].rc_size);

rm->rm_col[c].rc_data?=?zio->io_data;?//io的原始數(shù)據(jù),指向rm_firstdatacol(等于校驗(yàn)數(shù),即相當(dāng)于先跳過幾列校驗(yàn),之后開始按列寫入真實(shí)數(shù)據(jù))

for?(c?=?c?+?1;?c?

rm->rm_col[c].rc_data?=?(char?*)rm->rm_col[c?-?1].rc_data?+

rm->rm_col[c?-?1].rc_size;

/*

*?If?all?data?stored?spans?all?columns,?there's?a?danger?that?parity

*?will?always?be?on?the?same?device?and,?since?parity?isn't?read

*?during?normal?operation,?that?that?device's?I/O?bandwidth?won't?be

*?used?effectively.?We?therefore?switch?the?parity?every?1MB.

*

*?...?at?least?that?was,?ostensibly,?the?theory.?As?a?practical

*?matter?unless?we?juggle?the?parity?between?all?devices?evenly,?we

*?won't?see?any?benefit.?Further,?occasional?writes?that?aren't?a

*?multiple?of?the?LCM?of?the?number?of?children?and?the?minimum

*?stripe?width?are?sufficient?to?avoid?pessimal?behavior.

*?Unfortunately,?this?decision?created?an?implicit?on-disk?format

*?requirement?that?we?need?to?support?for?all?eternity,?but?only

*?for?single-parity?RAID-Z.

*

*?If?we?intend?to?skip?a?sector?in?the?zeroth?column?for?padding

*?we?must?make?sure?to?note?this?swap.?We?will?never?intend?to

*?skip?the?first?column?since?at?least?one?data?and?one?parity

*?column?must?appear?in?each?row.

*/

/*

如果所有數(shù)據(jù)存儲(chǔ)用到了每一列,則存在校驗(yàn)塊始終在同一設(shè)備上的問題。而校驗(yàn)塊不

參與正常的IO讀取,所以,從負(fù)載角度看,該設(shè)備的I/O帶寬無法被有效使用。因此,

我們每隔1MB切換奇偶校驗(yàn)(方法是僅針對(duì)RAID-Z,每隔1M,交換校驗(yàn)列與第一個(gè)數(shù)據(jù)列)。

疑問1:

校驗(yàn)列和第一個(gè)數(shù)據(jù)列交換,會(huì)不會(huì)因?yàn)楹穸炔煌?IO行數(shù)),導(dǎo)致IO片斷不連續(xù)

答:

不會(huì),因?yàn)樾r?yàn)列是最厚列(必須保證每一行都有校驗(yàn)),第一個(gè)數(shù)據(jù)列,也是最厚列

疑問2:

為什么要有padding?sector?

答:

zfs為了保證空間再分配時(shí)不至于出現(xiàn)孔洞,所以在申請(qǐng)空間時(shí),就必須滿足是(nparity?+?1)

的整數(shù)倍,就樣的好處在于,任意申請(qǐng)的空間,重用時(shí),至少都是夠最小運(yùn)算模式的。

比如:RAIDZ一段連續(xù)的空間中間,釋放了6個(gè)扇區(qū),如果再重用時(shí)只用了5個(gè),那剩下的1個(gè)還是會(huì)浪費(fèi)掉,

無法分配。如果是RAIDZ2或RAIDZ3,這種問題就更突出了。反正無法避免浪費(fèi),為了運(yùn)算簡潔,干脆在每

次申請(qǐng)時(shí)就按整塊的處理,確保無論如何釋放,都不會(huì)在下一次IO時(shí)出現(xiàn)浪費(fèi)。

疑問3:

為什么raidz2和raidz3無需每隔1M交換校驗(yàn)位置

答:

raidz2和raidz3都有超過1個(gè)的校驗(yàn)塊,反正會(huì)橫跨奇偶位置,交換的意義不大(雖然PQR的負(fù)載不完全對(duì)等)

*/

ASSERT(rm->rm_cols?>=?2);

ASSERT(rm->rm_col[0].rc_size?==?rm->rm_col[1].rc_size);

/*if(raidZ?&&?io位置是奇數(shù)個(gè)1M){

交換第一列(校驗(yàn)列),與第二列(第一個(gè)數(shù)據(jù)起始列)

}

*/

if?(rm->rm_firstdatacol?==?1?&&?(zio->io_offset?&?(1ULL?<

devidx?=?rm->rm_col[0].rc_devidx;

o?=?rm->rm_col[0].rc_offset;

rm->rm_col[0].rc_devidx?=?rm->rm_col[1].rc_devidx;

rm->rm_col[0].rc_offset?=?rm->rm_col[1].rc_offset;

rm->rm_col[1].rc_devidx?=?devidx;

rm->rm_col[1].rc_offset?=?o;

//rm->rm_skipstart?=?bc;

//bc=尾部扇區(qū)數(shù),加上校驗(yàn)塊的大小

//如果padding扇區(qū)正好位于第0列,被上面交換過后,就有錯(cuò)誤了

if?(rm->rm_skipstart?==?0)

rm->rm_skipstart?=?1;

}

zio->io_vsd?=?rm;

zio->io_vsd_ops?=?&vdev_raidz_vsd_ops;

return?(rm);

}

總結(jié)

以上是生活随笔為你收集整理的zblock 结构_zfs raidz结构详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。