Davamlı Məlumat Saxlama və Linux Fayl API-ləri

Bulud sistemlərində məlumatların saxlanmasının davamlılığını araşdırarkən, əsas şeyləri başa düşdüyümə əmin olmaq üçün özümü sınamaq qərarına gəldim. I NVMe spesifikasiyasını oxumaqla başladı Davamlı məlumatların saxlanması ilə bağlı hansı zəmanətlərin (yəni, məlumatların sistem nasazlığından sonra mövcud olacağına zəmanət) bizə NMVe disklərini verdiyini başa düşmək üçün. Aşağıdakı əsas nəticələrə gəldim: verilənlərin yazılması əmri verildiyi andan yaddaş daşıyıcısına yazılan ana qədər verilənlər zədələnmiş hesab edilməlidir. Bununla belə, əksər proqramlar məlumatları qeyd etmək üçün sistem zənglərindən məmnuniyyətlə istifadə edirlər.

Bu yazıda mən Linux fayl API-ləri tərəfindən təmin edilən davamlı saxlama mexanizmlərini araşdırıram. Görünür, burada hər şey sadə olmalıdır: proqram əmri çağırır write(), və bu əmr tamamlandıqdan sonra məlumatlar təhlükəsiz şəkildə diskdə saxlanacaq. Amma write() proqram məlumatlarını yalnız RAM-da yerləşən nüvə keşinə kopyalayır. Sistemi diskə məlumat yazmağa məcbur etmək üçün bəzi əlavə mexanizmlərdən istifadə etməlisiniz.

Davamlı Məlumat Saxlama və Linux Fayl API-ləri

Ümumiyyətlə, bu material məni maraqlandıran mövzuda öyrəndiklərimlə bağlı qeydlər toplusudur. Ən vacib şey haqqında çox qısa danışsaq, məlumatların davamlı saxlanmasını təşkil etmək üçün əmrdən istifadə etməlisiniz. fdatasync() və ya faylları bayraqla açın O_DSYNC. Əgər koddan diskə keçərkən verilənlərə nə baş verdiyi haqqında daha çox öyrənmək istəyirsinizsə, nəzər yetirin bu məqalə.

write() funksiyasından istifadənin xüsusiyyətləri

Sistem zəngi write() standartda müəyyən edilir IEEE POSIX fayl deskriptoruna məlumat yazmaq cəhdi kimi. Uğurlu başa çatdıqdan sonra write() Verilənlərin oxunması əməliyyatları əvvəllər yazılmış baytları tam olaraq qaytarmalıdır, hətta məlumatlara digər proseslərdən və ya iplərdən daxil olunsa belə bunu edir (burada POSIX standartının müvafiq bölməsi). Burada, mövzuların normal fayl əməliyyatları ilə qarşılıqlı əlaqəsinə dair bölmədə qeyd var ki, əgər iki başlıq hər biri bu funksiyaları çağırırsa, onda hər bir zəng digər çağırışın bütün təyin edilmiş nəticələrini görməli və ya heç birini görməməlidir. nəticələri. Bu, bütün fayl giriş/çıxış əməliyyatlarının işlədikləri resursda kilid saxlamalı olduğu qənaətinə gətirib çıxarır.

Bu əməliyyat deməkdir write() atomdur? Texniki baxımdan, bəli. Məlumatların oxunması əməliyyatları yazılanların hamısını və ya heç nəyi qaytarmalıdır write(). Amma əməliyyat write(), standarta görə, yazmaq istənilən hər şeyi yazmaqla bitirmək məcburiyyətində deyil. Ona məlumatların yalnız bir hissəsini yazmağa icazə verilir. Məsələn, eyni fayl deskriptoru tərəfindən təsvir edilən fayla hər biri 1024 bayt əlavə edən iki mövzu ola bilər. Standart baxımından, hər bir yazma əməliyyatı fayla yalnız bir bayt əlavə edə bildikdə məqbul nəticə olacaqdır. Bu əməliyyatlar atomik olaraq qalacaq, lakin tamamlandıqdan sonra fayla yazdıqları məlumatlar qarışdırılacaq. Burada Stack Overflow-da bu mövzuda çox maraqlı müzakirə.

fsync() və fdatasync() funksiyaları

