Hifadhi ya Data Inayodumu na API za Faili za Linux

Nilipokuwa nikitafiti uendelevu wa hifadhi ya data katika mifumo ya wingu, niliamua kujijaribu ili kuhakikisha kwamba nilielewa mambo ya msingi. I ilianza kwa kusoma vipimo vya NVMe ili kuelewa ni dhamana gani kuhusu uhifadhi wa data endelevu (yaani, dhamana ya kwamba data itapatikana baada ya kushindwa kwa mfumo) tupe diski za NMVe. Nilifanya hitimisho kuu zifuatazo: data lazima izingatiwe kuharibiwa tangu wakati amri ya kuandika data inatolewa hadi wakati imeandikwa kwa njia ya kuhifadhi. Walakini, programu nyingi hutumia simu za mfumo kwa furaha kurekodi data.

Katika chapisho hili, ninachunguza njia endelevu za uhifadhi zinazotolewa na API za faili za Linux. Inaonekana kwamba kila kitu kinapaswa kuwa rahisi hapa: programu inaita amri write(), na baada ya amri hii kukamilika, data itahifadhiwa kwa usalama kwenye diski. Lakini write() inakili tu data ya programu kwenye kashe ya kernel iliyo kwenye RAM. Ili kulazimisha mfumo kuandika data kwa diski, unahitaji kutumia njia zingine za ziada.

Hifadhi ya Data Inayodumu na API za Faili za Linux

Kwa ujumla, nyenzo hii ni mkusanyiko wa maelezo yanayohusiana na yale ambayo nimejifunza juu ya mada ya maslahi kwangu. Ikiwa tunazungumza kwa ufupi sana juu ya jambo muhimu zaidi, inageuka kuwa kuandaa uhifadhi wa data endelevu unahitaji kutumia amri. fdatasync() au fungua faili na bendera O_DSYNC. Ikiwa ungependa kujifunza zaidi kuhusu kile kinachotokea kwa data kutoka kwa nambari hadi diski, angalia hii makala.

Vipengele vya kutumia kuandika() kazi

Simu ya mfumo write() iliyofafanuliwa katika kiwango IEEE POSIX kama jaribio la kuandika data kwa maelezo ya faili. Baada ya kukamilika kwa mafanikio write() Uendeshaji wa usomaji data lazima urudishe baiti zilizoandikwa hapo awali, kwa kufanya hivi hata kama data inafikiwa kutoka kwa michakato au nyuzi zingine (tazama sehemu inayofaa ya kiwango cha POSIX). Hapa, katika sehemu ya jinsi nyuzi zinavyoingiliana na utendakazi wa kawaida wa faili, kuna barua inayosema kwamba ikiwa nyuzi mbili kila moja itaita kazi hizi, basi kila simu lazima ione matokeo yote yaliyowekwa ya simu nyingine, au hapana kabisa. matokeo. Hii inasababisha hitimisho kwamba shughuli zote za faili za I/O lazima zishikilie kufuli kwenye rasilimali wanayotumia.

Je, hii ina maana kwamba operesheni write() ni atomiki? Kutoka kwa mtazamo wa kiufundi, ndio. Uendeshaji wa usomaji data lazima urudishe yote au chochote kati ya kile kilichoandikwa write(). Lakini operesheni write(), kulingana na kiwango, si lazima kumaliza kwa kuandika kila kitu ambacho kiliulizwa kuandika. Anaruhusiwa kuandika sehemu tu ya data. Kwa mfano, tunaweza kuwa na nyuzi mbili kila moja ikiambatanisha baiti 1024 kwa faili iliyofafanuliwa na kifafanuzi sawa cha faili. Kutoka kwa mtazamo wa kiwango, matokeo yanayokubalika yatakuwa wakati kila operesheni ya kuandika inaweza kuongeza byte moja tu kwenye faili. Operesheni hizi zitabaki kuwa za atomiki, lakini baada ya kukamilika, data waliyoandika kwenye faili itachanganywa. Hapa majadiliano ya kuvutia sana juu ya mada hii juu ya kufurika kwa Stack.

fsync() na fdatasync() kazi

