Storage Durable Data è API di File Linux

I, a ricerca di a stabilità di l'almacenamiento di dati in i sistemi di nuvola, decisu di pruvà à mè stessu, per assicurà chì aghju capitu e cose basi. I principiatu da leghje a spec NVMe per capisce ciò chì guarantisci riguardu à a persistenza di dati (vale à dì, guarantisci chì i dati seranu dispunibili dopu à un fallimentu di u sistema) ci danu dischi NMVe. Aghju fattu e seguenti cunclusioni principali: avete bisognu di cunsiderà i dati dannighjati da u mumentu chì u cumandamentu di scrittura di dati hè datu, è finu à u mumentu chì sò scritti à u mediu di almacenamiento. In ogni casu, in a maiò parte di i prugrammi, i chjami di u sistema sò usati in modu abbastanza sicuru per scrive dati.

In questu articulu, scopre i meccanismi di persistenza furniti da l'API di file Linux. Sembra chì tuttu deve esse simplice quì: u prugramma chjama u cumandamentu write(), è dopu chì l'operazione di stu cumandamentu hè cumpletu, i dati seranu guardati in modu sicuru in u discu. Ma write() copia solu i dati di l'applicazione à a cache di u kernel situatu in RAM. Per furzà u sistema à scrive dati à u discu, alcuni meccanismi supplementari devenu esse usatu.

Storage Durable Data è API di File Linux

In generale, stu materiale hè un inseme di note relative à ciò chì aghju amparatu nantu à un tema d'interessu per mè. Se parlemu assai brevemente di u più impurtante, risulta chì per urganizà un almacenamentu di dati sustinibili, avete bisognu di utilizà u cumandamentu. fdatasync() o apre i schedari cù bandiera O_DSYNC. Sè vo site interessatu à amparà di più nantu à ciò chì succede à e dati nantu à a strada da u codice à u discu, fate un ochju questu articulu.

Funzioni di utilizà a funzione write().

Chjama di sistema write() definitu in u standard IEEE POSIX cum'è un tentativu di scrive dati à un descrittore di file. Dopu à a fine di u travagliu successu write() L'operazione di lettura di dati deve rinvià esattamente i byte chì sò stati scritti prima, fendu cusì ancu s'ellu si accede à e dati da altri prucessi o fili (eccu sezione corrispondente di u standard POSIX). , in a rùbbrica nantu à l'interazzione di i fili cù l'operazioni di u schedariu normale, ci hè una nota chì dice chì, se dui fili chjamanu sti funzioni, ogni chjama deve vede tutte e cunsequenze indicate chì l'esekzione di l'altra chjama porta, o ùn vede micca nunda di cunsequenze. Questu porta à a cunclusione chì tutte l'operazioni di l'I / O di u schedariu deve tene una serratura nantu à a risorsa chì hè travagliatu.

Questu significa chì l'operazione write() hè atomicu? Da un puntu di vista tecnicu, sì. L'operazione di lettura di dati deve rinvià tuttu o nimu di ciò chì hè statu scrittu write(). Ma l'operazione write(), in cunfurmità cù u standard, ùn deve micca finisce, avè scrittu tuttu ciò chì era dumandatu à scrive. Hè permessu di scrive solu una parte di e dati. Per esempiu, pudemu avè dui flussi chì aghjunghjenu ogni 1024 bytes à un schedariu deskrittu da u stessu descrittore di u schedariu. Da u puntu di vista di u standard, u risultatu serà accettatu quandu ognuna di l'operazioni di scrittura pò aghjunghje solu un byte à u schedariu. Queste operazioni fermanu atomiche, ma dopu avè finitu, i dati chì scrivenu à u schedariu seranu cunfusi. quì discussione assai interessante nantu à questu tema nantu à Stack Overflow.

funzioni fsync() è fdatasync().