Məlumatları diskə köçürməyin ən asan yolu funksiyanı çağırmaqdır fsync(). Bu funksiya əməliyyat sistemindən bütün dəyişdirilmiş blokları keşdən diskə köçürməyi xahiş edir. Buraya bütün fayl metadataları (giriş vaxtı, faylın dəyişdirilməsi vaxtı və s.) daxildir. Hesab edirəm ki, bu metadata nadir hallarda tələb olunur, ona görə də bunun sizin üçün vacib olmadığını bilirsinizsə, funksiyadan istifadə edə bilərsiniz. fdatasync(). Ilə kömək haqqında fdatasync() Deyilənə görə, bu funksiyanın işləməsi zamanı “aşağıdakı məlumatların oxunması əməliyyatlarının düzgün yerinə yetirilməsi üçün zəruri olan” o qədər metadata diskdə saxlanılır. Və bu, əksər tətbiqləri maraqlandıran şeydir.

Burada yarana biləcək bir problem, bu mexanizmlərin mümkün uğursuzluqdan sonra faylın aşkar ediləcəyinə zəmanət verməməsidir. Xüsusilə, yeni bir fayl yaratarkən, zəng etmək lazımdır fsync() onu ehtiva edən kataloq üçün. Əks halda, uğursuzluqdan sonra bu faylın mövcud olmadığı ortaya çıxa bilər. Bunun səbəbi odur ki, UNIX-də sərt keçidlərin istifadəsi səbəbindən bir fayl bir neçə kataloqda mövcud ola bilər. Buna görə də zəng edərkən fsync() faylın hansı kataloq məlumatlarının diskə silinməsi lazım olduğunu bilmək üçün heç bir yol yoxdur (burada Bu barədə ətraflı oxuya bilərsiniz). Görünür, ext4 fayl sistemi bacarır avtomatik olaraq müraciət edin fsync() müvafiq faylları olan qovluqlara, lakin bu, digər fayl sistemlərində olmaya bilər.

Bu mexanizm müxtəlif fayl sistemlərində fərqli şəkildə həyata keçirilə bilər. Mən istifadə etdim blktrace ext4 və XFS fayl sistemlərində hansı disk əməliyyatlarından istifadə edildiyini öyrənmək üçün. Hər ikisi həm fayl məzmunu, həm də fayl sistemi jurnalı üçün diskə müntəzəm yazma əmrləri verir, keşi təmizləyir və jurnala yazmaq üçün FUA (Force Unit Access, diskə məlumatların birbaşa yazılması, keşi keçərək) yerinə yetirərək çıxın. Yəqin ki, onlar bunu əməliyyatın baş verdiyini təsdiqləmək üçün edirlər. FUA-nı dəstəkləməyən sürücülərdə bu, iki önbelleğin yuyulmasına səbəb olur. Təcrübələrim bunu göstərdi fdatasync() bir az daha sürətli fsync(). Utility blktrace olduğunu göstərir fdatasync() adətən diskə daha az məlumat yazır (ext4 fsync() 20 KiB yazır və fdatasync() - 16 KiB). Həmçinin, XFS-nin ext4-dən bir qədər sürətli olduğunu öyrəndim. Və burada köməyi ilə blktrace bunu öyrənə bildi fdatasync() diskə daha az məlumat ötürür (XFS-də 4 KiB).

fsync() istifadə edərkən yaranan qeyri-müəyyən vəziyyətlər

Bununla bağlı üç qeyri-müəyyən vəziyyət düşünə bilərəm fsync()təcrübədə qarşılaşdığım.

İlk belə hal 2008-ci ildə baş verib. O zaman diskə çoxlu sayda fayl yazılsa, Firefox 3 interfeysi donacaqdı. Problem onda idi ki, interfeysin tətbiqi onun vəziyyəti haqqında məlumat saxlamaq üçün SQLite verilənlər bazasından istifadə edirdi. İnterfeysdə baş verən hər dəyişiklikdən sonra funksiya çağırılırdı fsync(), sabit məlumatların saxlanmasına yaxşı zəmanət verdi. Daha sonra istifadə edilən ext3 fayl sistemində funksiya fsync() sistemdəki bütün "çirkli" səhifələri diskə atdı, yalnız müvafiq faylla əlaqəli olanları deyil. Bu o demək idi ki, Firefox-da düyməyə basmaq meqabaytlarla məlumatın maqnit diskinə yazılmasına səbəb ola bilər ki, bu da bir neçə saniyə çəkə bilər. Problemin həlli, mənim anladığım qədər o material verilənlər bazası ilə işi asinxron fon tapşırıqlarına köçürmək idi. Bu o deməkdir ki, Firefox əvvəllər həqiqətən tələb olunandan daha sərt saxlama tələblərini həyata keçirirdi və ext3 fayl sisteminin xüsusiyyətləri bu problemi daha da gücləndirdi.

