Storio Data Gwydn ac APIs Ffeil Linux

Penderfynais, wrth ymchwilio i sefydlogrwydd storio data mewn systemau cwmwl, brofi fy hun, i wneud yn siŵr fy mod yn deall y pethau sylfaenol. i wedi dechrau trwy ddarllen y fanyleb NVMe er mwyn deall pa warantau ynghylch dyfalbarhad data (hynny yw, yn gwarantu y bydd data ar gael ar ôl methiant system) rhowch ddisgiau NMVe i ni. Gwneuthum y prif gasgliadau canlynol: mae angen ichi ystyried y data a ddifrodwyd o'r eiliad y rhoddir y gorchymyn ysgrifennu data, a hyd at yr eiliad y cânt eu hysgrifennu i'r cyfrwng storio. Fodd bynnag, yn y rhan fwyaf o raglenni, defnyddir galwadau system yn eithaf diogel i ysgrifennu data.

Yn yr erthygl hon, rwy'n archwilio'r mecanweithiau dyfalbarhad a ddarperir gan yr API ffeil Linux. Mae'n ymddangos y dylai popeth fod yn syml yma: mae'r rhaglen yn galw'r gorchymyn write(), ac ar ôl i weithrediad y gorchymyn hwn gael ei gwblhau, bydd y data'n cael ei storio'n ddiogel ar ddisg. Ond write() dim ond copi data cais i'r storfa cnewyllyn lleoli yn RAM. Er mwyn gorfodi'r system i ysgrifennu data i ddisg, rhaid defnyddio rhai mecanweithiau ychwanegol.

Storio Data Gwydn ac APIs Ffeil Linux

Yn gyffredinol, mae'r deunydd hwn yn set o nodiadau sy'n ymwneud â'r hyn yr wyf wedi'i ddysgu ar bwnc sydd o ddiddordeb i mi. Os byddwn yn siarad yn fyr iawn am y pwysicaf, mae'n ymddangos bod angen i chi ddefnyddio'r gorchymyn er mwyn trefnu storio data cynaliadwy. fdatasync() neu agor ffeiliau gyda baner O_DSYNC. Os oes gennych ddiddordeb mewn dysgu mwy am yr hyn sy'n digwydd i ddata ar y ffordd o god i ddisg, cymerwch gip arno hyn erthygl.

Nodweddion defnyddio'r ffwythiant ysgrifennu().

Galwad system write() a ddiffinnir yn y safon IEEE POSIX fel ymgais i ysgrifennu data i ddisgrifydd ffeil. Ar ôl cwblhau'r gwaith yn llwyddiannus write() rhaid i weithrediadau darllen data ddychwelyd yn union y beit a ysgrifennwyd yn flaenorol, gan wneud hynny hyd yn oed os yw'r data'n cael ei gyrchu o brosesau neu edafedd eraill (yma adran gyfatebol o safon POSIX). Yma, yn yr adran ar ryngweithio edafedd â gweithrediadau ffeil arferol, mae nodyn sy'n dweud, os yw dwy edefyn yr un yn galw'r swyddogaethau hyn, yna rhaid i bob galwad naill ai weld yr holl ganlyniadau a nodir y mae gweithredu'r alwad arall yn arwain atynt, neu ddim yn gweld dim canlyniadau o gwbl. Mae hyn yn arwain at y casgliad bod yn rhaid i holl weithrediadau ffeil I/O ddal clo ar yr adnodd y gweithir arno.

