Туруктуу маалыматтарды сактоо жана Linux File API'лери

Булут тутумдарында маалыматтарды сактоонун туруктуулугун изилдеп жатып, мен негизги нерселерди түшүнгөнүмдү текшерүү үчүн өзүмдү сынап көрүүнү чечтим. И NVMe спецификациясын окуу менен башталды Туруктуу маалыматтарды сактоого байланыштуу кандай кепилдиктерди түшүнүү үчүн (башкача айтканда, система бузулгандан кийин маалыматтар жеткиликтүү болот деген кепилдик) бизге NMVe дисктерин берет. Мен төмөнкүдөй негизги тыянактарды жасадым: маалымат жазууга буйрук берилген учурдан тартып ал сактагычка жазылган учурга чейин бузулган деп эсептелүүгө тийиш. Бирок, көпчүлүк программалар маалыматтарды жаздыруу үчүн системалык чалууларды колдонушат.

Бул постто мен Linux файл API'лери тарабынан берилген туруктуу сактоо механизмдерин изилдейм. Бул жерде баары жөнөкөй болушу керек окшойт: программа буйрукту чакырат write(), жана бул буйрук аяктагандан кийин, маалыматтар дискке коопсуз сакталат. Бирок write() колдонмонун маалыматтарын RAMда жайгашкан ядро ​​кэшине гана көчүрөт. Системаны дискке маалыматтарды жазууга мажбурлоо үчүн, сиз кээ бир кошумча механизмдерди колдонушуңуз керек.

Туруктуу маалыматтарды сактоо жана Linux File API'лери

Жалпысынан алганда, бул материал мени кызыктырган тема боюнча үйрөнгөн нерселерге байланыштуу эскертүүлөрдүн жыйындысы. Биз абдан маанилүү нерсе жөнүндө кыскача сөз кыла турган болсок, анда туруктуу маалыматтарды сактоону уюштуруу үчүн буйрукту колдонуу керек экен. fdatasync() же желек менен файлдарды ачыңыз O_DSYNC. Эгер сиз коддон дискке өтүү жолунда берилиштер эмне болору жөнүндө көбүрөөк билгиңиз келсе, анда карап көрүңүз бул макала.

write() функциясын колдонуунун өзгөчөлүктөрү

Системалык чалуу write() стандартта аныкталган IEEE POSIX файлдын дескрипторуна маалыматтарды жазуу аракети катары. ийгиликтүү аяктагандан кийин write() Берилиштерди окуу операциялары мурда жазылган байттарды так кайтарып бериши керек, бул маалыматка башка процесстерден же жиптерден кирсе дагы (бул жерде POSIX стандартынын тиешелүү бөлүмү). бул, жиптердин кадимки файл операциялары менен кандайча өз ара аракеттениши жөнүндө бөлүмдө, эгер эки жип ар бири бул функцияларды чакырса, анда ар бир чалуу башка чалуу үчүн белгиленген кесепеттерин же баарын көрүшү керек, же такыр бири да жок деп жазылган эскертүү бар. кесепеттери. Бул бардык файлдык I/O операциялары иштеп жаткан ресурста кулпуга ээ болушу керек деген тыянакка алып келет.

Бул операция дегенди билдиреби write() ал атомдукпу? Техникалык көз караштан алганда, ооба. Маалыматтарды окуу операциялары жазылгандардын баарын же эч нерсесин кайтарышы керек write(). Бирок операция write(), стандартка ылайык, сөзсүз эле жазууну суранган нерсенин баарын жазып бүтүрүү керек эмес. Ал маалыматтардын бир бөлүгүн гана жазууга уруксат берилет. Мисалы, бизде бир эле файл дескриптору менен сүрөттөлгөн файлга ар бири 1024 байт кошулган эки жип болушу мүмкүн. Стандарттын көз карашы боюнча, ар бир жазуу операциясы файлга бир гана байт кошо алганда алгылыктуу натыйжа болот. Бул операциялар атомдук бойдон кала берет, бирок алар аяктагандан кийин файлга жазган маалыматтар аралашып калат. бул жерде Stack Overflow боюнча бул тема боюнча абдан кызыктуу талкуу.

fsync() жана fdatasync() функциялары