İkinci problem 2009-cu ildə baş verdi. Sonra, sistem qəzasından sonra, yeni ext4 fayl sisteminin istifadəçiləri bir çox yeni yaradılmış faylların sıfır uzunluğuna malik olması ilə qarşılaşdılar, lakin köhnə ext3 fayl sistemi ilə bu baş vermədi. Əvvəlki paraqrafda mən ext3-ün diskə çoxlu məlumatı necə sildiyindən danışmışdım ki, bu da işləri çox yavaşlatdı. fsync(). Vəziyyəti yaxşılaşdırmaq üçün ext4-də yalnız müəyyən bir fayla aid olan çirkli səhifələr diskə silinir. Digər fayllardan alınan məlumatlar ext3 ilə müqayisədə daha uzun müddət yaddaşda qalır. Bu, performansı yaxşılaşdırmaq üçün edilib (defolt olaraq, məlumatlar 30 saniyə ərzində bu vəziyyətdə qalır, siz bunu istifadə edərək konfiqurasiya edə bilərsiniz. dirty_expire_centisecs; burada Bununla bağlı əlavə materiallar tapa bilərsiniz). Bu o deməkdir ki, uğursuzluqdan sonra böyük miqdarda məlumat geri qaytarıla bilməyəcək şəkildə itirilə bilər. Bu problemin həlli istifadə etməkdir fsync() sabit məlumatların saxlanmasını təmin etməli və onları nasazlıqların nəticələrindən mümkün qədər qorumalı olan tətbiqlərdə. Funksiya fsync() ext4 istifadə edərkən ext3 istifadə edərkən daha səmərəli işləyir. Bu yanaşmanın mənfi cəhəti ondan ibarətdir ki, onun istifadəsi əvvəlki kimi bəzi əməliyyatların, məsələn, proqramların quraşdırılmasının icrasını ləngidir. Bu barədə təfərrüata baxın burada и burada.

ilə bağlı üçüncü problem fsync(), 2018-ci ildə yaranmışdır. Daha sonra PostgreSQL layihəsi çərçivəsində funksiyanın əgər olduğu müəyyən edilmişdir fsync() xəta ilə qarşılaşdıqda, "çirkli" səhifələri "təmiz" olaraq qeyd edir. Nəticədə, aşağıdakı zənglər fsync() Belə səhifələrlə heç nə etmirlər. Bu səbəbdən dəyişdirilmiş səhifələr yaddaşda saxlanılır və heç vaxt diskə yazılmır. Bu, əsl fəlakətdir, çünki proqram bəzi məlumatların diskə yazıldığını düşünəcək, amma əslində olmayacaq. Belə uğursuzluqlar fsync() nadirdir, belə vəziyyətlərdə tətbiq problemlə mübarizə aparmaq üçün demək olar ki, heç nə edə bilməz. Bu günlərdə, bu baş verdikdə, PostgreSQL və digər proqramlar sıradan çıxır. Burada, “Tətbiqlər fsync xətalarından bərpa edə bilərmi?” materialında bu problem ətraflı şəkildə araşdırılır. Hal-hazırda bu problemin ən yaxşı həlli birbaşa I/O-dan bayraqla istifadə etməkdir O_SYNC ya da bayraqla O_DSYNC. Bu yanaşma ilə sistem xüsusi yazma əməliyyatları zamanı baş verə biləcək səhvlər barədə məlumat verəcək, lakin bu yanaşma tətbiqdən tamponların özünü idarə etməsini tələb edir. Bu barədə ətraflı oxuyun burada и burada.

O_SYNC və O_DSYNC bayraqlarından istifadə edərək faylların açılması

Sabit məlumatların saxlanmasını təmin edən Linux mexanizmlərinin müzakirəsinə qayıdaq. Daha doğrusu, bayraqdan istifadə etməkdən danışırıq O_SYNC və ya bayraq O_DSYNC sistem zəngindən istifadə edərək faylları açarkən açıq(). Bu yanaşma ilə hər bir məlumat yazma əməliyyatı sanki hər əmrdən sonra yerinə yetirilir write() sistemə müvafiq əmrlər verilir fsync() и fdatasync(). Ilə POSIX spesifikasiyası bu, "Sinxronlaşdırılmış I/O Fayl Bütövlüyünün Tamamlanması" və "Məlumat Bütövlüyünün Tamamlanması" adlanır. Bu yanaşmanın əsas üstünlüyü ondan ibarətdir ki, məlumatların bütövlüyünü təmin etmək üçün iki deyil, yalnız bir sistem zəngi etməlisiniz (məsələn - write() и fdatasync()). Bu yanaşmanın əsas çatışmazlığı ondan ibarətdir ki, müvafiq fayl deskriptorundan istifadə edən bütün yazılar sinxronlaşdırılacaq və bu, proqram kodunu strukturlaşdırmaq imkanını məhdudlaşdıra bilər.

