Datuen biltegiratze iraunkorra eta Linux fitxategien APIak

Nik, hodeiko sistemetan datuak biltegiratzeko egonkortasuna ikertzen, neure burua probatzea erabaki nuen, oinarrizko gauzak ulertzen ditudala ziurtatzeko. I NVMe zehaztapenak irakurriz hasi datuen iraunkortasunari buruzko zer bermek (hau da, sistemaren hutsegite baten ondoren datuak eskuragarri egongo direla bermatzen duten) NMVe diskoak ematen dizkigun ulertzeko. Ondorio nagusi hauek atera ditut: datuak idazteko komandoa ematen den unetik, eta biltegiratze euskarrian idazten diren arte, kaltetutako datuak kontuan hartu behar dituzu. Hala ere, programa gehienetan, sistema-deiak nahiko seguru erabiltzen dira datuak idazteko.

Artikulu honetan, Linux fitxategien APIek eskaintzen dituzten iraunkortasun mekanismoak aztertzen ditut. Badirudi hemen dena sinplea izan behar dela: programak komandoa deitzen du write(), eta komando honen funtzionamendua amaitu ondoren, datuak modu seguruan gordeko dira diskoan. Baina write() RAM-n dagoen nukleoaren cachean soilik kopiatzen ditu aplikazioaren datuak. Sistema datuak diskoan idaztera behartzeko, mekanismo osagarri batzuk erabili behar dira.

Datuen biltegiratze iraunkorra eta Linux fitxategien APIak

Oro har, material hau nire intereseko gai bati buruz ikasi dudanari buruzko ohar multzo bat da. Garrantzitsuenei buruz laburki hitz egiten badugu, datuen biltegiratze iraunkorra antolatzeko komandoa erabili behar duzu. fdatasync() edo ireki fitxategiak banderarekin O_DSYNC. Kodetik diskorako bidean datuekin gertatzen denari buruz gehiago jakiteko interesa baduzu, begiratu hau Artikulu.

write() funtzioa erabiltzeko ezaugarriak

Sistema-deia write() estandarrean zehaztuta IEEE POSIX fitxategi deskribatzaile batean datuak idazteko saiakera gisa. Lanak arrakastaz amaitu ondoren write() datuak irakurtzeko eragiketek aldez aurretik idatzitako byteak itzuli behar dituzte, nahiz eta datuak beste prozesu edo hari batzuetatik sartzen diren (Hemen POSIX estandarraren dagokion atala). Hemen, hariak fitxategien eragiketa arruntekin elkarrekintzari buruzko atalean, ohar bat dago esaten duena: bi hari bakoitzak funtzio horiei deitzen badie, orduan dei bakoitzak beste deiaren exekuzioak dakarren adierazitako ondorio guztiak ikusi behar ditu, edo ez ikusi batere ondoriorik. Honek ondorioztatzen du fitxategien I/O eragiketa guztiek blokeo bat eduki behar dutela lantzen ari den baliabidean.

Horrek esan nahi du operazioa dela write() atomikoa da? Ikuspuntu teknikotik, bai. Datuak irakurtzeko eragiketak idatzitako guztia edo bat ere ez itzuli behar du write(). Baina operazioa write(), arauaren arabera, ez du amaitu beharrik, idazteko eskatu zioten guztia idatzita duela. Datuen zati bat bakarrik idaztea onartzen da. Adibidez, bi korronte izan ditzakegu bakoitzak 1024 byte erantsiz fitxategi-deskriptore berak deskribatutako fitxategi bati. Estandarren ikuspuntutik, emaitza onargarria izango da idazketa-eragiketa bakoitzak fitxategiari byte bakarra eransten dionean. Eragiketa hauek atomikoak izaten jarraituko dute, baina amaitu ondoren, fitxategian idazten dituzten datuak nahastu egingo dira. Hemen gai honi buruzko eztabaida oso interesgarria Stack Overflow-en.

fsync() eta fdatasync() funtzioak