Дискке маалыматтарды тазалоонун эң оңой жолу бул функцияны чакыруу fsync(). Бул функция операциялык тутумдан бардык өзгөртүлгөн блокторду кэштен дискке өткөрүп берүүнү суранат. Бул бардык файл метаберилиштерин камтыйт (кирүү убактысы, файлды өзгөртүү убактысы жана башкалар). Бул метадайындар сейрек керек деп эсептейм, андыктан ал сиз үчүн маанилүү эмес экенин билсеңиз, функцияны колдонсоңуз болот. fdatasync(). The жардам боюнча fdatasync() Бул функциянын иштеши учурунда дискке мындай көлөмдөгү метаберилиштер сакталат деп айтылат, ал "кийинки маалыматтарды окуу операцияларын туура аткаруу үчүн зарыл". Жана дал ушул көпчүлүк колдонмолорго кам көрөт.

Бул жерде келип чыгышы мүмкүн болгон бир көйгөй, бул механизмдер файлдын мүмкүн болгон катадан кийин табыла тургандыгына кепилдик бербейт. Тактап айтканда, жаңы файлды түзүп жатканда, чакыруу керек fsync() аны камтыган каталог үчүн. Болбосо, ката болгондон кийин, бул файл жок болуп чыгышы мүмкүн. Мунун себеби, UNIXте, катуу шилтемелерди колдонуудан улам, файл бир нече каталогдордо болушу мүмкүн. Ошондуктан, чалганда fsync() Файлдын кайсы каталог маалыматтары да дискке жуурулушу керектигин билүү үчүн эч кандай жол жок (бул жерде Бул тууралуу көбүрөөк окуй аласыз). Бул ext4 файл системасы жөндөмдүү окшойт жазуусу колдонулат fsync() тиешелүү файлдарды камтыган каталогдорго, бирок башка файл системаларында андай болбошу мүмкүн.

Бул механизм ар кандай файл системаларында ар кандай түрдө ишке ашырылышы мүмкүн. Мен колдондум blktrace ext4 жана XFS файл системаларында кандай диск операциялары колдонулаарын билүү үчүн. Экөө тең файлдын мазмуну жана файл тутумунун журналы үчүн дискке үзгүлтүксүз жазуу буйруктарын чыгарышат, кэшти тазалашат жана журналга FUA (бирдиктин кирүүсүнө мажбурлоо, маалыматтарды түз дискке жазуу, кэшти айланып өтүү) аткаруу менен чыгат. Алар муну транзакция болгонун тастыктоо үчүн жасашса керек. FUA колдобогон дисктерде бул эки кэшти тазалоого алып келет. Муну менин эксперименттерим көрсөттү fdatasync() бир аз тезирээк fsync(). Утилита blktrace экенин көрсөтүп турат fdatasync() адатта дискке азыраак маалымат жазат (ext4 fsync() 20 КБ жазат, жана fdatasync() - 16 КБ). Ошондой эле, мен XFS ext4 караганда бир аз ылдамыраак экенин билдим. Жана бул жерде жардамы менен blktrace деп билүүгө жетишкен fdatasync() дискке азыраак маалыматтарды жууйт (XFSде 4 КБ).

fsync() колдонууда пайда болгон түшүнүксүз жагдайлар

Мен байланыштуу үч түшүнүксүз жагдайды ойлой алат fsync()аны мен практикада жолуктурдум.

Мындай окуя биринчи жолу 2008-жылы болгон. Андан кийин дискке көп сандагы файлдар жазылган болсо, Firefox 3 интерфейси катып калган. Маселе интерфейсти ишке ашырууда анын абалы жөнүндө маалыматты сактоо үчүн SQLite маалымат базасы колдонулган. Интерфейсте болгон ар бир өзгөртүүдөн кийин функция чакырылган fsync(), бул туруктуу маалыматтарды сактоонун жакшы кепилдиктерин берди. Андан кийин колдонулган ext3 файл тутумунда функция fsync() Тиешелүү файлга тиешелүү болгондорду гана эмес, тутумдагы бардык "кир" барактарды дискке таштады. Бул Firefox'тун баскычын басуу магниттик дискке жазыла турган мегабайт маалыматтарга түрткү бериши мүмкүн экенин билдирген. Маселени чечүү, мен түшүнгөндөй ал материал базасы менен иштөөнү асинхрондук фондук тапшырмаларга өткөрүү болгон. Бул Firefox мурда чындап эле талап кылынгандан да катуураак сактоо талаптарын ишке ашырганын жана ext3 файл тутумунун өзгөчөлүктөрү бул көйгөйдү ого бетер күчөттү дегенди билдирет.