O_DIRECT bayrağı ilə birbaşa I/O-dan istifadə

Sistem zəngi open() bayrağı dəstəkləyir O_DIRECT, disklə bilavasitə qarşılıqlı əlaqə quraraq I/O əməliyyatlarını yerinə yetirmək üçün əməliyyat sisteminin keşini yan keçmək üçün nəzərdə tutulmuşdur. Bu, bir çox hallarda proqram tərəfindən verilən yazma əmrlərinin birbaşa disklə işləməyə yönəlmiş əmrlərə çevriləcəyini bildirir. Lakin, ümumiyyətlə, bu mexanizm funksiyaları əvəz etmir fsync() və ya fdatasync(). Fakt budur ki, disk özü edə bilər təxirə salın və ya önbelleğe alın müvafiq məlumat yazma əmrləri. Və vəziyyəti daha da pisləşdirmək üçün, bəzi xüsusi hallarda bayraqdan istifadə edərkən I/O əməliyyatları yerinə yetirilir O_DIRECT, yayım ənənəvi tamponlu əməliyyatlara. Bu problemi həll etməyin ən asan yolu faylları açmaq üçün bayraqdan istifadə etməkdir O_DSYNC, bu o deməkdir ki, hər bir yazma əməliyyatından sonra zəng gələcək fdatasync().

Məlum oldu ki, XFS fayl sistemi bu yaxınlarda üçün "sürətli yol" əlavə edib O_DIRECT|O_DSYNC-məlumatların qeydə alınması. Bir blok istifadə edərək yenidən yazılırsa O_DIRECT|O_DSYNC, onda XFS, önbelleği təmizləmək əvəzinə, cihaz onu dəstəkləyirsə, FUA yazma əmrini yerinə yetirəcək. Bunu köməkçi proqramdan istifadə edərək təsdiqlədim blktrace Linux 5.4/Ubuntu 20.04 sistemində. Bu yanaşma daha səmərəli olmalıdır, çünki istifadə olunduqda diskə minimal miqdarda məlumat yazılır və iki əməliyyatdan (keşi yazmaq və təmizləmək) deyil, bir əməliyyatdan istifadə olunur. linki tapdım yamaq Bu mexanizmi həyata keçirən 2018 kernel. Bu optimallaşdırmanın digər fayl sistemlərinə tətbiqi ilə bağlı bəzi müzakirələr var, lakin bildiyimə görə, XFS indiyə qədər bunu dəstəkləyən yeganə fayl sistemidir.

sync_file_range() funksiyası

Linux-un sistem çağırışı var sync_file_range(), bu, bütün faylı deyil, faylın yalnız bir hissəsini diskə silməyə imkan verir. Bu zəng asinxron məlumatların yuyulmasına başlayır və onun tamamlanmasını gözləmir. Ancaq sertifikatda sync_file_range() komandanın "çox təhlükəli" olduğu deyilir. Onu istifadə etmək tövsiyə edilmir. Xüsusiyyətlər və təhlükələr sync_file_range() çox yaxşı təsvir edilmişdir bu material. Xüsusilə, bu zəng nüvənin çirkli məlumatları diskə nə vaxt sildiyini idarə etmək üçün RocksDB-dən istifadə edir. Ancaq eyni zamanda, sabit məlumatların saxlanmasını təmin etmək üçün ondan da istifadə olunur fdatasync(). Ilə kod RocksDB-nin bu mövzuda maraqlı şərhləri var. Məsələn, görünür ki, zəng sync_file_range() ZFS-dən istifadə edərkən o, məlumatları diskə köçürmür. Təcrübə mənə deyir ki, nadir hallarda istifadə olunan kodun səhvləri ola bilər. Buna görə də, çox ehtiyac olmadıqca bu sistem çağırışından istifadə etməməyi məsləhət görərdim.

Məlumatların davamlılığını təmin etməyə kömək edən sistem zəngləri

Mən məlumatların davamlılığını təmin edən I/O əməliyyatlarını yerinə yetirmək üçün istifadə edilə bilən üç yanaşmanın olduğu qənaətinə gəldim. Onların hamısı funksiya çağırışı tələb edir fsync() faylın yaradıldığı qovluq üçün. Bu yanaşmalar:

  1. Funksiya çağırışı fdatasync() və ya fsync() funksiyadan sonra write() (istifadə etmək daha yaxşıdır fdatasync()).
  2. Bayraqla açılan fayl deskriptoru ilə işləmək O_DSYNC və ya O_SYNC (daha yaxşı - bayraq ilə O_DSYNC).
  3. Komandadan istifadə etməklə pwritev2() bayraqla RWF_DSYNC və ya RWF_SYNC (tercihen bayraqla RWF_DSYNC).