Datuak diskora garbitzeko modurik errazena funtzioari deitzea da fsync(). Funtzio honek sistema eragileari eskatzen dio aldatutako bloke guztiak cachetik diskora eramateko. Honek fitxategiaren metadatu guztiak biltzen ditu (sarbide-denbora, fitxategia aldatzeko denbora, eta abar). Metadatu hauek oso gutxitan beharrezkoak direla uste dut, beraz, zuretzat garrantzitsua ez dela badakizu, funtzioa erabil dezakezu fdatasync(). Urtean lagundu on fdatasync() funtzio honen funtzionamenduan zehar, metadatu kopuru hori diskoan gordetzen dela dio, eta hori "beharrezkoa da ondorengo datuak irakurtzeko eragiketa zuzen egiteko". Eta horixe da aplikazio gehienei axola zaiona.

Hemen sor daitekeen arazo bat da mekanismo hauek ez dutela bermatzen fitxategia balizko hutsegite baten ondoren aurki daitekeenik. Bereziki, fitxategi berri bat sortzen denean, deitu behar da fsync() daukan direktoriorako. Bestela, hutsegite baten ondoren, baliteke fitxategi hau existitzen ez dela. Honen arrazoia UNIXen arabera, esteka gogorren erabilera dela eta, fitxategi bat hainbat direktoriotan egon daitekeela da. Hori dela eta, deitzerakoan fsync() ez dago modurik fitxategi batek jakitea zein direktorioko datuak diskora hustu behar diren ere (Hemen honi buruz gehiago irakur dezakezu). Badirudi ext4 fitxategi sistemak egiteko gai dela automatikoki aplikatzeko fsync() dagozkion fitxategiak dituzten direktorioetara, baina baliteke hori ez izatea beste fitxategi-sistemekin.

Mekanismo hau modu ezberdinean inplementa daiteke fitxategi-sistema desberdinetan. erabili nuen blktraza ext4 eta XFS fitxategi-sistemetan zer disko-eragiketa erabiltzen diren jakiteko. Biek diskora ohiko idazteko komandoak igortzen dituzte, bai fitxategien edukietarako bai fitxategi-sistemako aldizkarirako, cachea garbitu eta irteten dira aldizkarian idazteko FUA (Force Unit Access, datuak zuzenean diskoan idaztea, cachea saihestuz). Seguruenik hori egiten dute transakzioaren errealitatea berresteko. FUA onartzen ez duten unitateetan, honek bi cache hustu egiten ditu. Nire esperimentuek hori erakutsi dute fdatasync() pixka bat azkarrago fsync(). Erabilgarritasuna blktrace hori adierazten du fdatasync() normalean datu gutxiago idazten ditu diskoan (ext4 fsync() 20 KiB idazten du, eta fdatasync() - 16 KiB). Gainera, jakin nuen XFS ext4 baino apur bat azkarragoa dela. Eta hemen laguntzarekin blktrace hori jakitea lortu zuen fdatasync() datu gutxiago garbitzen ditu diskora (4 KiB XFSn).

Egoera anbiguoak fsync() erabiltzean

Hiru egoera anbiguo bururatzen zaizkit fsync()praktikan topatu dudana.

Horrelako lehen gertakaria 2008an gertatu zen. Garai hartan, Firefox 3 interfazea "izoztu" zen fitxategi kopuru handi bat diskoan idazten bazen. Arazoa izan zen interfazearen ezarpenak SQLite datu-base bat erabiltzen zuela bere egoerari buruzko informazioa gordetzeko. Interfazean gertatutako aldaketa bakoitzaren ondoren, funtzioari deitu zitzaion fsync(), datuen biltegiratze egonkorren berme onak ematen zituena. Orduan erabilitako ext3 fitxategi-sisteman, funtzioa fsync() sistemako orrialde "zikin" guztiak diskoan garbitu ditu, eta ez dagokien fitxategiarekin erlazionatuta zeudenak bakarrik. Horrek esan nahi zuen Firefox-eko botoi bat sakatzean megabyte datuak disko magnetiko batean idaztea eragin zezakeela, eta horrek segundo asko behar zituen. Arazoaren konponbidea, nik ulertu nuenetik da materiala, datu-basearekin egindako lana hondoko ataza asinkronoetara eramatea zen. Horrek esan nahi du Firefoxek benetan beharrezkoa zena baino biltegiratze iraunkortasun baldintza zorrotzagoak ezartzen zituela eta ext3 fitxategi-sistemaren funtzioek arazo hau areagotu baino ez zuten egiten.

