Es, izpējot datu glabāšanas izturību mākoņa sistēmās, nolēmu pārbaudīt sevi un pārliecināties, ka saprotu pamata lietas. Es lai noskaidrotu, kādas garantijas attiecībā uz izturīgu datu glabāšanu (proti — garantijas, ka dati būs pieejami pēc sistēmas kļūmes) mums sniedz NMVe diski. Es izdarīju šādus galvenos secinājumus: no brīža, kad tiek doti dati ierakstīšanai, līdz brīdim, kad tie ir pabeigti ierakstīšanā uz datu nesēja, jāuzskata, ka dati ir bojāti. Tomēr lielākajā daļā datu ierakstīšanas programmu mierīgi tiek izmantoti sistēmas izsaukumi.
Šajā materiālā es pētu izturīga datu glabāšanas mehānismus, ko nodrošina Linux failu API. Izskatās, ka te viss jābūt vienkārši: programma izsauc komandu write(), un, kad šīs komandas izpilde pabeigta, dati tiks droši saglabāti diskā. Bet write() tikai kopē lietotāju datus uz kodola kešatmiņu, kas atrodas operatīvajā atmiņā. Lai piespiedu sistēmu ierakstīt datus uz diska, jāizmanto daži papildu mehānismi.
Kopumā šis materiāls ir piezīmju kopums par to, ko es uzzināju par mani interesējošo tēmu. Ja ļoti īsi pastāstām par vissvarīgāko, tad sanāk, ka, lai organizētu izturīgu datu glabāšanu, ir jāizmanto komanda fdatasync() vai jāatver faili ar karogu O_DSYNC. Ja jums interesē sīkāk uzzināt, kas notiek ar datiem ceļā no programmatūras koda uz disku, ieskatieties rakstā.
funkcijas write() izmantošanas īpatnībās
Sistēmas izsaukums write() tiek definēts standartā kā mēģinājums ierakstīt datus faila aprakstītājā. Pēc veiksmīgas izpildes write() datu lasīšanas operācijām jāatgriež tieši tie biti, kas iepriekš tika ierakstīti, to veicot pat tajā gadījumā, ja dati tiek piekļūti no citiem procesiem vai pavedieniem ( atbilstošā POSIX standarta sadaļa). , sadaļā, kas veltīta plūsmu mijiedarbībai ar parastajām failu operācijām, ir piezīme, kurā teikts, ka, ja katra no divām plūsmām izsauc šīs funkcijas, tad katram izsaukumam jāredz vai nu visas norādītās sekas, kuras rada cita izsaukuma izpilde, vai arī neviena sekas. Tas ļauj secināt, ka visām ievades/izvades failu operācijām jānotur resursa bloķēšana, ar kuru tās strādā.
Vai tas nozīmē, ka operācija write() ir atomāra? Tehniski runājot — jā. Datu lasīšanas operācijām jāatgriež vai nu viss, vai nekas no tā, kas tika ierakstīts, izmantojot write(). Bet operācija write(), saskaņā ar standartu, nav obligāti jābeidzas, ierakstot visu to, ko tai tika piedāvāts ierakstīt. Tās atļauts ierakstīt tikai daļu datu. Piemēram, mums var būt divas plūsmas, no kurām katra pievieno 1024 baitus failam, kas aprakstīts ar vienu un to pašu failu aprakstītāju. Standarta acīs pieņemams būs rezultāts, kad katra no rakstīšanas operācijām varēs pievienot failam tikai vienu baitu. Šīs operācijas paliks atomāras, taču, kad tās beigsies, tajā failā ierakstītie dati būs sajaukti. ļoti interesanta diskusija par šo tēmu Stack Overflow.
Funkcijas fsync() un fdatasync()
Vienkāršākais veids, kā nosūtīt datus uz disku, ir izsaukt funkciju . Šī funkcija pieprasa operētājsistēmai pārsūtīt visus modificētos blokos no kešatmiņas uz disku. Tajā ietilpst arī visi faila metadati (piekļuves laiks, faila modificēšanas laiks utt.). Es uzskatu, ka nepieciešamība pēc šiem metadatiem rodas reti, tāpēc, ja jūs zināt, ka tie jums nav svarīgi, jūs varat izmantot funkciju fdatasync(). Iz par fdatasync() teikts, ka tās funkcijas darbības laikā tiek saglabāts diskā tāds metadatu apjoms, kas "nepieciešams nākamo datu lasīšanas operāciju pareizai izpildei". Un tas ir tieši tas, kas uztrauc lielāko daļu lietojumprogrammu.
Viens no jautājumiem, kas šeit var rasties, ir tas, ka šie mehānismi negarantē, ka failu varēs atrast pēc iespējamā avārijas. Proti, ja tiek izveidots jauns fails, ir jāizsauc fsync() direktorijai, kurā tas atrodas. Pretējā gadījumā pēc avārijas var izrādīties, ka šis fails nepastāv. Iemesls tam ir tas, ka UNIX sistēmās, pateicoties cietajiem saites, failam var būt vairākas atrašanās vietas direktorijās. Tāpēc, izsaucot fsync() fails nevar zināt, kuru tieši direktoriju vajadzētu saglabāt diskā ( par to var lasīt sīkāk). Izrādās, ka failu sistēma ext4 ir spējīga tikt pielietoti fsync() uz direktorijām, kas satur attiecīgos failus, bet citām failu sistēmām tas var nebūt tā.
Šis mehānisms var būt dažādi realizēts dažādās failu sistēmās. Es izmantoju lai uzzinātu, kādas diskoperācijas tiek izmantotas failu sistēmās ext4 un XFS. Abas sniedz parastus komandas, kas raksta diskā, gan failu saturu, gan failu sistēmas žurnālu, iztīra kešu un pabeidz darbu, veicot FUA rakstīšanu (Force Unit Access, datu rakstīšana tieši diskā, apejot kešu) žurnālā. Iespējams, viņi tamdēļ dara tā, lai apstiprinātu operācijas veikšanu. Diskos, kas neatbalsta FUA, tas izraisa divu kešu iztīrīšanu. Mani eksperimenti parādīja, ka fdatasync() nedaudz ātrāk fsync(). Rīks blktrace norāda, ka fdatasync() parasti raksta diskā mazāk datu (ext4 fsync() ieraksta 20 KiB, bet fdatasync() — 16 KiB). Turklāt es noskaidroju, ka XFS ir nedaudz ātrāks nekā ext4. Un šeit ar palīdzību blktrace izdevās noskaidrot, ka fdatasync() izmet diskā mazāk datu (4 KiB XFS).
Neskaidras situācijas, kas rodas, lietojot fsync()
Es varu atcerēties trīs neskaidras situācijas, kas saistītas fsync(), ar kurām es saskāros praksē.
Pirmais šāds gadījums notika 2008. gadā. Tad Firefox 3 interfeiss «iesaldēja» tajā gadījumā, ja notika liela skaita failu rakstīšana diskā. Problēma bija tā, ka interfeisa stāvokļa datu uzglabāšanai tika izmantota SQLite datubāze. Pēc katras izmaiņas, kas notika interfeisā, tika izsaukta funkcija fsync(), kas sniedza labas garantijas datu stabilai saglabāšanai. Izmantotajā failu sistēmā ext3 funkcija fsync() tika tika diska visas "netīrās" lapas sistēmā, nevis tikai tās, kas bija saistītas ar konkrēto failu. Tas nozīmēja, ka klikšķis uz pogas Firefox varēja ierosināt megabaitu datu ierakstīšanu magnētiskajā diskā, kas varēja aizņemt daudzas sekundes. Problēmas risinājums, cik es sapratu no materiāla, bija pārvietot darbu ar datu bāzi uz asinkronajām fonu uzdevumiem. Tas nozīmē, ka iepriekš Firefox bija ieviesti stingrāki datu saglabāšanas noturības prasības, nekā patiesībā bija nepieciešams, un ext3 failu sistēmas īpatnības tikai saasināja šo problēmu.
Otra nelāga notikās 2009. gadā. Toreiz, pēc sistēmas avārijas, jaunās ext4 failu sistēmas lietotāji saskārās ar to, ka daudzi nesen izveidotie faili ir ar nulles garumu, bet ar vecāko ext3 failu sistēmu tāda problēma nenotika. Iepriekšējā rindkopā es runāju par to, ka ext3 diska izsiti pārāk daudz datu, kas būtiski palēnināja darbu fsync(). Lai uzlabotu situāciju, ext4 diska izsiti tikai tās "netīrās" lapas, kas ir saistītas ar konkrēto failu. Savukārt citu failu dati paliek atmiņā daudz ilgāk nekā ar ext3 izmantošanu. Tas tika izdarīts veiktspējas uzlabošanas nolūkos (pēc noklusējuma dati paliek šādā stāvoklī 30 sekundes, šo iestatījumu var mainīt ar ; , var atrast papildu materiālus par šo). Tas nozīmē, ka liels datu apjoms var tikt neatgriezeniski zaudēts pēc avārijas. Šīs problēmas risinājums ir fsync() lietojumprogrammās, kurām ir nepieciešams nodrošināt datu ilgstošu saglabāšanu un maksimāli pasargāt tos no avāriju sekām. Funkcija fsync() strādā ar ext4 daudz efektīvāk nekā ar ext3. Šā pieejas mīnuss ir tas, ka tās izmantošana, tāpat kā iepriekš, palēnina dažu operāciju izpildi, piemēram, programmu instalāciju. Sīkāku informāciju par to skatiet un .
Trešā problēma, kas attiecas uz fsync(), radās 2018. gadā. Toreiz, PostgreSQL projekta ietvaros, tika noskaidrots, ka, ja funkcija fsync() saskaras ar kļūdu, tā atzīmē "netīrās" lapas kā "tīras". Rezultātā nākamie izsaukumi fsync() nekas ar šādām lapām netiek darīts. Tāpēc modificētās lapas tiek glabātas atmiņā un nekad netiek ierakstītas diskā. Tas ir īsta katastrofa, jo lietotne uzskatīs, ka daži dati ir ierakstīti diskā, bet patiesībā tā nebūs. Šādi traucējumi fsync() ir reti sastopami, lietotne šādās situācijās gandrīz neko nevar darīt, lai cīnītos pret problēmu. Mūsdienās, kad tas notiek, PostgreSQL un citas lietotnes avarē. , materiālā «Vai lietotnes var atgūt no fsync kļūdām?», šī problēma ir pētīta visos sīkumos. Pašlaik labākais risinājums šai problēmai ir izmantot Direct I/O ar karogu O_SYNC vai ar karogu O_DSYNC. Izmantojot šo pieeju, sistēma ziņos par kļūdām, kas var rasties, veicot konkrētas datu ierakstīšanas operācijas, bet šī pieeja prasa, lai lietotne pati pārvaldītu buferus. Par to lasiet un .
Failu atvēršana, izmantojot karogus O_SYNC un O_DSYNC
Atgriezīsimies pie diskusiju par Linux mehānismiem, kas nodrošina datu noturīgu glabāšanu. Proti, runa ir par karoga izmantošanu O_SYNC vai karoga O_DSYNC failu atvēršanā, izmantojot sistēmas izsaukumu . Izmantojot šo pieeju, katra datu ierakstīšanas operācija tiek veikta tā, it kā pēc katras komandas write() sistēmā tiek dotas attiecīgi komandas fsync() un fdatasync(). Iz to dēvē par «Sakārtotu I/O failu integritātes pabeigšanu» un «Datu integritātes pabeigšanu». Galvenā šī pieejas priekšrocība ir tā, ka datu integritātes nodrošināšanai jāveic tikai viens sistēmas izsaukums, nevis divi (piemēram — write() un fdatasync()). Galvenais šīs pieejas trūkums ir tas, ka visas ierakstīšanas operācijas, kas izmanto attiecīgo faila deskriptoru, tiks sinhronizētas, kas var ierobežot lietotnes koda struktūras iespējas.
Izmantošana Direct I/O ar karogu O_DIRECT
Sistēmas izsaukums open() atbalsta karogu O_DIRECT, kas paredzēts tam, lai, apejot operācijas sistēmas kešu, veiktu ievades-izvades operācijas, tieši sadarbojoties ar disku. Tas, daudzu gadījumu, nozīmē, ka ierakstīšanas komandas, ko izplata programma, tiks tieši pārveidotas par komandām, kas vērstas uz darbu ar disku. Bet, vispārīgi, šis mehānisms nav aizstāšana funkcijām fsync() vai fdatasync(). Problēma ir tāda, ka pats disks var atbilstošās datu ierakstīšanas komandas. Un, kas vēl sliktāk, noteiktos īpašos gadījumos ievades-izvades operācijas, kas veiktas, izmantojot karogu O_DIRECT, tradicionālajās buferizētajās operācijās. Šo problēmu visvieglāk var atrisināt, izmantojot failu atvēršanai arī karogu O_DSYNC, kas nozīmēs, ka katrai ierakstīšanas operācijai sekos izsaukums fdatasync().
Izrādās, ka XFS failu sistēmā nesen ir pievienota "ātrā pieeja" O_DIRECT|O_DSYNC-datu ierakstiem. Ja pārraksta bloku, izmantojot O_DIRECT|O_DSYNC, tad XFS, nevis iztukšojot kešu, izpildīs FUA ierakstīšanas komandu, ja iekārta to atbalsta. Es to pārbaudīju, izmantojot utilītu blktrace Linux 5.4 / Ubuntu 20.04 sistēmā. Šāda pieeja būtu efektīvāka, jo tās izmantošanas gadījumā diskā tiek ierakstīts minimāls datu daudzums, un tiek veikta viena operācija, nevis divas (ieraksts un kešatmiņas iztukšošana). Es atradu saiti uz 2018. gada kodu, kurā tika īstenots šis mehānisms. Tur ir diskusija par šīs optimizācijas pielietošanu arī citās failu sistēmās, taču, cik man zināms, XFS ir pagaidām vienīgā failu sistēma, kas to atbalsta.
sync_file_range() funkcija
Linux ir sistēmas izsaukums , kas ļauj iztukšot diskā tikai daļu no faila, nevis visu failu. Šis izsaukums iniciē asinkrono datu iztukšošanu un negaida tās pabeigšanu. Bet, kā minēts sync_file_range() , šis rīks ir "ļoti bīstams". Tā lietošana nav ieteicama. Īpašības un bīstamības sync_file_range() ir ļoti labi aprakstītas materiālā. Īpaši, šķiet, ka šis izsaukums izmanto RocksDB, lai pārvaldītu, kad kods iztukšo "netīros" datus diskā. Tomēr tur, lai nodrošinātu stabilu datu glabāšanu, tiek izmantots arī fdatasync(). Iz RocksDB ir interesantas piezīmes par šo tēmu. Piemēram, šķiet, ka izsaukums sync_file_range() izmantojot ZFS, nenoved pie datu iztukšošanas diskā. Mans pieredze liecina, ka kods, ko izmanto reti, iespējams, satur kļūdas. Tāpēc es ieteiktu nelietot šo sistēmas izsaukumu bez absolūtas nepieciešamības.
Sistēmas izsaukumi, kas palīdz nodrošināt stabilu datu glabāšanu
Es nonācu, ka trīs pieejas var izmantot, lai veiktu ievades/izvades operācijas, kas nodrošina noturīgu datu glabāšanu. Viss prasa izsaukt funkciju fsync() direktorijā, kurā ir izveidots fails. Šīs ir pieejas:
- Funkcijas izsaukums
fdatasync()vaifsync()pēc funkcijaswrite()(velams izmantotfdatasync()). - Darbs ar faila deskriptoru, kas atvērts ar karogu
O_DSYNCvaiO_SYNC(labāk — ar karoguO_DSYNC). - Komandas izmantošana
pwritev2()ar atzīmiRWF_DSYNCvaiRWF_SYNC(priekšroka — ar karoguRWF_DSYNC).
Veiktspējas piezīmes
Es neesmu veicis rūpīgu dažādu manā pētījumā iepazīto mehānismu veiktspējas mērījumu. Mani uztvertie ātruma atšķirības ir diezgan niecīgas. Tas nozīmē, ka es varu kļūdīties, un, ka citos apstākļos tas pats var radīt citus rezultātus. Visupirms es pastāstīšu par to, kas ietekmē veiktspēju vairāk, bet pēc tam, par to, kas ietekmē veiktspēju mazāk.
- Datu pārrakstīšana failā ir ātrāka nekā datu pievienošana failam (veiktspējas ieguvums var būt no 2-100%). Datu pievienošana failam prasa veikt papildu izmaiņas faila metadatos, pat pēc sistēmas izsaukuma
fallocate(), bet šī efekta apjoms var mainīties. Es iesaku, lai nodrošinātu labāko veiktspēju, izsauktfallocate()lai iepriekšēji rezervētu nepieciešamo telpu. Pēc tam šī telpa jāaizpilda ar nullēm un jāizsaucfsync(). Pateicoties tam, attiecīgie bloki failu sistēmā tiks marķēti kā "rezervēti", nevis kā "nereservēti". Tas nodrošina nelielu (apmēram 2%) veiktspējas uzlabošanu. Turklāt dažiem diskam pirmā piekļuves operācija blokam var notikt lēnāk nekā citām. Tas nozīmē, ka telpas aizpildīšana ar nullēm var radīt ievērojamu (apmēram 100%) veiktspējas uzlabošanu. Īpaši tas var notikt diskos (šie ir neoficiāli dati, kurus es nevarēju apstiprināt). Tas pats attiecas arī uz krātuvēm (un šie ir oficiāli dati, ko apstiprinājuši testi). Citi speciālisti ir veikuši līdzīgas , kas attiecas uz dažādiem diskiem. - Jo mazāk sistēmas izsaukumu, jo augstāka veiktspēja (iegūšana var būt apmēram 5%). Izskatās, ka izsaukums
open()ar atzīmiO_DSYNCvai izsaukumspwritev2()ar atzīmiRWF_SYNCir ātrāk nekā izsaukumsfdatasync()Pārliecinājos, ka šeit loma ir tas, ka šāds pieejas veids nozīmē, ka, lai atrisinātu to pašu uzdevumu, ir jāveic mazāk sistēmas izsaukumu (viens izsaukums, nevis divi). Taču veiktspējas atšķirība ir ļoti maza, tāpēc jūs varat to pilnīgi ignorēt un lietot aplikācijā to, kas nenovedīs pie tās loģikas sarežģīšanas.
Ja jūs interesē ilgtspējīgas datu glabāšanas tēma — šeit ir dažas noderīgas materisāls:
- — apskats par ievades/izvades mehānismu pamatiem.
- — stāsts par to, kas notiek ar datiem ceļā no aplikācijas līdz diskam.
- — atbilde uz jautājumu, kad jāizmanto
fsync()direktorijām. Ja šo izskaidrot divos vārdos, tad izrādās, ka tas jādara, izveidojot jaunu failu, un iemesls šai rekomendācijai ir tāds, ka Linux var būt daudz saišu uz to pašu failu. - — šeit ir aprakstīts, kā ilgtspējīga datu glabāšana ir īstenota SQL Server Linux platformā. Šeit ir dažas interesantas salīdzinošas atšķirības starp Windows un Linux sistēmas izsaukumiem. Es esmu gandrīz pārliecināts, ka tieši pateicoties šim materiālam uzzināju par FUA optimizāciju XFS.
Vai jūs kādreiz esat zaudējuši datus, ko uzskatījāt par droši saglabātiem diskā?
Avots: habr.com