A manera più faciule di sbulicà e dati à u discu hè di chjamà a funzione fsync (). Questa funzione dumanda à u sistema operatore di spustà tutti i blocchi mudificati da u cache à u discu. Questu include tutte e metadata di u schedariu (tempu d'accessu, tempu di mudificazione di u schedariu, etc.). Credu chì sta metadata hè raramente necessariu, perchè se sapete chì ùn hè micca impurtante per voi, pudete aduprà a funzione fdatasync(). L' aiutu nantu fdatasync() dice chì durante l'operazione di sta funzione, una tale quantità di metadata hè salvata à u discu, chì hè "necessariu per l'esekzione curretta di e seguenti operazioni di lettura di dati". È questu hè esattamente ciò chì a maiò parte di l'applicazioni importa.

Un prublema chì pò esce quì hè chì sti miccanismi ùn guarantisci micca chì u schedariu pò esse truvatu dopu un eventuale fallimentu. In particulare, quandu un novu schedariu hè creatu, unu deve chjamà fsync() per u cartulare chì cuntene. Altrimenti, dopu un crash, pò esse chì stu schedariu ùn esiste micca. U mutivu di questu hè chì sottu UNIX, per via di l'usu di ligami duri, un schedariu pò esse in parechje cartulari. Dunque, quandu si chjama fsync() ùn ci hè micca manera per un schedariu di sapè quale dati di u repertoriu anu da esse lavati ancu à u discu (ccà pudete leghje più nantu à questu). Sembra chì u sistema di schedari ext4 hè capaci di automaticamente per appiicà fsync() à cartulari chì cuntenenu i schedarii currispundenti, ma questu pò esse micca u casu cù altri sistemi di schedari.

Stu mecanismu pò esse implementatu in modu diversu in diversi sistemi di schedari. Aghju utilizatu blktrace per sapè quale operazioni di discu sò aduprate in i sistemi di schedari ext4 è XFS. Tramindui emettenu i cumandamenti di scrittura abituale à u discu per u cuntenutu di i schedari è u ghjurnale di u sistema di fugliale, sguassate u cache è esce da eseguendu un FUA (Force Unit Access, scrivendu dati direttamente à u discu, bypassing the cache) scrive à u ghjurnale. Probabilmente facenu cusì per cunfirmà u fattu di a transazzione. In unità chì ùn supportanu micca FUA, questu provoca dui flushe di cache. I mo esperimenti anu dimustratu chì fdatasync() un pocu più veloce fsync(). Utilità blktrace indica chì fdatasync() di solitu scrive menu dati à u discu (in ext4 fsync() scrive 20 KiB, è fdatasync() - 16 KiB). Inoltre, aghju scupertu chì XFS hè un pocu più veloce di ext4. È quì cù l'aiutu blktrace era capaci di scopre chì fdatasync() scarica menu dati à u discu (4 KiB in XFS).

Situazioni ambigue quandu si usa fsync ()

Puderaghju pensà à trè situazioni ambigue riguardanti fsync()chì aghju scontru in pratica.

U primu tali incidente hè accadutu in u 2008. À quellu tempu, l'interfaccia Firefox 3 "congelata" se un gran numaru di schedari eranu scritti à u discu. U prublema era chì l'implementazione di l'interfaccia utilizzava una basa di dati SQLite per almacenà l'infurmazioni nantu à u so statu. Dopu ogni cambiamentu chì hè accadutu in l'interfaccia, a funzione hè stata chjamata fsync(), chì hà datu boni garanti di almacenamentu di dati stabile. In u sistema di schedariu ext3 allora utilizatu, a funzione fsync() lavate à u discu tutte e pagine "spurte" in u sistema, è micca solu quelli chì eranu ligati à u schedariu currispundente. Questu significava chì cliccà un buttone in Firefox puderia causà megabytes di dati per esse scritti à un discu magneticu, chì puderia piglià parechji seconde. A suluzione à u prublema, finu à ch'e aghju capitu si materiale, era di trasfurmà u travagliu cù a basa di dati à i travaglii di fondo asincroni. Questu significa chì Firefox hà utilizatu per implementà esigenze di persistenza di almacenamiento più strette di ciò chì era veramente necessariu, è e funzioni di u sistema di file ext3 anu aggravatu solu stu prublema.

U sicondu prublema hè accadutu in u 2009. Dopu, dopu à un crash di u sistema, l'utilizatori di u novu sistema di schedari ext4 anu truvatu chì parechji schedarii di novu creati eranu di lunghezza zero, ma questu ùn hè micca successu cù u sistema di schedari ext3 più anticu. In u paràgrafu precedente, aghju parlatu di cumu l'ext3 hà scaricatu troppu dati nantu à u discu, chì rallentò assai e cose. fsync(). Per migliurà a situazione, ext4 sguassate solu e pagine "spurte" chì sò pertinenti à un schedariu particulari. E i dati di l'altri schedari restanu in memoria per un tempu assai più longu chì cù ext3. Questu hè statu fattu per migliurà u rendiment (per difettu, i dati restanu in questu statu per 30 seconde, pudete cunfigurà questu utilizendu dirty_expire_centisecs; ccà pudete truvà più infurmazione nantu à questu). Questu significa chì una grande quantità di dati pò esse irretrievably persu dopu un crash. A suluzione à stu prublema hè di utilizà fsync() in l'applicazioni chì anu bisognu di furnisce un almacenamentu di dati stabile è prutegge quant'è pussibule da e cunsequenze di fallimenti. Funzione fsync() travaglia assai più efficace cù ext4 cà cù ext3. U svantaghju di stu approcciu hè chì u so usu, cum'è prima, rallenta certi operazioni, cum'è a stallazione di prugrammi. Vede i dettagli nantu à questu ccà и ccà.

U terzu prublema riguardanti fsync(), urigginatu in 2018. Allora, in u quadru di u prugettu PostgreSQL, hè statu scupertu chì si a funzione fsync() scontra un errore, marca pagine "spurche" cum'è "pulite". In u risultatu, i seguenti chjama fsync() ùn fate nunda cù tali pagine. Per quessa, e pagine mudificate sò guardate in memoria è mai scritte à u discu. Questu hè un veru disastru, perchè l'applicazione pensarà chì certi dati sò scritti à u discu, ma in fattu ùn serà micca. Tali fallimenti fsync() sò rari, l 'applicazzioni in tali situazioni pò fà guasi nunda à cumbatte u prublema. In questi ghjorni, quandu questu succede, PostgreSQL è altre applicazioni crash. , in l'articulu "Can Applications Recover from fsync Failures?", Stu prublema hè esploratu in detail. Attualmente a megliu suluzione à stu prublema hè di utilizà l'I / O direttu cù a bandiera O_SYNC o cù una bandiera O_DSYNC. Cù questu approcciu, u sistema informarà l'errori chì ponu accade quandu eseguisce operazioni specifiche di scrittura di dati, ma questu approcciu richiede l'applicazione per gestisce i buffers stessu. Leghjite più nantu à questu ccà и ccà.

Apertura di i fugliali cù i bandieri O_SYNC è O_DSYNC

Riturnemu à a discussione di i meccanismi Linux chì furnisce un almacenamentu di dati persistenti. Vale à dì, si parla di l'usu di a bandiera O_SYNC o bandiera O_DSYNC quandu apre i fugliali cù a chjama di u sistema apre (). Cù questu approcciu, ogni operazione di scrittura di dati hè realizatu cum'è dopu à ogni cumanda write() u sistema hè datu, rispettivamente, cumandamenti fsync() и fdatasync(). L' Specificazioni POSIX chistu veni chiamatu "Synchronized I/O File Integrity Completion" è "Data Integrity Completion". U vantaghju principali di questu approcciu hè chì solu una chjama di sistema deve esse eseguita per assicurà l'integrità di e dati, è micca duie (per esempiu - write() и fdatasync()). U principale svantaghju di questu approcciu hè chì tutte l'operazioni di scrittura chì utilizanu u descriptore di u schedariu currispundenti seranu sincronizate, chì ponu limità a capacità di struttura u codice di l'applicazione.

Utilizà l'I/O direttu cù a bandiera O_DIRECT

Chjama di sistema open() sustene a bandiera O_DIRECT, chì hè cuncepitu per scaccià a cache di u sistema operatore, eseguisce operazioni I / O, interagisce direttamente cù u discu. Questu, in parechji casi, significa chì i cumandamenti di scrittura emessi da u prugramma seranu tradutti direttamente in cumandamenti destinati à travaglià cù u discu. Ma, in generale, stu mecanismu ùn hè micca un sustitutu di e funzioni fsync() o fdatasync(). U fattu hè chì u discu stessu pò ritardu o cache cumandamenti adattati per scrive dati. E, ancu peggiu, in certi casi spiciali, l'operazioni I / O realizate quandu si usa a bandiera O_DIRECT, broadcast in operazioni tradiziunali buffered. U modu più faciule per risolve stu prublema hè di utilizà a bandiera per apre i schedari O_DSYNC, chì significarà chì ogni operazione di scrittura serà seguita da una chjama fdatasync().

Risultava chì u sistema di fugliale XFS hà aghjustatu recentemente un "caminu veloce" per O_DIRECT|O_DSYNC- i registri di dati. Se u bloccu hè sovrascrittu usendu O_DIRECT|O_DSYNC, dopu XFS, invece di lavà a cache, eseguirà u cumandamentu di scrittura FUA se u dispusitivu sustene. Aghju verificatu questu cù l'utilità blktrace nantu à un sistema Linux 5.4/Ubuntu 20.04. Stu approcciu deve esse più efficaci, postu chì scrive a quantità minima di dati à u discu è usa una operazione, micca duie (scrivite è sguassate a cache). Aghju trovu un ligame patch 2018 kernel chì implementa stu mecanismu. Ci hè una discussione nantu à l'applicazioni di sta ottimisazione à l'altri filesystems, ma quant'è cunnoscu, XFS hè l'unicu sistema di fugliale chì sustene finu à avà.

sync_file_range() funzione

Linux hà una chjama di sistema sync_file_range(), chì vi permette di sguassà solu una parte di u schedariu à u discu, micca u schedariu sanu. Questa chjama inizia un flush asincronu è ùn aspetta micca chì finiscinu. Ma in u riferimentu à sync_file_range() stu cumandamentu hè dettu "assai periculosu". Ùn hè cunsigliatu per aduprà. Funzioni è periculi sync_file_range() assai ben descrittu in questu materiale. In particulare, sta chjamata pare aduprà RocksDB per cuntrullà quandu u kernel flushes data "spurcia" à u discu. Ma à u listessu tempu ci, per assicurà un almacenamentu di dati stabile, hè ancu usatu fdatasync(). L' codice RocksDB hà alcuni cumenti interessanti nantu à questu sughjettu. Per esempiu, s'assumiglia à a chjama sync_file_range() quandu si usa ZFS ùn sguassate micca e dati à u discu. L'esperienza mi dice chì u codice raramente utilizatu pò cuntene bug. Per quessa, avissi da cunsigliu di utilizà sta chjama di u sistema, salvu micca assolutamente necessariu.

Chjama di sistema per aiutà à assicurà a persistenza di dati

Sò ghjuntu à a cunclusione chì ci sò trè approcci chì ponu esse aduprati per eseguisce operazioni I / O persistenti. Tutti necessitanu una chjama di funzione fsync() per u cartulare induve u schedariu hè statu creatu. Quessi sò l'approcciu:

  1. Chjama di funzione fdatasync() o fsync() dopu a funzione write() (megliu aduprà fdatasync()).
  2. U travagliu cù un descriptore di fugliale apertu cù una bandiera O_DSYNC o O_SYNC (megliu - cù una bandiera O_DSYNC).
  3. Cumandamenti usu pwritev2() cù bandiera RWF_DSYNC o RWF_SYNC (preferibilmente cù una bandiera RWF_DSYNC).

Note di prestazione

Ùn aghju micca misuratu currettamente u rendiment di i varii miccanismi chì aghju investigatu. I differenzi chì aghju nutatu in a rapidità di u so travagliu sò assai chjuchi. Questu significa chì possu esse sbagliatu, è chì in altre cundizioni a listessa cosa pò vede risultati diffirenti. Prima, parleraghju di ciò chì affetta più u rendiment, è dopu, di ciò chì affetta u rendiment menu.

  1. A sovrascrittura di dati di u schedariu hè più veloce di l'appensione di dati à un schedariu (u guadagnu di rendiment pò esse 2-100%). Attaccà dati à un schedariu richiede cambiamenti supplementari à i metadati di u schedariu, ancu dopu a chjama di u sistema fallocate(), ma a magnitudine di stu effettu pò varià. Aghju cunsigliatu, per u megliu rendimentu, di chjamà fallocate() per pre-assignà u spaziu necessariu. Allora stu spaziu deve esse cumpletu esplicitamente cù zeri è chjamatu fsync(). Questu pruvucarà chì i blocchi currispundenti in u sistema di fugliale per esse marcati cum'è "allocati" invece di "non assignati". Questu dà una piccula (circa 2%) miglioramentu di u rendiment. Inoltre, certi dischi ponu avè una prima operazione d'accessu di bloccu più lenta chè l'altri. Questu significa chì riempie u spaziu cù zeri pò purtà à un significativu (circa 100%) migliuramentu di rendiment. In particulare, questu pò accade cù i dischi. AWS EBS (questu sò dati micca ufficiali, ùn li pudia cunfirmà). U stessu passa per u almacenamentu. Discu Persistente GCP (è questu hè digià infurmazione ufficiale, cunfirmata da e teste). Altri esperti anu fattu u listessu osservazionein relazione à i diversi dischi.
  2. U menu chjama di u sistema, u più altu u rendiment (u guadagnu pò esse circa 5%). Sembra una chjama open() cù bandiera O_DSYNC o chjamate pwritev2() cù bandiera RWF_SYNC chjama più veloce fdatasync(). Sospettate chì u puntu quì hè chì cù questu approcciu, u fattu chì menu chjama di u sistema deve esse realizatu per risolve u listessu compitu (una chjama invece di dui) ghjoca un rolu. Ma a diferenza di rendiment hè assai chjuca, cusì pudete facilmente ignurà è aduprà qualcosa in l'applicazione chì ùn porta micca à a cumplicazione di a so logica.

Sè site interessatu in u tema di l'almacenamiento di dati sustinibili, eccu alcuni materiali utili:

  • I/O metudi d'accessu - una panoramica di i principii di i meccanismi di input / output.
  • Assicurendu chì i dati ghjunghjenu à u discu - una storia di ciò chì succede à i dati nantu à a strada da l'applicazione à u discu.
  • Quandu duvete fsync u cartulare chì cuntene - a risposta à a quistione di quandu applicà fsync() per i repertorii. In poche parole, risulta chì avete bisognu di fà questu quandu crea un novu schedariu, è u mutivu di sta ricunniscenza hè chì in Linux pò esse parechje referenze à u stessu schedariu.
  • SQL Server in Linux: FUA Internals - quì hè una descrizzione di cumu l'almacenamiento di dati persistenti hè implementatu in SQL Server nantu à a piattaforma Linux. Ci hè parechje paraguni interessanti trà e chjama di u sistema Windows è Linux quì. Sò quasi sicuru chì era grazia à questu materiale chì aghju amparatu nantu à l'ottimisazione FUA di XFS.

Avete mai persu dati chì pensate chì sò stati guardati in modu sicuru in u discu?

Storage Durable Data è API di File Linux

Storage Durable Data è API di File Linux

Source: www.habr.com