Bigarren arazoa 2009an gertatu zen. Orduan, sistemaren hutsegite baten ondoren, ext4 fitxategi-sistema berriaren erabiltzaileek sortu berri diren fitxategi asko zero-luzera zirela ikusi zuten, baina hori ez zen ext3 fitxategi-sistema zaharragoarekin gertatu. Aurreko paragrafoan, ext3-k diskoan datu gehiegi botatzen dituenari buruz hitz egin nuen, eta horrek gauzak asko moteltzen zituen. fsync(). Egoera hobetzeko, ext4-k fitxategi jakin baterako garrantzitsuak diren orrialde "zikinak" baino ez ditu garbitzen. Eta beste fitxategi batzuen datuak ext3-rekin baino askoz denbora gehiagoz geratzen dira memorian. Errendimendua hobetzeko egin da (lehenespenez, datuak 30 segundoz egoten dira egoera honetan, hau erabiliz konfigura dezakezu dirty_expire_centisecs; Hemen honi buruzko informazio gehiago aurki dezakezu). Horrek esan nahi du hutsegite baten ondoren datu-kopuru handi bat berreskuratu gabe gal daitekeela. Arazo honen irtenbidea erabiltzea da fsync() Datu biltegiratze egonkorra eskaini eta hutsegiteen ondorioetatik ahalik eta gehien babestu behar duten aplikazioetan. Funtzioa fsync() ext4-rekin ext3-rekin baino askoz eraginkorragoa da. Ikuspegi honen desabantaila da bere erabilerak, lehen bezala, eragiketa batzuk moteltzen dituela, programak instalatzea adibidez. Ikusi honi buruzko xehetasunak Hemen ΠΈ Hemen.

Hirugarren arazoa fsync(), 2018an sortua. Ondoren, PostgreSQL proiektuaren esparruan, funtzioa bada fsync() errore bat aurkitzen du, orri "zikinak" "garbi" gisa markatzen ditu. Ondorioz, honako deialdi hauek fsync() ez egin ezer horrelako orrialdeekin. Horregatik, aldatutako orrialdeak memorian gordetzen dira eta inoiz ez dira diskoan idazten. Benetako hondamendia da, aplikazioak datu batzuk diskoan idazten direla pentsatuko duelako, baina egia esan ez da hala izango. Halako porrotak fsync() arraroak dira, horrelako egoeretan aplikazioak ezin du ia ezer egin arazoari aurre egiteko. Egun, hau gertatzen denean, PostgreSQL eta beste aplikazio batzuk huts egiten dute. Hemen, "Aplikazioak fsync hutsegiteetatik berreskuratu al daitezke?" artikuluan, arazo hau zehatz-mehatz aztertzen da. Gaur egun arazo honen irtenbiderik onena I/O zuzena banderarekin erabiltzea da O_SYNC edo bandera batekin O_DSYNC. Planteamendu honekin, sistemak datuen idazketa eragiketa zehatzak egitean gerta daitezkeen akatsen berri emango du, baina ikuspegi honek aplikazioak buffer-ak berak kudeatzea eskatzen du. Irakurri gehiago horri buruz Hemen ΠΈ Hemen.

Fitxategiak irekitzea O_SYNC eta O_DSYNC banderak erabiliz

Itzul gaitezen datuen biltegiratze iraunkorra eskaintzen duten Linux mekanismoen eztabaidara. Alegia, bandera erabiltzeaz ari gara O_SYNC edo bandera O_DSYNC fitxategiak sistema-deia erabiliz irekitzean ireki (). Planteamendu honekin, datuak idazteko eragiketa bakoitza komando bakoitzaren ondoren bezala egiten da write() sistemari aginduak ematen zaizkio, hurrenez hurren fsync() ΠΈ fdatasync(). Urtean POSIX zehaztapenak horri "Sinkronizatutako I/O fitxategiaren osotasuna osatzea" eta "Datuen osotasuna osatzea" deitzen zaio. Ikuspegi honen abantaila nagusia da datuen osotasuna bermatzeko sistema dei bakarra exekutatu behar dela, eta ez bi (adibidez - write() ΠΈ fdatasync()). Ikuspegi honen desabantaila nagusia da dagokion fitxategi deskribatzailea erabiliz idazteko eragiketa guztiak sinkronizatuko direla, eta horrek aplikazioaren kodea egituratzeko gaitasuna mugatu dezake.