Экинчи маселе 2009-жылы болгон. Андан кийин, система бузулгандан кийин, жаңы ext4 файл тутумунун колдонуучулары жаңы түзүлгөн көптөгөн файлдардын узундугу нөлгө барабар болгон фактыга туш болушкан, бирок бул эски ext3 файл тутумунда болгон эмес. Мурунку абзацта мен ext3 дискке өтө көп маалыматтарды кантип жууп салганы жөнүндө айтып бердим, бул ишти бир топ жайлаткан. fsync(). Кырдаалды жакшыртуу үчүн, ext4 ичинде белгилүү бир файлга тиешелүү кир барактар ​​гана дискке тазаланат. Жана башка файлдардан алынган маалыматтар эстутумда ext3 файлына караганда алда канча узак убакытка сакталат. Бул иштин майнаптуулугун жогорулатуу үчүн жасалды (демейки боюнча, маалыматтар ушул абалда 30 секундада калат, сиз муну колдонуп конфигурациялай аласыз dirty_expire_centisecs; бул жерде Бул тууралуу кошумча материалдарды таба аласыз). Бул ката болгондон кийин чоң көлөмдөгү маалымат кайтарылгыс жоголуп кетиши мүмкүн дегенди билдирет. Бул маселени чечүү үчүн пайдалануу болуп саналат fsync() туруктуу маалыматтарды сактоону камсыз кылуу жана аларды мүмкүн болушунча бузулуулардын кесепеттеринен коргоону талап кылган тиркемелерде. Функция fsync() ext4 колдонууда ext3 колдонууга караганда алда канча натыйжалуу иштейт. Бул ыкманын кемчилиги, аны колдонуу, мурункудай эле, программаларды орнотуу сыяктуу кээ бир операциялардын аткарылышын жайлатат. Бул тууралуу чоо-жайын караңыз бул жерде и бул жерде.

Үчүнчү маселе боюнча fsync(), 2018-жылы пайда болгон. Андан кийин, PostgreSQL долбоорунун алкагында, эгерде функция бар экени аныкталган fsync() катага туш болсо, ал "кир" баракчаларды "таза" деп белгилейт. Натыйжада, төмөнкү чакырыктар fsync() Алар мындай баракчалар менен эч нерсе жасашпайт. Ушундан улам, өзгөртүлгөн барактар ​​эстутумда сакталат жана эч качан дискке жазылбайт. Бул чыныгы кырсык, анткени колдонмо кээ бир маалыматтар дискке жазылган деп ойлойт, бирок чындыгында андай болбойт. Мындай ийгиликсиздиктер fsync() сейрек кездешет, мындай жагдайларда колдонуу көйгөй менен күрөшүү үчүн дээрлик эч нерсе кыла албайт. Ушул күндөрү, мындай болгондо, PostgreSQL жана башка тиркемелер бузулат. бул, материалында "Колдонмолор fsync каталарынан калыбына келтире алабы?", бул көйгөй деталдуу түрдө изилденген. Азыркы учурда бул маселенин эң жакшы чечими желек менен Түз киргизүү/Чыгууну колдонуу болуп саналат O_SYNC же желек менен O_DSYNC. Бул ыкма менен система белгилүү бир жазуу операцияларында пайда болушу мүмкүн болгон каталар жөнүндө кабарлайт, бирок бул ыкма тиркемеден буферлердин өзүн башкаруусун талап кылат. Бул тууралуу көбүрөөк окуңуз бул жерде и бул жерде.

O_SYNC жана O_DSYNC желектерин колдонуу менен файлдарды ачуу

Туруктуу маалыматтарды сактоону камсыз кылган Linux механизмдерин талкуулоого кайтып келели. Тактап айтканда, желек колдонуу жөнүндө сөз болуп жатат O_SYNC же желек O_DSYNC системалык чалуу аркылуу файлдарды ачууда ачык(). Бул ыкма менен ар бир маалымат жазуу операциясы ар бир буйруктан кийин аткарылат write() системага ылайыктуу буйруктар берилет fsync() и fdatasync(). The POSIX спецификациялары бул "Synchronized I/O File Integrity Completion" жана "Маалыматтардын бүтүндүгүн аяктоо" деп аталат. Бул ыкманын негизги артыкчылыгы маалыматтардын бүтүндүгүн камсыз кылуу үчүн эки эмес, бир гана тутум чалуу керек (мисалы - write() и fdatasync()). Бул ыкманын негизги кемчилиги - файлдын тиешелүү дескрипторун колдонгон бардык жазуулар синхрондолот, бул колдонмонун кодун структуралоо мүмкүнчүлүгүн чектеши мүмкүн.