A yw hyn yn golygu bod y llawdriniaeth write() yn atomig? O safbwynt technegol, ie. Rhaid i weithrediadau darllen data ddychwelyd naill ai'r cyfan neu ddim o'r hyn a ysgrifennwyd ag ef write(). Ond y llawdriniaeth write(), yn unol â'r safon, nid oes rhaid iddo ddod i ben, ar ôl ysgrifennu popeth y gofynnwyd iddi ei ysgrifennu. Caniateir ysgrifennu rhan yn unig o'r data. Er enghraifft, efallai y bydd gennym ddwy ffrwd yr un yn atodi 1024 beit i ffeil a ddisgrifir gan yr un disgrifydd ffeil. O safbwynt y safon, bydd y canlyniad yn dderbyniol pan fydd pob un o'r gweithrediadau ysgrifennu yn gallu atodi un beit yn unig i'r ffeil. Bydd y gweithrediadau hyn yn aros yn atomig, ond ar ôl iddynt gwblhau, bydd y data y byddant yn ei ysgrifennu at y ffeil yn cael ei gymysgu. Yma trafodaeth ddiddorol iawn ar y pwnc hwn ar Stack Overflow.

swyddogaethau fsync() a fdatasync().

Y ffordd hawsaf i fflysio data i ddisg yw galw'r swyddogaeth fsync(). Mae'r swyddogaeth hon yn gofyn i'r system weithredu symud yr holl flociau wedi'u haddasu o'r storfa i ddisg. Mae hyn yn cynnwys holl fetadata'r ffeil (amser mynediad, amser addasu ffeil, ac ati). Rwy'n credu mai anaml y mae angen y metadata hwn, felly os ydych chi'n gwybod nad yw'n bwysig i chi, gallwch chi ddefnyddio'r swyddogaeth fdatasync(). Yn help ar fdatasync() mae'n dweud, yn ystod gweithrediad y swyddogaeth hon, bod cymaint o fetadata yn cael ei arbed i ddisg, sy'n "angenrheidiol ar gyfer cyflawni'r gweithrediadau darllen data canlynol yn gywir." A dyma'n union beth sy'n bwysig i'r mwyafrif o gymwysiadau.

Un broblem a all godi yma yw nad yw'r mecanweithiau hyn yn gwarantu y gellir dod o hyd i'r ffeil ar ôl methiant posibl. Yn benodol, pan fydd ffeil newydd yn cael ei chreu, dylai un alw fsync() ar gyfer y cyfeiriadur sy'n ei gynnwys. Fel arall, ar ôl damwain, mae'n bosibl nad yw'r ffeil hon yn bodoli. Y rheswm am hyn yw y gall ffeil fodoli mewn cyfeirlyfrau lluosog o dan UNIX, oherwydd y defnydd o ddolenni caled. Felly, wrth alw fsync() nid oes unrhyw ffordd i ffeil wybod pa ddata cyfeiriadur y dylid ei fflysio i ddisg hefyd (yma gallwch ddarllen mwy am hyn). Mae'n edrych fel bod y system ffeiliau ext4 yn gallu gwneud hynny yn awtomatig gwneud cais fsync() i gyfeiriaduron sy'n cynnwys y ffeiliau cyfatebol, ond efallai nad yw hyn yn wir gyda systemau ffeiliau eraill.