Zuzeneko I/O erabiliz O_DIRECT banderarekin

Sistema-deia open() bandera onartzen du O_DIRECT, sistema eragilearen cachea saihesteko diseinatuta dagoena, I/O eragiketak egiteko, diskoarekin zuzenean elkarreragiten. Horrek, kasu askotan, esan nahi du programak emandako idazketa-aginduak zuzenean diskoarekin lan egiteko aginduetara itzuliko direla. Baina, oro har, mekanismo hau ez da funtzioen ordezkoa fsync() edo fdatasync(). Kontua da diskoak berak egin dezakeela atzerapena edo cachea dagozkion datuak idazteko aginduak. Eta, are okerrago, kasu berezi batzuetan, bandera erabiltzean egiten diren I/O eragiketak O_DIRECT, emankizuna Bufferen eragiketa tradizionaletan. Arazo hau konpontzeko modurik errazena bandera erabiltzea da fitxategiak irekitzeko O_DSYNC, hau da, idazketa eragiketa bakoitzari dei bat egingo zaiola esan nahi du fdatasync().

Agertu zen XFS fitxategi-sistemak "bide azkar" bat gehitu zuela duela gutxi O_DIRECT|O_DSYNC-datuen erregistroak. Blokea gainidazten bada erabiliz O_DIRECT|O_DSYNC, orduan XFS-k, cachea garbitu beharrean, FUA idazteko komandoa exekutatuko du gailuak onartzen badu. Utilitatea erabiliz egiaztatu dut hau blktrace Linux 5.4/Ubuntu 20.04 sisteman. Ikuspegi honek eraginkorragoa izan beharko luke, gutxieneko datu-kopurua diskoan idazten baitu eta eragiketa bat erabiltzen du, ez bi (cachea idatzi eta garbitu). Esteka bat aurkitu dut adabaki 2018ko nukleoa mekanismo hau ezartzen duena. Eztabaida dago optimizazio hau beste fitxategi-sistema batzuei aplikatzeari buruz, baina nik dakidanez, XFS da orain arte onartzen duen fitxategi-sistema bakarra.

sync_file_range() funtzioa

Linux sistema dei bat du sync_file_range(), fitxategiaren zati bat soilik diskora garbitzeko aukera ematen duena, ez fitxategi osoa. Dei honek hustuketa asinkrono bat abiarazten du eta ez du amaitu arte itxaron. Baina erreferentzian sync_file_range() agindu hau "oso arriskutsua" dela esaten da. Ez da gomendagarria erabiltzea. Ezaugarriak eta arriskuak sync_file_range() oso ondo deskribatuta hau materiala. Bereziki, badirudi dei honek RocksDB erabiltzen duela nukleoak diskora datu "zikinak" noiz garbitzen dituen kontrolatzeko. Baina, aldi berean, bertan, datu biltegiratze egonkorra bermatzeko, ere erabiltzen da fdatasync(). Urtean kodea RocksDB-k iruzkin interesgarri batzuk ditu gai honi buruz. Adibidez, deiaren itxura du sync_file_range() ZFS erabiltzean ez ditu datuak diskora garbitzen. Esperientziak esaten dit gutxitan erabiltzen den kodeak akatsak izan ditzakeela. Horregatik, sistema-dei hau ez erabiltzea gomendatuko nuke, guztiz beharrezkoa ez bada.

Sistema-deiak datuen iraunkortasuna ziurtatzen laguntzeko

I/O eragiketa iraunkorrak egiteko hiru planteamendu erabil daitezkeela ondorioztatu dut. Guztiek funtzio-dei bat behar dute fsync() fitxategia sortu den direktoriorako. Hauek dira planteamenduak:

  1. Funtzio-deia fdatasync() edo fsync() funtzioaren ondoren write() (erabiltzea hobe fdatasync()).
  2. Bandera batekin irekitako fitxategi deskribatzaile batekin lan egitea O_DSYNC edo O_SYNC (hobe - bandera batekin O_DSYNC).
  3. Komandoen erabilera pwritev2() banderarekin RWF_DSYNC edo RWF_SYNC (hobe bandera batekin RWF_DSYNC).