Njia rahisi zaidi ya kufuta data kwenye diski ni kupiga kazi fsync(). Kazi hii inauliza mfumo wa uendeshaji kuhamisha vitalu vyote vilivyobadilishwa kutoka kwenye cache hadi kwenye diski. Hii inajumuisha metadata zote za faili (muda wa ufikiaji, wakati wa kurekebisha faili, na kadhalika). Ninaamini kuwa metadata hii haihitajiki sana, kwa hivyo ikiwa unajua kuwa sio muhimu kwako, unaweza kutumia kitendakazi. fdatasync(). Katika msaada juu ya fdatasync() inasemekana kwamba wakati wa uendeshaji wa kazi hii, kiasi kama hicho cha metadata kinahifadhiwa kwenye diski ambayo "ni muhimu kwa utekelezaji sahihi wa shughuli zifuatazo za kusoma data." Na hii ndiyo hasa maombi mengi yanajali.

Tatizo moja linaloweza kutokea hapa ni kwamba taratibu hizi hazihakikishi kwamba faili itagunduliwa baada ya kushindwa iwezekanavyo. Hasa, wakati wa kuunda faili mpya, unahitaji kupiga simu fsync() kwa saraka ambayo ina. Vinginevyo, baada ya kushindwa, inaweza kugeuka kuwa faili hii haipo. Sababu ya hii ni kwamba katika UNIX, kutokana na matumizi ya viungo ngumu, faili inaweza kuwepo katika saraka nyingi. Kwa hiyo, wakati wa kupiga simu fsync() hakuna njia ya faili kujua ni data gani ya saraka inapaswa pia kusafishwa kwa diski (hapa Unaweza kusoma zaidi juu ya hii). Inaonekana kama mfumo wa faili wa ext4 una uwezo wa moja kwa moja tumia fsync() kwa saraka zilizo na faili zinazolingana, lakini hii inaweza kuwa sio kwa mifumo mingine ya faili.

Utaratibu huu unaweza kutekelezwa tofauti kwenye mifumo tofauti ya faili. nilitumia blktrace kujifunza juu ya shughuli gani za diski hutumiwa katika mifumo ya faili ya ext4 na XFS. Zote mbili hutoa amri za kawaida za uandishi kwa diski kwa yaliyomo kwenye faili na jarida la mfumo wa faili, futa kashe, na utoke kwa kutekeleza FUA (Ufikiaji wa Kitengo cha Nguvu, kuandika data moja kwa moja kwa diski, kupita kache) kuandika kwenye jarida. Pengine wanafanya hivyo ili kuthibitisha kwamba shughuli hiyo imefanyika. Kwenye viendeshi ambavyo haviungi mkono FUA, hii husababisha mafuriko mawili ya kache. Majaribio yangu yalionyesha hivyo fdatasync() kwa kasi kidogo fsync(). Huduma blktrace inaonyesha kwamba fdatasync() kawaida huandika data kidogo kwa diski (katika ext4 fsync() anaandika 20 KB, na fdatasync() - 16KiB). Pia, niligundua kuwa XFS ni haraka kidogo kuliko ext4. Na hapa kwa msaada blktrace alifanikiwa kugundua hilo fdatasync() husafisha data kidogo kwa diski (4 KB katika XFS).

Hali zisizoeleweka zinazotokea wakati wa kutumia fsync()

Ninaweza kufikiria hali tatu za utata kuhusu fsync()ambayo nilikutana nayo katika mazoezi.

Kesi kama hiyo ya kwanza ilitokea mnamo 2008. Kisha interface ya Firefox 3 ingefungia ikiwa idadi kubwa ya faili ziliandikwa kwenye diski. Shida ilikuwa kwamba utekelezaji wa kiolesura ulitumia hifadhidata ya SQLite kuhifadhi habari kuhusu hali yake. Baada ya kila mabadiliko yaliyotokea kwenye interface, kazi iliitwa fsync(), ambayo ilitoa dhamana nzuri ya uhifadhi wa data thabiti. Katika mfumo wa faili wa ext3 kisha kutumika, kazi fsync() ilitupa kurasa zote "chafu" kwenye mfumo kwa diski, na sio tu zile ambazo zilihusiana na faili inayolingana. Hii ilimaanisha kuwa kubofya kitufe katika Firefox kunaweza kusababisha megabaiti za data kuandikwa kwa diski ya sumaku, ambayo inaweza kuchukua sekunde nyingi. Suluhisho la shida, kwa kadiri ninavyoelewa ni nyenzo ilikuwa kuhamisha kazi na hifadhidata hadi kazi za usuli zisizolingana. Hii ina maana kwamba Firefox hapo awali ilitekeleza mahitaji magumu zaidi ya uhifadhi kuliko ilivyohitajika, na vipengele vya mfumo wa faili wa ext3 vilizidisha tatizo hili.