Gellir gweithredu'r mecanwaith hwn yn wahanol mewn systemau ffeiliau gwahanol. Defnyddiais i blktrace i ddysgu pa weithrediadau disg a ddefnyddir mewn systemau ffeiliau ext4 a XFS. Mae'r ddau yn cyhoeddi'r gorchmynion ysgrifennu arferol i ddisg ar gyfer cynnwys y ffeiliau a dyddlyfr y system ffeiliau, fflysio'r storfa ac ymadael trwy berfformio FUA (Force Unit Access, ysgrifennu data yn uniongyrchol i ddisg, osgoi'r storfa) ysgrifennu at y dyddlyfr. Mae'n debyg eu bod yn gwneud hynny'n union er mwyn cadarnhau ffaith y trafodiad. Ar yriannau nad ydynt yn cynnal FUA, mae hyn yn achosi dwy fflysio storfa. Mae fy arbrofion wedi dangos hynny fdatasync() ychydig yn gyflymach fsync(). Cyfleustodau blktrace yn nodi hynny fdatasync() fel arfer yn ysgrifennu llai o ddata i ddisg (yn ext4 fsync() yn ysgrifenu 20 KiB, a fdatasync() - 16 KiB). Hefyd, darganfyddais fod XFS ychydig yn gyflymach nag ext4. Ac yma gyda chymorth blktrace roedd yn gallu darganfod hynny fdatasync() yn fflysio llai o ddata i ddisg (4 KiB yn XFS).

Sefyllfaoedd amwys wrth ddefnyddio fsync()

Gallaf feddwl am dair sefyllfa amwys fsync()yr wyf wedi dod ar eu traws yn ymarferol.

Digwyddodd y digwyddiad cyntaf o'r fath yn 2008. Bryd hynny, roedd rhyngwyneb Firefox 3 wedi “rhewi” os oedd nifer fawr o ffeiliau'n cael eu hysgrifennu ar ddisg. Y broblem oedd bod gweithrediad y rhyngwyneb yn defnyddio cronfa ddata SQLite i storio gwybodaeth am ei gyflwr. Ar ôl pob newid a ddigwyddodd yn y rhyngwyneb, galwyd y swyddogaeth fsync(), a roddodd warantau da o storio data sefydlog. Yn y system ffeiliau ext3 a ddefnyddir bryd hynny, y swyddogaeth fsync() fflysio i ddisg yr holl dudalennau "budr" yn y system, ac nid dim ond y rhai a oedd yn gysylltiedig â'r ffeil cyfatebol. Roedd hyn yn golygu y gallai clicio botwm yn Firefox achosi i megabeit o ddata gael ei ysgrifennu i ddisg magnetig, a allai gymryd llawer o eiliadau. Yr ateb i'r broblem, cyn belled ag y deallais o hyn deunydd, oedd symud y gwaith gyda'r gronfa ddata i dasgau cefndir anghydamserol. Mae hyn yn golygu bod Firefox yn arfer gweithredu gofynion dyfalbarhad storio llymach nag oedd yn wirioneddol angenrheidiol, a dim ond gwaethygu'r broblem hon a wnaeth nodweddion y system ffeiliau ext3.

Digwyddodd yr ail broblem yn 2009. Yna, ar ôl damwain system, canfu defnyddwyr y system ffeiliau ext4 newydd fod llawer o ffeiliau newydd eu creu o hyd sero, ond ni ddigwyddodd hyn gyda'r system ffeiliau ext3 hŷn. Yn y paragraff blaenorol, soniais am sut y gwnaeth ext3 ddympio gormod o ddata ar y ddisg, a oedd yn arafu pethau'n fawr. fsync(). Er mwyn gwella'r sefyllfa, mae ext4 yn fflysio dim ond y tudalennau "budr" hynny sy'n berthnasol i ffeil benodol. Ac mae data ffeiliau eraill yn aros yn y cof am amser llawer hirach na gydag est3. Gwnaethpwyd hyn i wella perfformiad (yn ddiofyn, mae'r data yn aros yn y cyflwr hwn am 30 eiliad, gallwch chi ffurfweddu hyn gan ddefnyddio budr_expire_centisecs; yma gallwch ddod o hyd i ragor o wybodaeth am hyn). Mae hyn yn golygu y gellir colli llawer iawn o ddata yn anadferadwy ar ôl damwain. Yr ateb i'r broblem hon yw defnyddio fsync() mewn cymwysiadau sydd angen darparu storfa ddata sefydlog a'u hamddiffyn cymaint â phosibl rhag canlyniadau methiannau. Swyddogaeth fsync() yn gweithio'n llawer mwy effeithlon gydag est4 nag ag est3. Anfantais y dull hwn yw bod ei ddefnydd, fel o'r blaen, yn arafu rhai gweithrediadau, megis gosod rhaglenni. Gweler y manylion am hyn yma и yma.

Y drydedd broblem o ran fsync(), yn tarddu yn 2018. Yna, o fewn fframwaith y prosiect PostgreSQL, darganfuwyd bod y swyddogaeth fsync() dod ar draws gwall, mae'n nodi tudalennau "budr" fel "glân". O ganlyniad, mae'r galwadau canlynol fsync() gwneud dim gyda thudalennau o'r fath. Oherwydd hyn, mae tudalennau wedi'u haddasu yn cael eu storio yn y cof a byth yn cael eu hysgrifennu ar ddisg. Mae hwn yn drychineb go iawn, oherwydd bydd y cais yn meddwl bod rhywfaint o ddata wedi'i ysgrifennu ar ddisg, ond mewn gwirionedd ni fydd. Methiannau o'r fath fsync() yn brin, ni all y cais mewn sefyllfaoedd o'r fath wneud bron dim i frwydro yn erbyn y broblem. Y dyddiau hyn, pan fydd hyn yn digwydd, mae PostgreSQL a chymwysiadau eraill yn chwalu. Yma, yn yr erthygl "A all Ceisiadau Adfer o Fethiannau fsync?", Archwilir y broblem hon yn fanwl. Ar hyn o bryd yr ateb gorau i'r broblem hon yw defnyddio Direct I/O gyda'r faner O_SYNC neu gyda baner O_DSYNC. Gyda'r dull hwn, bydd y system yn adrodd am wallau a all ddigwydd wrth berfformio gweithrediadau ysgrifennu data penodol, ond mae'r dull hwn yn ei gwneud yn ofynnol i'r cais reoli'r byfferau ei hun. Darllenwch fwy amdano yma и yma.

Agor ffeiliau gan ddefnyddio'r baneri O_SYNC ac O_DSYNC

Gadewch i ni ddychwelyd at y drafodaeth ar y mecanweithiau Linux sy'n darparu storio data parhaus. Sef, yr ydym yn sôn am y defnydd o'r faner O_SYNC neu faner O_DSYNC wrth agor ffeiliau gan ddefnyddio system call agored (). Gyda'r dull hwn, mae pob gweithrediad ysgrifennu data yn cael ei berfformio fel pe bai ar ôl pob gorchymyn write() rhoddir gorchmynion i'r system, yn y drefn honno fsync() и fdatasync(). Yn Manylebau POSIX gelwir hyn yn "Cwblhau Uniondeb Ffeil I/O Cydamserol" a "Cwblhau Uniondeb Data". Prif fantais y dull hwn yw mai dim ond un alwad system sydd angen ei gweithredu er mwyn sicrhau cywirdeb data, ac nid dau (er enghraifft − write() и fdatasync()). Prif anfantais y dull hwn yw y bydd yr holl weithrediadau ysgrifennu gan ddefnyddio'r disgrifydd ffeil cyfatebol yn cael eu cydamseru, a all gyfyngu ar y gallu i strwythuro cod y cais.

Defnyddio I/O Uniongyrchol gyda'r faner O_DIRECT

Galwad system open() yn cefnogi'r faner O_DIRECT, sydd wedi'i gynllunio i osgoi storfa'r system weithredu, perfformio gweithrediadau I / O, gan ryngweithio'n uniongyrchol â'r ddisg. Mae hyn, mewn llawer o achosion, yn golygu y bydd y gorchmynion ysgrifennu a gyhoeddir gan y rhaglen yn cael eu trosi'n uniongyrchol i orchmynion sydd wedi'u hanelu at weithio gyda'r ddisg. Ond, yn gyffredinol, nid yw'r mecanwaith hwn yn disodli'r swyddogaethau fsync() neu fdatasync(). Y ffaith yw y gall y ddisg ei hun oedi neu cache gorchmynion priodol ar gyfer ysgrifennu data. Ac, hyd yn oed yn waeth, mewn rhai achosion arbennig, perfformiodd y gweithrediadau I / O wrth ddefnyddio'r faner O_DIRECT, darlledu i mewn i weithrediadau byffer traddodiadol. Y ffordd hawsaf o ddatrys y broblem hon yw defnyddio'r faner i agor ffeiliau O_DSYNC, a fydd yn golygu y bydd galwad yn dilyn pob gweithrediad ysgrifennu fdatasync().

Daeth i'r amlwg bod system ffeiliau XFS wedi ychwanegu "llwybr cyflym" yn ddiweddar ar gyfer O_DIRECT|O_DSYNC- cofnodion data. Os caiff y bloc ei drosysgrifo gan ddefnyddio O_DIRECT|O_DSYNC, yna bydd XFS, yn lle fflysio'r storfa, yn gweithredu'r gorchymyn ysgrifennu FUA os yw'r ddyfais yn ei gefnogi. Fe wnes i wirio hyn gan ddefnyddio'r cyfleustodau blktrace ar system Linux 5.4/Ubuntu 20.04. Dylai'r dull hwn fod yn fwy effeithlon, gan ei fod yn ysgrifennu'r lleiafswm o ddata ar ddisg ac yn defnyddio un gweithrediad, nid dau (ysgrifennu a fflysio'r storfa). Fe wnes i ddod o hyd i ddolen i clwt cnewyllyn 2018 sy'n gweithredu'r mecanwaith hwn. Mae rhywfaint o drafodaeth ynghylch cymhwyso'r optimeiddio hwn i systemau ffeiliau eraill, ond hyd y gwn, XFS yw'r unig system ffeiliau sy'n ei gefnogi hyd yn hyn.

swyddogaeth sync_file_range().

Mae gan Linux alwad system cysoni_ffeil_ystod(), sy'n eich galluogi i fflysio rhan o'r ffeil i ddisg yn unig, nid y ffeil gyfan. Mae'r alwad hon yn cychwyn fflysio asyncronaidd ac nid yw'n aros iddo gael ei gwblhau. Ond yn y cyfeiriad at sync_file_range() dywedir bod y gorchymyn hwn yn "beryglus iawn". Ni argymhellir ei ddefnyddio. Nodweddion a pheryglon sync_file_range() wedi'i ddisgrifio'n dda iawn yn hyn deunydd. Yn benodol, mae'n ymddangos bod yr alwad hon yn defnyddio RocksDB i reoli pan fydd y cnewyllyn yn fflysio data "budr" i ddisg. Ond ar yr un pryd yno, er mwyn sicrhau storio data sefydlog, fe'i defnyddir hefyd fdatasync(). Yn côd Mae gan RocksDB rai sylwadau diddorol ar y pwnc hwn. Er enghraifft, mae'n edrych fel yr alwad sync_file_range() wrth ddefnyddio ZFS nid yw'n fflysio data i ddisg. Mae profiad yn dweud wrthyf y gall cod a ddefnyddir yn anaml gynnwys bygiau. Felly, byddwn yn cynghori peidio â defnyddio'r alwad system hon oni bai bod hynny'n gwbl angenrheidiol.

Galwadau system i helpu i sicrhau dyfalbarhad data

Rwyf wedi dod i'r casgliad bod tri dull gweithredu y gellir eu defnyddio i gyflawni gweithrediadau I/O parhaus. Mae angen galwad ffwythiant ar bob un ohonynt fsync() ar gyfer y cyfeiriadur lle crëwyd y ffeil. Dyma'r dulliau gweithredu:

  1. Galwad swyddogaeth fdatasync() neu fsync() ar ôl swyddogaeth write() (gwell i'w ddefnyddio fdatasync()).
  2. Gweithio gyda disgrifydd ffeil wedi'i agor gyda baner O_DSYNC neu O_SYNC (gwell - gyda baner O_DSYNC).
  3. Defnydd gorchymyn pwritev2() gyda baner RWF_DSYNC neu RWF_SYNC (yn ddelfrydol gyda baner RWF_DSYNC).

Nodiadau Perfformiad

Ni wnes i fesur perfformiad y gwahanol fecanweithiau yr ymchwiliais iddynt yn ofalus. Mae'r gwahaniaethau a sylwais yng nghyflymder eu gwaith yn fach iawn. Mae hyn yn golygu y gallaf fod yn anghywir, ac mewn amodau eraill gall yr un peth ddangos canlyniadau gwahanol. Yn gyntaf, byddaf yn siarad am yr hyn sy'n effeithio'n fwy ar berfformiad, ac yna, am yr hyn sy'n effeithio llai ar berfformiad.

  1. Mae trosysgrifo data ffeil yn gyflymach nag atodi data i ffeil (gall y cynnydd perfformiad fod yn 2-100%). Mae atodi data i ffeil yn gofyn am newidiadau ychwanegol i fetadata'r ffeil, hyd yn oed ar ôl galwad y system fallocate(), ond gall maint yr effaith hon amrywio. Rwy'n argymell, ar gyfer perfformiad gorau, i alw fallocate() i neilltuo'r lle gofynnol ymlaen llaw. Yna rhaid llenwi'r gofod hwn yn benodol â sero a'i alw fsync(). Bydd hyn yn achosi i'r blociau cyfatebol yn y system ffeiliau gael eu marcio fel "dyrannwyd" yn lle "heb ei ddyrannu". Mae hyn yn rhoi gwelliant perfformiad bach (tua 2%). Yn ogystal, efallai y bydd gan rai disgiau weithrediad mynediad bloc cyntaf arafach nag eraill. Mae hyn yn golygu y gall llenwi'r gofod gyda sero arwain at welliant perfformiad sylweddol (tua 100%). Yn benodol, gall hyn ddigwydd gyda disgiau. AWS EBS (data answyddogol yw hwn, ni allwn eu cadarnhau). Mae'r un peth yn wir am storio. Disg Parhaus GCP (ac mae hon eisoes yn wybodaeth swyddogol, wedi'i chadarnhau gan brofion). Mae arbenigwyr eraill wedi gwneud yr un peth arsylwigysylltiedig â disgiau gwahanol.
  2. Po leiaf o alwadau system, yr uchaf yw'r perfformiad (gall y cynnydd fod tua 5%). Mae'n edrych fel galwad open() gyda baner O_DSYNC neu ffoniwch pwritev2() gyda baner RWF_SYNC galwad cyflymach fdatasync(). Rwy'n amau ​​​​mai'r pwynt yma yw, gyda'r dull hwn, bod y ffaith bod yn rhaid cyflawni llai o alwadau system i ddatrys yr un dasg (un alwad yn lle dwy) yn chwarae rhan. Ond mae'r gwahaniaeth perfformiad yn fach iawn, felly gallwch chi ei anwybyddu'n hawdd a defnyddio rhywbeth yn y cais nad yw'n arwain at gymhlethdod ei resymeg.

Os oes gennych ddiddordeb yn y pwnc storio data cynaliadwy, dyma rai deunyddiau defnyddiol:

  • I/O Dulliau mynediad — trosolwg o hanfodion mecanweithiau mewnbwn / allbwn.
  • Sicrhau bod data yn cyrraedd disg - stori am yr hyn sy'n digwydd i'r data ar y ffordd o'r cymhwysiad i'r ddisg.
  • Pryd ddylech chi fsync y cyfeiriadur sy'n cynnwys - yr ateb i'r cwestiwn pryd i wneud cais fsync() ar gyfer cyfeiriaduron. Yn gryno, mae'n troi allan bod angen i chi wneud hyn wrth greu ffeil newydd, a'r rheswm dros yr argymhelliad hwn yw y gall fod llawer o gyfeiriadau at yr un ffeil yn Linux.
  • Gweinydd SQL ar Linux: FUA Internals - dyma ddisgrifiad o sut mae storio data parhaus yn cael ei weithredu yn SQL Server ar y platfform Linux. Mae rhai cymariaethau diddorol rhwng galwadau system Windows a Linux yma. Rwyf bron yn siŵr mai diolch i'r deunydd hwn y dysgais am optimeiddio FUA XFS.

Ydych chi erioed wedi colli data yr oeddech yn meddwl ei fod wedi'i storio'n ddiogel ar ddisg?

Storio Data Gwydn ac APIs Ffeil Linux

Storio Data Gwydn ac APIs Ffeil Linux

Ffynhonnell: hab.com