Errendimendu oharrak

Ez nuen arretaz neurtu ikertu ditudan mekanismo ezberdinen errendimendua. Haien lanaren abiaduran nabaritu ditudan aldeak oso txikiak dira. Horrek esan nahi du oker egon naitekeela, eta beste baldintza batzuetan gauza bera emaitza desberdinak ager daitezkeela. Lehenik eta behin, errendimenduari gehiago eragiten dionari buruz hitz egingo dut, eta gero, errendimenduari gutxiago eragiten dionari buruz.

  1. Fitxategien datuak gainidaztea azkarragoa da fitxategi bati datuak eranstea baino (errendimenduaren irabazia % 2-100 izan daiteke). Fitxategi bati datuak eransteko, fitxategiaren metadatuetan aldaketa gehigarriak behar dira, sistema-deiaren ondoren ere fallocate(), baina eragin horren magnitudea alda daiteke. Errendimendu onena lortzeko, deitzea gomendatzen dut fallocate() behar den espazioa aurrez esleitzeko. Orduan, espazio hori esplizituki zeroz bete eta deitu behar da fsync(). Honek fitxategi-sisteman dagozkien blokeak "esleitu" gisa markatuko ditu "esleitu gabe" ordez. Horrek errendimenduaren hobekuntza txiki bat (% 2 inguru) ematen du. Gainera, disko batzuek beste batzuek baino lehen bloke-sarbide-eragiketa motelagoa izan dezakete. Horrek esan nahi du espazioa zeroz betetzeak errendimenduaren hobekuntza nabarmena ekar dezakeela (% 100 inguru). Bereziki, hau diskoekin gerta daiteke. AWS EBS (datu ez-ofiziala da, ezin izan ditut baieztatu). Gauza bera gertatzen da biltegiratzearekin. GCP Disko iraunkorra (eta hori dagoeneko informazio ofiziala da, probek baieztatuta). Beste aditu batzuek gauza bera egin dute behaketadisko ezberdinekin erlazionatuta.
  2. Zenbat eta sistema dei gutxiago, orduan eta errendimendu handiagoa (irabazia %5 ingurukoa izan daiteke). Dei bat dirudi open() banderarekin O_DSYNC edo deitu pwritev2() banderarekin RWF_SYNC dei azkarragoa fdatasync(). Susmoa dut hemen kontua dela ikuspegi honekin, zeregin bera konpontzeko sistema-dei gutxiago egin behar izateak (dei bat bi beharrean) jokatzen duela. Baina errendimendu-aldea oso txikia da, beraz, erraz alde batera utzi dezakezu eta aplikazioan bere logikaren konplikazioa eragiten ez duen zerbait erabil dezakezu.

Datuen biltegiratze iraunkorraren gaia interesatzen bazaizu, hona hemen material erabilgarriak:

  • I/O Sarbide-metodoak β€” sarrera/irteera mekanismoen oinarrizko ikuspegia.
  • Datuak diskora iristen direla ziurtatzea - Aplikaziotik diskorako bidean datuekin gertatzen denari buruzko istorio bat.
  • Noiz sinkronizatu behar duzu eduki duen direktorioa - Eskaera noiz egin behar den galderari erantzuna fsync() direktorioetarako. Laburbilduz, fitxategi berri bat sortzean hori egin behar duzula ikusten da, eta gomendio honen arrazoia Linux-en fitxategi beraren erreferentzia asko egon daitezkeela da.
  • SQL Server Linux-en: FUA Internals - Hona hemen Linux plataformako SQL Server-en datuen biltegiratze iraunkorra nola inplementatzen den azaltzen den. Windows eta Linux sistema-deien arteko konparaketa interesgarri batzuk daude hemen. Ia ziur nago material honi esker XFS-ren FUA optimizazioari buruz ikasi nuela.

Inoiz galdu al duzu diskoan modu seguruan gordeta zegoela uste zenuen datuak?

Datuen biltegiratze iraunkorra eta Linux fitxategien APIak

Datuen biltegiratze iraunkorra eta Linux fitxategien APIak

Iturria: www.habr.com