Tatizo la pili lilitokea mwaka 2009. Kisha, baada ya ajali ya mfumo, watumiaji wa mfumo mpya wa faili wa ext4 walikabiliwa na ukweli kwamba faili nyingi zilizoundwa hivi karibuni zilikuwa na urefu wa sifuri, lakini hii haikutokea kwa mfumo wa faili wa zamani wa ext3. Katika aya iliyotangulia, nilizungumza juu ya jinsi ext3 ilifuta data nyingi kwenye diski, ambayo ilipunguza mambo sana. fsync(). Ili kuboresha hali hiyo, katika ext4 tu kurasa hizo chafu ambazo zinafaa kwa faili fulani hupigwa kwenye diski. Na data kutoka kwa faili zingine inabaki kwenye kumbukumbu kwa muda mrefu zaidi kuliko na ext3. Hii ilifanywa ili kuboresha utendaji (kwa chaguo-msingi, data hukaa katika hali hii kwa sekunde 30, unaweza kusanidi hii kwa kutumia chafu_expire_centisecs; hapa Unaweza kupata nyenzo za ziada kuhusu hili). Hii ina maana kwamba kiasi kikubwa cha data kinaweza kupotea bila kurejeshwa baada ya kushindwa. Suluhisho la tatizo hili ni kutumia fsync() katika programu ambazo zinahitaji kuhakikisha uhifadhi wa data thabiti na kuwalinda iwezekanavyo kutokana na matokeo ya kushindwa. Kazi fsync() inafanya kazi kwa ufanisi zaidi wakati wa kutumia ext4 kuliko wakati wa kutumia ext3. Ubaya wa njia hii ni kwamba matumizi yake, kama hapo awali, hupunguza kasi ya utekelezaji wa shughuli zingine, kama vile kusanikisha programu. Tazama maelezo kuhusu hili hapa ΠΈ hapa.

Tatizo la tatu kuhusu fsync(), ilianzishwa mwaka 2018. Kisha, ndani ya mfumo wa mradi wa PostgreSQL, ilipatikana kuwa ikiwa kazi hiyo fsync() hukutana na hitilafu, inaashiria kurasa "chafu" kama "safi". Matokeo yake, simu zifuatazo fsync() Hawafanyi chochote na kurasa kama hizo. Kwa sababu ya hili, kurasa zilizobadilishwa zimehifadhiwa kwenye kumbukumbu na hazijaandikwa kwa diski. Hii ni maafa ya kweli, kwani programu itafikiri kwamba data fulani imeandikwa kwenye diski, lakini kwa kweli haitakuwa. Vile kushindwa fsync() ni nadra, maombi katika hali kama hizi hayawezi kufanya chochote kukabiliana na shida. Siku hizi, hii inapotokea, PostgreSQL na programu zingine huanguka. Hapa, katika nyenzo "Je, Programu zinaweza Kuokoa kutoka kwa Kushindwa kwa fsync?", Tatizo hili linachunguzwa kwa kina. Hivi sasa suluhisho bora kwa shida hii ni kutumia Direct I/O na bendera O_SYNC au na bendera O_DSYNC. Kwa mbinu hii, mfumo utaripoti makosa ambayo yanaweza kutokea wakati wa shughuli maalum za uandishi, lakini mbinu hii inahitaji programu kudhibiti buffers yenyewe. Soma zaidi kuhusu hili hapa ΠΈ hapa.

Kufungua faili kwa kutumia bendera za O_SYNC na O_DSYNC

Wacha turudi kwenye mjadala wa mifumo ya Linux ambayo hutoa uhifadhi wa data thabiti. Yaani, tunazungumza juu ya kutumia bendera O_SYNC au bendera O_DSYNC wakati wa kufungua faili kwa kutumia simu ya mfumo fungua (). Kwa mbinu hii, kila operesheni ya kuandika data inafanywa kana kwamba baada ya kila amri write() mfumo unapewa amri ipasavyo fsync() ΠΈ fdatasync(). Katika Vipimo vya POSIX hii inaitwa "Ukamilifu wa Uadilifu wa Faili ya I/O" na "Ukamilishaji wa Uadilifu wa Data". Faida kuu ya mbinu hii ni kwamba ili kuhakikisha uadilifu wa data, unahitaji tu kupiga simu ya mfumo mmoja, badala ya mbili (kwa mfano - write() ΠΈ fdatasync()) Ubaya kuu wa njia hii ni kwamba maandishi yote kwa kutumia maelezo ya faili yanayolingana yatasawazishwa, ambayo yanaweza kupunguza uwezo wa kuunda nambari ya programu.