O_DIRECT желеги менен Түз I/O колдонуу

Системалык чалуу open() желегин колдойт O_DIRECT, ал диск менен түздөн-түз өз ара аракеттенүү аркылуу киргизүү/чыгаруу операцияларын аткаруу үчүн операциялык системанын кэшин айланып өтүүгө арналган. Бул, көп учурларда, программа тарабынан берилген жазуу буйруктары түздөн-түз диск менен иштөөгө багытталган буйруктарга которулат дегенди билдирет. Бирок, жалпысынан алганда, бул механизм функцияларын алмаштыруу эмес fsync() же fdatasync(). Чындыгында, дисктин өзү болот кийинкиге калтыруу же кэш тиешелүү маалыматтарды жазуу буйруктары. Андан да жаманы, кээ бир өзгөчө учурларда желекти колдонууда I/O операциялары аткарылат O_DIRECT, берүү салттуу буфердик операцияларга. Бул көйгөйдү чечүүнүн эң оңой жолу - файлдарды ачуу үчүн желекти колдонуу O_DSYNC, бул ар бир жазуу операциясы чакыруу менен коштолот дегенди билдирет fdatasync().

Бул XFS файл системасы жакында үчүн "тез жолду" кошкон экен O_DIRECT|O_DSYNC- маалыматтарды жазуу. Эгерде блоктун жардамы менен кайра жазылса O_DIRECT|O_DSYNC, анда XFS, кэшти тазалоонун ордуна, эгер түзмөк аны колдосо, FUA жазуу буйругун аткарат. Мен муну утилитаны колдонуу менен текшердим blktrace Linux 5.4/Ubuntu 20.04 системасында. Бул ыкма натыйжалуураак болушу керек, анткени колдонулганда дискке минималдуу маалымат жазылат жана эки эмес (кэшти жазуу жана тазалоо) бир операция колдонулат. Мен шилтемени таптым жамаачы Бул механизмди ишке ашырган 2018 ядросу. Бул оптималдаштырууну башка файл тутумдарына колдонуу жөнүндө кээ бир талкуулар бар, бирок менин билишимче, XFS азырынча муну колдогон жалгыз файл системасы.

sync_file_range() функциясы

Linux тутумдук чалууга ээ sync_file_range(), бул сизге бүт файлды эмес, файлдын бир бөлүгүн гана дискке тазалоого мүмкүндүк берет. Бул чалуу асинхрондук маалыматтарды тазалоону баштайт жана анын бүтүшүн күтпөйт. Бирок күбөлүктө sync_file_range() команда "өтө коркунучтуу" деп айтылат. Аны колдонуу сунушталбайт. Өзгөчөлүктөрү жана коркунучтары sync_file_range() абдан жакшы сүрөттөлгөн бул материал. Тактап айтканда, бул чалуу өзөктүн дискке кир маалыматтарды жууп салганын көзөмөлдөө үчүн RocksDBди колдонгон көрүнөт. Бирок, ошол эле учурда, туруктуу маалыматтарды сактоону камсыз кылуу үчүн, ал да колдонулат fdatasync(). The код RocksDB бул тема боюнча кээ бир кызыктуу комментарийлер бар. Мисалы, чалуу болуп көрүнөт sync_file_range() ZFS колдонууда, ал дискке маалыматтарды жууп салбайт. Тажрыйба мага сейрек колдонулган коддо мүчүлүштүктөрдү камтышы мүмкүн экенин айтат. Ошондуктан, мен өтө зарылчылык болбосо, бул тутумдук чалууларды колдонбоону сунуштайт элем.

Дайындардын туруктуулугун камсыздоого жардам берген тутумдук чалуулар

Мен маалыматтардын туруктуулугун камсыз кылуучу I/O операцияларын аткаруу үчүн колдонула турган үч ыкма бар деген тыянакка келдим. Алардын бардыгы функция чакыруусун талап кылат fsync() файл түзүлгөн каталог үчүн. Бул ыкмалар:

  1. Функция чакыруу fdatasync() же fsync() функциядан кийин write() (колдонгон жакшы fdatasync()).
  2. Желек менен ачылган файлдын дескриптору менен иштөө O_DSYNC же O_SYNC (жакшы - желек менен O_DSYNC).
  3. Буйрукту колдонуу pwritev2() желек менен RWF_DSYNC же RWF_SYNC (жакшы желек менен RWF_DSYNC).