Performans Qeydləri

Mən araşdırdığım müxtəlif mexanizmlərin işini diqqətlə ölçməmişəm. Onların iş sürətində müşahidə etdiyim fərqlər çox azdır. Bu o deməkdir ki, mən səhv edə bilərəm və fərqli şərtlərdə eyni şey fərqli nəticələr verə bilər. Əvvəlcə performansa nəyin daha çox təsir etdiyini, sonra isə performansa nəyin daha az təsir etdiyini danışacağam.

  1. Fayl məlumatlarının üzərinə yazmaq fayla məlumat əlavə etməkdən daha sürətlidir (performans faydası 2-100% ola bilər). Fayla məlumat əlavə etmək hətta sistem çağırışından sonra da faylın metadatasına əlavə dəyişikliklər tələb edir fallocate(), lakin bu təsirin miqyası fərqli ola bilər. Ən yaxşı performans üçün zəng etməyi tövsiyə edirəm fallocate() tələb olunan yerin əvvəlcədən ayrılması. Sonra bu boşluq açıq şəkildə sıfırlarla doldurulmalı və çağırılmalıdır fsync(). Bu, fayl sistemindəki müvafiq blokların "bölünməmiş" deyil, "ayrılmış" kimi qeyd olunmasını təmin edəcəkdir. Bu, kiçik (təxminən 2%) performans yaxşılaşması verir. Bundan əlavə, bəzi disklərin bloka ilk girişi digərlərinə nisbətən daha yavaş ola bilər. Bu o deməkdir ki, boşluğu sıfırlarla doldurmaq performansın əhəmiyyətli dərəcədə yaxşılaşmasına (təxminən 100%) səbəb ola bilər. Xüsusilə, bu disklərdə baş verə bilər AWS EBS (bu qeyri-rəsmi məlumatdır, təsdiq edə bilmədim). Eyni şey saxlama üçün də gedir GCP Persistent Disk (və bu artıq rəsmi məlumatdır, testlərlə təsdiqlənir). Digər ekspertlər də eyni şeyi etdilər müşahidələr, müxtəlif disklərlə əlaqəli.
  2. Sistem zəngləri nə qədər az olsa, performans bir o qədər yüksək olar (qazanc təxminən 5% ola bilər). Çağırış kimi görünür open() bayraqla O_DSYNC və ya zəng edin pwritev2() bayraqla RWF_SYNC zəngdən daha sürətli fdatasync(). Mən şübhələnirəm ki, burada əsas məqam ondan ibarətdir ki, bu yanaşma eyni problemi həll etmək üçün daha az sistem çağırışının yerinə yetirilməli olmasında rol oynayır (iki əvəzinə bir zəng). Ancaq performans fərqi çox kiçikdir, buna görə də onu tamamilə görməməzliyə vura və tətbiqdə onun məntiqini çətinləşdirməyəcək bir şey istifadə edə bilərsiniz.

Davamlı məlumatların saxlanması mövzusu ilə maraqlanırsınızsa, burada bəzi faydalı materiallar var:

  • I/O Giriş üsulları — giriş/çıxış mexanizmlərinin əsaslarına ümumi baxış.
  • Verilənlərin diskə çatmasını təmin etmək — proqramdan diskə gedən yolda verilənlərlə nə baş verdiyi haqqında hekayə.
  • Tərkibindəki kataloqu nə vaxt sinxronizasiya etməlisiniz - nə vaxt istifadə etmək sualına cavab fsync() kataloqlar üçün. Bunu qısaca desək, belə çıxır ki, yeni fayl yaratarkən bunu etmək lazımdır və bu tövsiyənin səbəbi Linux-da eyni fayla çoxlu istinadların ola biləcəyidir.
  • Linux-da SQL Server: FUA Daxili — burada Linux platformasında SQL Server-də davamlı məlumat saxlanmasının necə həyata keçirildiyinin təsviri verilmişdir. Burada Windows və Linux sistem zəngləri arasında maraqlı müqayisələr var. Demək olar ki, əminəm ki, məhz bu material sayəsində XFS-nin FUA optimallaşdırılmasını öyrəndim.

Diskdə təhlükəsiz saxlandığını düşündüyünüz məlumatları itirmisiniz?

Davamlı Məlumat Saxlama və Linux Fayl API-ləri

Davamlı Məlumat Saxlama və Linux Fayl API-ləri

Mənbə: www.habr.com