Kwa kutumia I/O ya Moja kwa moja na bendera ya O_DIRECT

Simu ya mfumo open() inasaidia bendera O_DIRECT, ambayo imeundwa ili kupitisha cache ya mfumo wa uendeshaji kufanya shughuli za I/O kwa kuingiliana moja kwa moja na diski. Hii, mara nyingi, ina maana kwamba amri za kuandika zilizotolewa na programu zitatafsiriwa moja kwa moja kwenye amri zinazolenga kufanya kazi na diski. Lakini, kwa ujumla, utaratibu huu sio badala ya kazi fsync() au fdatasync(). Ukweli ni kwamba disk yenyewe inaweza kuahirisha au kache amri zinazolingana za uandishi wa data. Na, ili kufanya mambo kuwa mbaya zaidi, katika baadhi ya matukio maalum shughuli za I/O zinafanywa wakati wa kutumia bendera O_DIRECT, matangazo katika shughuli za jadi zilizoakibishwa. Njia rahisi ya kutatua tatizo hili ni kutumia bendera kufungua faili O_DSYNC, ambayo itamaanisha kwamba kila operesheni ya kuandika itafuatiwa na simu fdatasync().

Ilibadilika kuwa mfumo wa faili wa XFS hivi karibuni umeongeza "njia ya haraka" kwa O_DIRECT|O_DSYNC- kurekodi data. Ikiwa kizuizi kitaandikwa tena kwa kutumia O_DIRECT|O_DSYNC, kisha XFS, badala ya kusafisha kashe, itatoa amri ya uandishi ya FUA ikiwa kifaa kinaiunga mkono. Nilithibitisha hili kwa kutumia matumizi blktrace kwenye mfumo wa Linux 5.4/Ubuntu 20.04. Njia hii inapaswa kuwa na ufanisi zaidi, kwani inapotumiwa, kiasi kidogo cha data kimeandikwa kwenye diski na operesheni moja hutumiwa, badala ya mbili (kuandika na kufuta cache). Nimepata kiungo cha kiraka 2018 kernel, ambayo inatekeleza utaratibu huu. Kuna majadiliano hapo juu ya kutumia utoshelezaji huu kwa mifumo mingine ya faili, lakini nijuavyo, XFS ndio mfumo pekee wa faili unaounga mkono hii hadi sasa.

sync_file_range() kitendakazi

Linux ina simu ya mfumo sync_file_range(), ambayo inakuwezesha kufuta sehemu tu ya faili kwenye diski, badala ya faili nzima. Simu hii huanzisha usawazishaji wa data usiolandanishwa na haisubiri ikamilike. Lakini katika cheti sync_file_range() timu inasemekana kuwa "hatari sana". Haipendekezi kuitumia. Vipengele na hatari sync_file_range() imeelezewa vizuri sana ndani hii nyenzo. Hasa, simu hii inaonekana kutumia RocksDB kudhibiti wakati kernel inasambaza data chafu kwenye diski. Lakini wakati huo huo, ili kuhakikisha hifadhi ya data imara, pia hutumiwa fdatasync(). Katika kanuni RocksDB ina maoni ya kuvutia juu ya mada hii. Kwa mfano, inaonekana kwamba simu sync_file_range() Wakati wa kutumia ZFS, haitoi data kwenye diski. Uzoefu unaniambia kuwa nambari ambayo haitumiki sana inaweza kuwa na mende. Kwa hivyo, ningeshauri dhidi ya kutumia simu ya mfumo huu isipokuwa lazima kabisa.

Simu za mfumo ambazo husaidia kuhakikisha uendelevu wa data

Nimefikia hitimisho kwamba kuna njia tatu ambazo zinaweza kutumika kufanya shughuli za I/O zinazohakikisha uendelevu wa data. Zote zinahitaji simu ya utendaji fsync() kwa saraka ambayo faili iliundwa. Hizi ndizo mbinu:

  1. Kuita kipengele fdatasync() au fsync() baada ya kazi write() (ni bora kutumia fdatasync()).
  2. Kufanya kazi na maelezo ya faili kufunguliwa na bendera O_DSYNC au O_SYNC (bora - na bendera O_DSYNC).
  3. Kwa kutumia amri pwritev2() na bendera RWF_DSYNC au RWF_SYNC (ikiwezekana na bendera RWF_DSYNC).