Performance Notes

Мен текшерген ар кандай механизмдердин иштешин кылдаттык менен ченеген жокмун. Алардын ишинин ылдамдыгында мен байкаган айырмачылыктар өтө аз. Бул мен туура эмес болушум мүмкүн жана ар кандай шарттарда бир эле нерсе ар кандай натыйжаларды бериши мүмкүн дегенди билдирет. Биринчиден, мен аткарууга эмне көбүрөөк таасир этет, андан кийин эмне азыраак иштеши жөнүндө сүйлөшөм.

  1. Файлдын берилиштерин кайра жазуу файлга маалыматтарды кошууга караганда тезирээк (өндүрүшүнүн пайдасы 2-100% болушу мүмкүн). Файлга берилиштерди кошуу системалык чакыруудан кийин да файлдын метаберилиштерине кошумча өзгөртүүлөрдү талап кылат fallocate(), бирок бул таасирдин чоңдугу ар кандай болушу мүмкүн. Мен мыкты аткаруу үчүн, чалууну сунуштайм fallocate() талап кылынган мейкиндикти алдын ала бөлүп берүү. Андан кийин бул боштук ачык нөл менен толтурулат жана чакырылышы керек fsync(). Бул файл тутумундагы тиешелүү блоктордун "бөлүштүрүлбөгөн" эмес, "бөлүнгөн" деп белгиленгенин камсыздайт. Бул кичинекей (болжол менен 2%) аткарууну жакшыртууну берет. Кошумчалай кетсек, кээ бир дисктерде блокко биринчи кирүү башкаларга караганда жайыраак болушу мүмкүн. Бул нөл менен мейкиндикти толтуруу аткаруунун олуттуу (болжол менен 100%) жакшырышына алып келиши мүмкүн дегенди билдирет. Атап айтканда, бул дисктер менен болушу мүмкүн AWS EBS (бул расмий эмес маалымат, аны тастыктай алган жокмун). Ошол эле сактоо үчүн GCP Persistent Disk (жана бул тесттер менен тастыкталган расмий маалымат). Башка эксперттер да ушундай кылышкан байкоолор, ар кандай дисктерге байланыштуу.
  2. Системалык чалуулар канчалык аз болсо, ошончолук жогорку өндүрүмдүүлүк (пайда болжол менен 5% болушу мүмкүн). Чакырык окшойт open() желек менен O_DSYNC же чакыруу pwritev2() желек менен RWF_SYNC чалууга караганда тезирээк fdatasync(). Менин оюмча, бул жерде бул ыкма бир эле көйгөйдү чечүү үчүн системалык чалуулар азыраак аткарылышы керек болгон ролду ойнойт (экөөнүн ордуна бир чалуу). Бирок аткаруунун айырмасы өтө аз, ошондуктан сиз аны таптакыр этибар албай, колдонмодо анын логикасын татаалдаштырбай турган нерсени колдонсоңуз болот.

Эгер сиз туруктуу маалыматтарды сактоо темасына кызыксаңыз, бул жерде кээ бир пайдалуу материалдар бар:

  • I/O мүмкүндүк алуу ыкмалары — киргизүү/чыгаруу механизмдеринин негиздерине сереп салуу.
  • Маалыматтын дискке жетүүсүн камсыз кылуу — тиркемеден дискке баратканда маалыматтар эмне болору жөнүндө окуя.
  • Качан камтыган каталогду синхрондоштуруу керек - качан колдонуу керек деген суроого жооп fsync() каталогдор үчүн. Муну кыскача айтканда, жаңы файлды түзүп жатканда муну жасашыңыз керек экен, жана бул сунуштун себеби Linux-та бир эле файлга көптөгөн шилтемелер болушу мүмкүн.
  • Linux боюнча SQL Server: FUA ички — бул жерде Linux платформасында SQL серверинде туруктуу маалыматтарды сактоонун кантип ишке ашырылаарынын сүрөттөлүшү. Бул жерде Windows жана Linux тутумдук чалууларынын ортосунда кызыктуу салыштыруулар бар. Бул материалдын аркасында мен XFSди FUA оптималдаштыруу жөнүндө билдим деп ишенем.

Сиз дискте коопсуз сакталган деп ойлогон маалыматтарды жоготуп алдыңызбы?

Туруктуу маалыматтарды сактоо жана Linux File API'лери

Туруктуу маалыматтарды сактоо жана Linux File API'лери

Source: www.habr.com