Би үүлэн систем дэх өгөгдөл хадгалах тогтвортой байдлын талаар судалж үзээд үндсэн зүйлийг ойлгож байгаа эсэхийг шалгахын тулд өөрийгөө туршихаар шийдсэн. I
Энэ нийтлэлд би Линукс файлын API-уудын өгсөн тогтвортой байдлын механизмуудыг судлах болно. Энд бүх зүйл энгийн байх ёстой юм шиг санагдаж байна: програм тушаалыг дууддаг write()
, мөн энэ тушаалын ажиллагаа дууссаны дараа өгөгдөл дискэн дээр найдвартай хадгалагдах болно. Гэхдээ write()
зөвхөн RAM-д байрлах цөмийн кэш рүү програмын өгөгдлийг хуулна. Системийг диск рүү өгөгдөл бичихийг албадахын тулд зарим нэмэлт механизмуудыг ашиглах шаардлагатай.
Ерөнхийдөө энэ материал бол миний сонирхсон сэдвээр миний сурсан зүйлтэй холбоотой тэмдэглэлийн багц юм. Хэрэв бид хамгийн чухал зүйлийн талаар товч ярих юм бол тогтвортой өгөгдөл хадгалах ажлыг зохион байгуулахын тулд та тушаалыг ашиглах хэрэгтэй болж байна. fdatasync()
эсвэл тугтай файлуудыг нээх O_DSYNC
. Хэрэв та кодоос диск рүү шилжих замд өгөгдөлд юу тохиолдох талаар илүү ихийг мэдэхийг сонирхож байгаа бол эндээс харна уу
write() функцийг ашиглах онцлог
Системийн дуудлага write()
стандартад тодорхойлсон write()
Өгөгдөл унших үйлдлүүд нь бусад процессууд эсвэл урсгалуудаас өгөгдөлд хандаж байсан ч өмнө нь бичигдсэн байтуудыг буцаах ёстой (
Энэ нь үйл ажиллагаа гэсэн үг үү write()
атом уу? Техникийн үүднээс авч үзвэл тийм ээ. Өгөгдөл унших үйлдлүүд нь бичигдсэн зүйлсийн аль нэгийг нь эсвэл бүгдийг нь буцаах ёстой write()
. Гэхдээ мэс засал write()
, стандартын дагуу бичихийг хүссэн бүх зүйлийг бичиж дуусгах шаардлагагүй. Өгөгдлийн зөвхөн нэг хэсгийг бичихийг зөвшөөрдөг. Жишээлбэл, бид ижил файлын тодорхойлогчоор тайлбарласан файлд тус бүр 1024 байт нэмэх хоёр урсгалтай байж болно. Стандартын үүднээс авч үзвэл бичих үйлдэл бүр нь файлд зөвхөн нэг байт нэмэх боломжтой үед үр дүн нь хүлээн зөвшөөрөгдөх болно. Эдгээр үйлдлүүд нь атомын шинж чанартай хэвээр байх боловч дууссаны дараа файлд бичсэн өгөгдөл нь будлиантай байх болно.
fsync() болон fdatasync() функцууд
Диск рүү өгөгдлийг цэвэрлэх хамгийн хялбар арга бол функцийг дуудах явдал юм fdatasync()
. The fdatasync()
Энэ функцийг ажиллуулах явцад ийм хэмжээний мета өгөгдөл дискэнд хадгалагддаг бөгөөд энэ нь "дараах өгөгдлийг унших үйлдлүүдийг зөв гүйцэтгэхэд шаардлагатай" гэжээ. Мөн энэ нь ихэнх програмуудад анхаарал хандуулдаг зүйл юм.
Энд тохиолдож болох нэг асуудал бол эдгээр механизмууд нь алдаа гарсаны дараа файлыг олж болно гэсэн баталгааг өгдөггүй явдал юм. Ялангуяа шинэ файл үүсгэх үед нэг нь залгах ёстой fsync()
түүнийг агуулсан лавлахын хувьд. Үгүй бол эвдрэлийн дараа энэ файл байхгүй болж магадгүй юм. Үүний шалтгаан нь UNIX-ийн дагуу хатуу холбоосыг ашигласнаар файл олон директорт байж болно. Тиймээс, дуудлага хийх үед fsync()
Ямар директорын өгөгдлийг диск рүү оруулахыг файлд мэдэх арга байхгүй (fsync()
харгалзах файлуудыг агуулсан сангууд руу чиглүүлэх боловч бусад файлын системд тийм биш байж болно.
Энэ механизмыг өөр өөр файлын системд өөр өөрөөр хэрэгжүүлж болно. би хэрэглэсэн 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-ын товчлуур дээр дарснаар мегабайт өгөгдлийг соронзон диск рүү бичихэд олон секунд зарцуулагдана гэсэн үг. Миний ойлгосноор асуудлын шийдэл
Хоёр дахь асуудал 2009 онд гарсан. Дараа нь системийн эвдрэлийн дараа шинэ ext4 файлын системийн хэрэглэгчид шинээр үүсгэсэн олон файлууд тэг урттай болохыг олж мэдсэн боловч хуучин ext3 файлын системд ийм зүйл тохиолдоогүй. Өмнөх догол мөрөнд би ext3 нь дискэн дээр хэт их өгөгдөл хаяж, үйлдлийг маш их удаашруулсан тухай ярьсан. fsync()
. Нөхцөл байдлыг сайжруулахын тулд ext4 нь зөвхөн тухайн файлтай холбоотой "бохир" хуудсыг устгадаг. Мөн бусад файлуудын өгөгдөл санах ойд ext3-ээс хамаагүй удаан хадгалагддаг. Энэ нь гүйцэтгэлийг сайжруулахын тулд хийгдсэн (өгөгдмөл байдлаар өгөгдөл 30 секундын турш энэ төлөвт үлдэнэ, та үүнийг ашиглан тохируулж болно. fsync()
Тогтвортой өгөгдөл хадгалах, бүтэлгүйтлийн үр дагавраас аль болох хамгаалах шаардлагатай програмуудад. Чиг үүрэг fsync()
ext4-тай харьцуулахад ext3-тэй илүү үр дүнтэй ажилладаг. Энэ аргын сул тал нь түүнийг ашиглах нь өмнөх шигээ програм суулгах гэх мэт зарим үйлдлүүдийг удаашруулдаг. Энэ талаар дэлгэрэнгүй харна уу
Гурав дахь асуудал fsync()
, 2018 онд үүссэн. Дараа нь PostgreSQL төслийн хүрээнд хэрэв функц байгаа нь тогтоогдсон fsync()
алдаатай тулгарвал "бохир" хуудсыг "цэвэр" гэж тэмдэглэнэ. Үүний үр дүнд дараах дуудлага ирдэг fsync()
ийм хуудсуудтай юу ч бүү хий. Үүнээс болж өөрчилсөн хуудсууд нь санах ойд хадгалагддаг бөгөөд хэзээ ч диск рүү бичигддэггүй. Энэ бол жинхэнэ гамшиг, учир нь програм нь зарим өгөгдлийг дискэнд бичсэн гэж бодох боловч үнэндээ тийм биш юм. Ийм бүтэлгүйтэл fsync()
ховор тохиолддог тул ийм нөхцөлд хэрэглэх нь асуудалтай тэмцэхэд бараг юу ч хийж чадахгүй. Эдгээр өдрүүдэд PostgreSQL болон бусад програмууд гацаж байна. O_SYNC
эсвэл тугтай O_DSYNC
. Энэ аргын тусламжтайгаар систем нь тодорхой өгөгдөл бичих үйлдлийг гүйцэтгэх үед гарч болох алдаануудыг мэдээлэх боловч энэ арга нь програмаас буферийг өөрөө удирдахыг шаарддаг. Энэ талаар дэлгэрэнгүй уншина уу
O_SYNC болон O_DSYNC тугуудыг ашиглан файлуудыг нээж байна
Тогтвортой өгөгдөл хадгалах боломжийг олгодог Линукс механизмуудын тухай хэлэлцүүлэгт буцаж орцгооё. Тодруулбал, туг ашиглах тухай ярьж байна O_SYNC
эсвэл туг O_DSYNC
системийн дуудлага ашиглан файлуудыг нээх үед write()
системд тус тус тушаал өгсөн fsync()
и fdatasync()
. The write()
и fdatasync()
). Энэ аргын гол сул тал нь харгалзах файлын тодорхойлогчийг ашиглан бичих бүх үйлдлүүд синхрончлогдох бөгөөд энэ нь програмын кодыг зохион байгуулах боломжийг хязгаарлаж болзошгүй юм.
O_DIRECT тугтай шууд I/O ашиглах
Системийн дуудлага open()
тугийг дэмждэг O_DIRECT
, үйлдлийн системийн кэшийг тойрч гарах, оролт / гаралтын үйлдлийг гүйцэтгэх, дисктэй шууд харьцах зориулалттай. Энэ нь ихэнх тохиолдолд програмаас гаргасан бичих командуудыг дисктэй ажиллахад чиглэсэн командууд руу шууд хөрвүүлнэ гэсэн үг юм. Гэхдээ ерөнхийдөө энэ механизм нь функцийг орлуулах зүйл биш юм fsync()
буюу fdatasync()
. Үнэн хэрэгтээ диск өөрөө боломжтой юм O_DIRECT
, O_DSYNC
, энэ нь бичих үйлдэл бүрийн дараа дуудлага ирнэ гэсэн үг юм fdatasync()
.
XFS файлын систем саяхан "хурдан зам"-ыг нэмсэн нь тогтоогдсон O_DIRECT|O_DSYNC
- мэдээллийн бүртгэл. блок ашиглан дарж бичсэн бол O_DIRECT|O_DSYNC
, дараа нь XFS нь кэшийг цэвэрлэхийн оронд төхөөрөмж дэмждэг бол FUA бичих командыг гүйцэтгэх болно. Би үүнийг хэрэглүүрийг ашиглан баталгаажуулсан blktrace
Linux 5.4/Ubuntu 20.04 систем дээр. Энэ арга нь илүү үр дүнтэй байх ёстой, учир нь энэ нь хамгийн бага хэмжээний өгөгдлийг дискэнд бичиж, хоёр биш нэг үйлдлийг ашигладаг (кэшийг бичих, цэвэрлэх). Би холбоосыг олсон
sync_file_range() функц
Линукс системийн дуудлагатай sync_file_range()
Энэ тушаалыг "маш аюултай" гэж хэлдэг. Үүнийг хэрэглэхийг зөвлөдөггүй. Онцлог шинж чанарууд ба аюулууд sync_file_range()
-д маш сайн дүрсэлсэн fdatasync()
. The sync_file_range()
ZFS-ийг ашиглах үед өгөгдлийг диск рүү шилжүүлдэггүй. Туршлагаас харахад ховор хэрэглэгддэг код нь алдаатай байж магадгүй юм. Тиймээс би онцын шаардлагагүй бол энэ системийн дуудлагыг ашиглахгүй байхыг зөвлөж байна.
Өгөгдлийн тогтвортой байдлыг хангахад туслах системийн дуудлага
Тогтвортой оролт гаралтын үйлдлүүдийг гүйцэтгэхэд ашиглаж болох гурван арга байдаг гэсэн дүгнэлтэд хүрсэн. Тэд бүгд функцийн дуудлага шаарддаг fsync()
файл үүсгэгдсэн директорийн хувьд. Эдгээр нь дараахь аргууд юм.
- Функцийн дуудлага
fdatasync()
буюуfsync()
функцийн дарааwrite()
(ашиглах нь дээрfdatasync()
). - Тугтай нээгдсэн файлын тодорхойлогчтой ажиллах
O_DSYNC
буюуO_SYNC
(илүү сайн - тугтайO_DSYNC
). - Командын хэрэглээ
pwritev2()
тугтайRWF_DSYNC
буюуRWF_SYNC
(туг далбаатай байвал зохимжтойRWF_DSYNC
).
Гүйцэтгэлийн тэмдэглэл
Би судалж үзсэн янз бүрийн механизмуудын гүйцэтгэлийг сайтар хэмжээгүй. Тэдний ажлын хурдад миний анзаарсан ялгаа нь маш бага юм. Энэ нь би буруу байж магадгүй гэсэн үг бөгөөд бусад нөхцөлд ижил зүйл өөр үр дүнг харуулж магадгүй юм. Эхлээд би гүйцэтгэлд юу илүү нөлөөлдөг талаар, дараа нь гүйцэтгэлд юу нөлөөлдөг талаар ярих болно.
- Файлын өгөгдлийг дарж бичих нь файлд өгөгдөл нэмэхээс илүү хурдан байдаг (гүйцэтгэлийн өсөлт 2-100% байж болно). Файлд өгөгдөл хавсаргахад системийн дуудлагын дараа ч файлын мета өгөгдөлд нэмэлт өөрчлөлт оруулах шаардлагатай
fallocate()
, гэхдээ энэ нөлөөллийн хэмжээ өөр байж болно. Би хамгийн сайн гүйцэтгэлтэй байхын тулд утсаар ярихыг зөвлөж байнаfallocate()
шаардлагатай зайг урьдчилан хуваарилах. Дараа нь энэ зайг тэгээр тодорхой дүүргэж, дуудах ёстойfsync()
. Энэ нь файлын систем дэх харгалзах блокуудыг "хуваарилагдаагүй" биш харин "хуваарилагдсан" гэж тэмдэглэхэд хүргэнэ. Энэ нь бага зэрэг (ойролцоогоор 2%) гүйцэтгэлийг сайжруулдаг. Түүнчлэн, зарим дискний эхний блок хандалтын ажиллагаа бусадтай харьцуулахад удаан байж болно. Энэ нь орон зайг тэгээр дүүргэх нь гүйцэтгэлийг мэдэгдэхүйц (100% орчим) сайжруулахад хүргэдэг гэсэн үг юм. Ялангуяа энэ нь дискэнд тохиолдож болно.AWS EBS (энэ бол албан бус мэдээлэл, би баталгаажуулж чадаагүй). Хадгалахад ч мөн адил.GCP байнгын диск (мөн энэ нь туршилтаар батлагдсан албан ёсны мэдээлэл юм). Бусад мэргэжилтнүүд ч мөн адил зүйлийг хийсэнажиглалт янз бүрийн дискүүдтэй холбоотой. - Системийн дуудлага бага байх тусам гүйцэтгэл өндөр байх болно (олз нь 5% орчим байж болно). Энэ нь дуудлага шиг харагдаж байна
open()
тугтайO_DSYNC
эсвэл залгана ууpwritev2()
тугтайRWF_SYNC
илүү хурдан дуудлага хийхfdatasync()
. Энд байгаа гол зүйл бол энэ хандлагыг ашигласнаар нэг ажлыг шийдвэрлэхийн тулд цөөн тооны системийн дуудлага хийх шаардлагатай (хоёр биш нэг дуудлага) үүрэг гүйцэтгэдэг гэж би бодож байна. Гэхдээ гүйцэтгэлийн ялгаа нь маш бага тул та үүнийг үл тоомсорлож, програмын логикийн хүндрэлд хүргэхгүй зүйлийг ашиглаж болно.
Тогтвортой өгөгдөл хадгалах сэдвийг сонирхож байгаа бол дараах хэрэгтэй материалууд энд байна.
I/O хандалтын аргууд - оролт / гаралтын механизмын үндсүүдийн тойм.Өгөгдөл дискэнд хүрэхийг баталгаажуулах - програмаас диск рүү явах замд өгөгдөлд юу тохиолдох тухай түүх.Та агуулсан лавлахыг хэзээ fсинк хийх ёстой вэ? - хэзээ өргөдөл гаргах вэ гэсэн асуултын хариултfsync()
сангуудын хувьд. Товчхондоо, та шинэ файл үүсгэхдээ үүнийг хийх хэрэгтэй болж байгаа бөгөөд энэ зөвлөмжийн шалтгаан нь Линукс дээр нэг файлд олон лавлагаа байж болно.Линукс дээрх SQL сервер: FUA дотоод - Линукс платформ дээрх SQL Server-д өгөгдөл хадгалах ажиллагааг хэрхэн хэрэгжүүлдэг тухай тайлбарыг энд оруулав. Windows болон Линукс системийн дуудлагуудын хооронд сонирхолтой харьцуулалт энд байна. Энэ материалын ачаар би XFS-ийн FUA оновчлолын талаар олж мэдсэн гэдэгт би бараг итгэлтэй байна.
Та хэзээ нэгэн цагт дискэн дээр найдвартай хадгалагдсан гэж бодож байсан мэдээллээ алдаж байсан уу?
Эх сурвалж: www.habr.com