Vidokezo vya Utendaji

Sijapima kwa uangalifu utendaji wa mifumo mbalimbali niliyochunguza. Tofauti nilizoziona katika kasi ya kazi zao ni ndogo sana. Hii ina maana kwamba ninaweza kuwa na makosa, na kwamba chini ya hali tofauti kitu kimoja kinaweza kutoa matokeo tofauti. Kwanza, nitazungumza juu ya kile kinachoathiri utendaji zaidi, na kisha kinachoathiri utendaji kidogo.

  1. Kubatilisha data ya faili ni haraka kuliko kuambatisha data kwenye faili (faida ya utendakazi inaweza kuwa 2-100%). Kuweka data kwenye faili kunahitaji mabadiliko ya ziada kwenye metadata ya faili, hata baada ya simu ya mfumo fallocate(), lakini ukubwa wa athari hii inaweza kutofautiana. Ninapendekeza, kwa utendaji bora, kupiga simu fallocate() ili kutenga mapema nafasi inayohitajika. Kisha nafasi hii lazima ijazwe kwa uwazi na zero na kuitwa fsync(). Hii itahakikisha kwamba vizuizi vinavyolingana katika mfumo wa faili vinawekwa alama kama "zilizotengwa" badala ya "hazijatengwa". Hii inatoa uboreshaji mdogo (karibu 2%) wa utendaji. Zaidi ya hayo, diski zingine zinaweza kuwa na ufikiaji polepole wa kwanza kwa kizuizi kuliko zingine. Hii ina maana kwamba kujaza nafasi kwa sufuri kunaweza kusababisha uboreshaji mkubwa (karibu 100%) katika utendaji. Hasa, hii inaweza kutokea kwa disks AWS EBS (hii ni data isiyo rasmi, sikuweza kuithibitisha). Vile vile huenda kwa uhifadhi Diski ya Kudumu ya GCP (na hii tayari ni habari rasmi, iliyothibitishwa na vipimo). Wataalam wengine wamefanya vivyo hivyo uchunguzi, kuhusiana na diski mbalimbali.
  2. Kadiri simu zinavyopungua, ndivyo utendaji unavyoongezeka (faida inaweza kuwa karibu 5%). Inaonekana kama changamoto open() na bendera O_DSYNC au piga simu pwritev2() na bendera RWF_SYNC haraka kuliko simu fdatasync(). Ninashuku kuwa jambo hapa ni kwamba mbinu hii ina jukumu katika ukweli kwamba simu chache za mfumo zinapaswa kufanywa ili kutatua shida sawa (simu moja badala ya mbili). Lakini tofauti katika utendaji ni ndogo sana, hivyo unaweza kupuuza kabisa na kutumia kitu katika maombi ambayo si magumu mantiki yake.

Ikiwa una nia ya mada ya uhifadhi endelevu wa data, hapa kuna nyenzo muhimu:

  • Mbinu za Ufikiaji wa I/O β€” muhtasari wa misingi ya mifumo ya pembejeo/pato.
  • Kuhakikisha data inafikia diski - hadithi kuhusu kile kinachotokea kwa data kwenye njia kutoka kwa programu hadi kwenye diski.
  • Ni lini unapaswa kusawazisha saraka iliyo na - jibu la swali la wakati wa kutumia fsync() kwa saraka. Ili kuweka hii kwa ufupi, inageuka kuwa unahitaji kufanya hivyo wakati wa kuunda faili mpya, na sababu ya pendekezo hili ni kwamba katika Linux kunaweza kuwa na kumbukumbu nyingi za faili sawa.
  • Seva ya SQL kwenye Linux: FUA Internals - hapa kuna maelezo ya jinsi uhifadhi wa data unaoendelea kutekelezwa katika Seva ya SQL kwenye jukwaa la Linux. Kuna ulinganisho wa kuvutia kati ya simu za mfumo wa Windows na Linux hapa. Nina hakika kuwa ilikuwa shukrani kwa nyenzo hii ambayo nilijifunza juu ya uboreshaji wa FUA ya XFS.

Je, umepoteza data ambayo ulifikiri ilihifadhiwa kwa usalama kwenye diski?

Hifadhi ya Data Inayodumu na API za Faili za Linux

Hifadhi ya Data Inayodumu na API za Faili za Linux

Chanzo: mapenzi.com