Երկարատև տվյալների պահպանման և Linux ֆայլերի API-ներ
Ամպային համակարգերում տվյալների պահպանման կայունությունը ուսումնասիրելիս ես որոշեցի ինքս ինձ փորձարկել՝ համոզվելու համար, որ հասկանում եմ հիմնական բաները: Ի սկսվեց կարդալով NVMe բնութագրերը որպեսզի հասկանանք, թե ինչ երաշխիքներ են տալիս տվյալների կայուն պահպանման վերաբերյալ (այսինքն՝ երաշխիքներ, որ տվյալները հասանելի կլինեն համակարգի խափանումից հետո) մեզ տալիս են NMVe սկավառակներ: Ես արեցի հետևյալ հիմնական եզրակացությունները՝ տվյալները պետք է վնասված համարվեն տվյալների գրելու հրամանի տրվելու պահից մինչև այն պահը, երբ դրանք գրվում են պահեստային միջավայրում։ Այնուամենայնիվ, ծրագրերի մեծ մասը հաճույքով օգտագործում է համակարգային զանգեր տվյալների ձայնագրման համար:
Այս գրառման մեջ ես ուսումնասիրում եմ Linux ֆայլերի API-ների կողմից տրամադրված մշտական պահպանման մեխանիզմները: Թվում է, թե այստեղ ամեն ինչ պետք է պարզ լինի. ծրագիրը կանչում է հրամանը write(), և այս հրամանի ավարտից հետո տվյալները ապահով կերպով կպահվեն սկավառակի վրա: Բայց write() միայն պատճենում է հավելվածի տվյալները RAM-ում տեղակայված միջուկի քեշին: Համակարգին ստիպելու համար գրել տվյալներ սկավառակի վրա, դուք պետք է օգտագործեք լրացուցիչ մեխանիզմներ:
Ընդհանուր առմամբ, այս նյութը նշումների հավաքածու է, որը վերաբերում է այն ամենին, ինչ ես սովորել եմ ինձ հետաքրքրող թեմայի վերաբերյալ: Եթե շատ հակիրճ խոսենք ամենակարևոր բանի մասին, ապա պարզվում է, որ տվյալների կայուն պահեստավորում կազմակերպելու համար անհրաժեշտ է օգտագործել հրամանը. fdatasync() կամ բացել ֆայլերը դրոշով O_DSYNC. Եթե դուք հետաքրքրված եք ավելին իմանալու մասին, թե ինչ է տեղի ունենում տվյալների հետ կոդից սկավառակ ճանապարհին, նայեք սա է հոդված.
Write() ֆունկցիայի օգտագործման առանձնահատկությունները
Համակարգային զանգ write() սահմանված ստանդարտում IEEE POSIX որպես ֆայլի նկարագրիչի վրա տվյալներ գրելու փորձ: Հաջող ավարտից հետո write() Տվյալների ընթերցման գործողությունները պետք է վերադարձնեն հենց այն բայթերը, որոնք նախկինում գրվել են, դա անելով նույնիսկ այն դեպքում, եթե տվյալները հասանելի են այլ գործընթացներից կամ թելերից (այստեղ POSIX ստանդարտի համապատասխան բաժինը): Այստեղ, այն բաժնում, թե ինչպես են շղթաները փոխազդում ֆայլի սովորական գործողությունների հետ, կա նշում, որն ասում է, որ եթե երկու շղթաներից յուրաքանչյուրը կանչում է այս գործառույթները, ապա յուրաքանչյուր զանգ պետք է տեսնի կամ մյուս զանգի բոլոր նշանակված հետևանքները, կամ ընդհանրապես ոչ մեկը: հետեւանքները. Սա հանգեցնում է այն եզրակացության, որ ֆայլի մուտքի/ելքի բոլոր գործողությունները պետք է կողպված լինեն ռեսուրսի վրա, որի վրա նրանք աշխատում են:
Արդյո՞ք սա նշանակում է, որ վիրահատությունը write() ատոմային է? Տեխնիկական տեսանկյունից՝ այո։ Տվյալների ընթերցման գործողությունները պետք է վերադարձնեն կամ ամբողջը կամ ոչինչ, ինչով գրված է write(). Բայց վիրահատությունը write()Ըստ ստանդարտի, պարտադիր չէ, որ ավարտվի այն ամենը, ինչ պահանջվել է գրել: Նրան թույլատրվում է գրել տվյալների միայն մի մասը: Օրինակ, մենք կարող ենք ունենալ երկու շղթա, որոնցից յուրաքանչյուրը 1024 բայթ ավելացնում է նույն ֆայլի նկարագրությամբ նկարագրված ֆայլին: Ստանդարտի տեսանկյունից ընդունելի արդյունք կլինի, երբ գրելու յուրաքանչյուր գործողություն կարող է ֆայլին ավելացնել միայն մեկ բայթ: Այս գործողությունները կմնան ատոմային, բայց դրանք ավարտվելուց հետո այն տվյալները, որոնք նրանք գրել են ֆայլում, կխառնվեն: Այստեղ շատ հետաքրքիր քննարկում այս թեմայի շուրջ Stack Overflow-ում:
fsync() և fdatasync() ֆունկցիաները
Տվյալները սկավառակի վրա լցնելու ամենահեշտ ձևը ֆունկցիան կանչելն է fsync (). Այս գործառույթը խնդրում է օպերացիոն համակարգին փոխանցել բոլոր փոփոխված բլոկները քեշից սկավառակ: Սա ներառում է ֆայլի բոլոր մետատվյալները (մուտքի ժամանակը, ֆայլի փոփոխման ժամանակը և այլն): Կարծում եմ, որ այս մետատվյալները հազվադեպ են անհրաժեշտ, այնպես որ, եթե գիտեք, որ դա ձեզ համար կարևոր չէ, կարող եք օգտագործել գործառույթը fdatasync(): Մեջ Օգնություն մասին fdatasync() Ասվում է, որ այս ֆունկցիայի գործարկման ժամանակ սկավառակի վրա պահվում է մետատվյալների այնպիսի քանակություն, որը «անհրաժեշտ է տվյալների ընթերցման հետևյալ գործողությունների ճիշտ կատարման համար»։ Եվ սա հենց այն է, ինչի մասին հոգ է տանում հավելվածների մեծ մասը:
Խնդիրներից մեկը, որը կարող է առաջանալ այստեղ, այն է, որ այս մեխանիզմները չեն երաշխավորում, որ ֆայլը հայտնաբերելի կլինի հնարավոր ձախողումից հետո: Մասնավորապես, նոր ֆայլ ստեղծելիս պետք է զանգահարել fsync() այն պարունակող գրացուցակի համար: Հակառակ դեպքում, ձախողումից հետո կարող է պարզվել, որ այս ֆայլը գոյություն չունի: Սրա պատճառն այն է, որ UNIX-ում, կոշտ հղումների օգտագործման պատճառով, ֆայլը կարող է գոյություն ունենալ բազմաթիվ գրացուցակներում: Հետեւաբար, զանգահարելիս fsync() ոչ մի կերպ ֆայլը չի կարող իմանալ, թե որ գրացուցակի տվյալները նույնպես պետք է լցվեն սկավառակի վրա (այստեղ Այս մասին կարող եք կարդալ ավելին): Կարծես թե ext4 ֆայլային համակարգը ունակ է ավտոմատ դիմել fsync() համապատասխան ֆայլեր պարունակող դիրեկտորիաներին, սակայն այլ ֆայլային համակարգերի դեպքում դա չի կարող լինել:
Այս մեխանիզմը կարող է տարբեր կերպ կիրառվել տարբեր ֆայլային համակարգերում: ես օգտագործել եմ blktrace իմանալ, թե սկավառակի ինչ գործառնություններ են օգտագործվում ext4 և XFS ֆայլային համակարգերում: Երկուսն էլ թողարկում են սկավառակի վրա կանոնավոր գրելու հրամաններ և՛ ֆայլի բովանդակության, և՛ ֆայլային համակարգի ամսագրի համար, մաքրում են քեշը և դուրս են գալիս՝ կատարելով FUA (Force Unit Access, տվյալներ անմիջապես սկավառակի վրա գրելը, քեշը շրջանցելով) գրել ամսագրում: Հավանաբար դա անում են, որպեսզի հաստատեն, որ գործարքը կայացել է։ Սկավառակների վրա, որոնք չեն աջակցում FUA-ին, դա առաջացնում է քեշի երկու ողողում: Իմ փորձերը դա ցույց տվեցին fdatasync() մի քիչ ավելի արագ fsync(). Կոմունալ blktrace ցույց է տալիս, որ fdatasync() սովորաբար ավելի քիչ տվյալներ է գրում սկավառակի վրա (ext4 fsync() գրում է 20 KiB, իսկ fdatasync() - 16 ԿԲ): Բացի այդ, ես պարզեցի, որ XFS-ը մի փոքր ավելի արագ է, քան ext4-ը: Եվ ահա՝ օգնությամբ blktrace հաջողվել է պարզել դա fdatasync() ավելի քիչ տվյալներ է փոխանցում սկավառակի վրա (4 KiB XFS-ում):
Ոչ միանշանակ իրավիճակներ, որոնք առաջանում են 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-ի ձախողումներից» նյութում այս խնդիրը մանրամասն ուսումնասիրված է: Ներկայումս այս խնդրի լավագույն լուծումը դրոշակով Direct I/O-ի օգտագործումն է O_SYNC կամ դրոշակով O_DSYNC. Այս մոտեցմամբ համակարգը կզեկուցի սխալների մասին, որոնք կարող են առաջանալ գրելու հատուկ գործողությունների ժամանակ, սակայն այս մոտեցումը պահանջում է, որ հավելվածն ինքը կառավարի բուֆերները: Կարդացեք ավելին այս մասին այստեղ и այստեղ.
Ֆայլերի բացում՝ օգտագործելով O_SYNC և O_DSYNC դրոշները
Վերադառնանք Linux մեխանիզմների քննարկմանը, որոնք ապահովում են տվյալների կայուն պահպանում։ Խոսքը, մասնավորապես, դրոշն օգտագործելու մասին է O_SYNC կամ դրոշ O_DSYNC համակարգային զանգի միջոցով ֆայլեր բացելիս բաց (). Այս մոտեցմամբ տվյալների գրման յուրաքանչյուր գործողություն կատարվում է այնպես, կարծես յուրաքանչյուր հրամանից հետո write() Համակարգին համապատասխան հրամաններ են տրվում fsync() и fdatasync(): Մեջ POSIX բնութագրերը սա կոչվում է «Synchronized I/O File Integrity Completion» և «Data Integrity Completion»: Այս մոտեցման հիմնական առավելությունն այն է, որ տվյալների ամբողջականությունն ապահովելու համար անհրաժեշտ է կատարել միայն մեկ համակարգային զանգ, այլ ոչ թե երկու (օրինակ՝ write() и fdatasync()). Այս մոտեցման հիմնական թերությունն այն է, որ համապատասխան ֆայլի նկարագրիչ օգտագործող բոլոր գրությունները կհամաժամանակացվեն, ինչը կարող է սահմանափակել հավելվածի կոդը կառուցվածքավորելու հնարավորությունը:
Օգտագործելով Direct I/O O_DIRECT դրոշակով
Համակարգային զանգ open() աջակցում է դրոշը O_DIRECT, որը նախատեսված է օպերացիոն համակարգի քեշը շրջանցելու համար՝ I/O գործողություններ կատարելու համար՝ ուղղակիորեն սկավառակի հետ փոխազդելով։ Սա շատ դեպքերում նշանակում է, որ ծրագրի կողմից թողարկված գրելու հրամաններն ուղղակիորեն կվերածվեն սկավառակի հետ աշխատելուն ուղղված հրամանների: Բայց, ընդհանուր առմամբ, այս մեխանիզմը ֆունկցիաներին փոխարինող չէ 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 համակարգի վրա: Այս մոտեցումը պետք է ավելի արդյունավետ լինի, քանի որ երբ օգտագործվում է, նվազագույն քանակությամբ տվյալներ են գրվում սկավառակի վրա և օգտագործվում է մեկ գործողություն, այլ ոչ թե երկու (քեշը գրելը և մաքրելը): Ես գտա հղումը դեպի patch 2018 միջուկը, որն իրականացնում է այս մեխանիզմը։ Այնտեղ որոշակի քննարկում կա այս օպտիմիզացումը այլ ֆայլային համակարգերի վրա նույնպես կիրառելու վերաբերյալ, բայց որքան ես գիտեմ, XFS-ը միակ ֆայլային համակարգն է, որն աջակցում է մինչ այժմ:
sync_file_range() ֆունկցիան
Linux-ն ունի համակարգային զանգ sync_file_range (), որը թույլ է տալիս ֆայլի միայն մի մասը տեղադրել սկավառակի վրա, այլ ոչ թե ամբողջ ֆայլը: Այս զանգը սկսում է տվյալների ասինխրոն մաքրում և չի սպասում դրա ավարտին: Բայց վկայականում sync_file_range() Ասում են, որ թիմը «շատ վտանգավոր է»: Խորհուրդ չի տրվում օգտագործել այն։ Առանձնահատկություններ և վտանգներ sync_file_range() շատ լավ նկարագրված է սա է նյութական. Մասնավորապես, այս զանգը, կարծես, օգտագործում է RocksDB-ն՝ վերահսկելու, երբ միջուկը կեղտոտ տվյալներ է թափում սկավառակի վրա: Բայց միևնույն ժամանակ տվյալների կայուն պահպանումն ապահովելու համար այն նաև օգտագործվում է fdatasync(): Մեջ կոդը RocksDB-ն այս թեմայի վերաբերյալ մի քանի հետաքրքիր մեկնաբանություն ունի: Օրինակ, թվում է, որ զանգը sync_file_range() ZFS-ն օգտագործելիս այն չի տեղափոխում տվյալները սկավառակի վրա: Փորձն ինձ ասում է, որ հազվադեպ օգտագործվող կոդը, հավանաբար, պարունակում է սխալներ: Հետևաբար, ես խորհուրդ կտայի չօգտագործել այս համակարգային զանգը, եթե խիստ անհրաժեշտություն չկա:
Համակարգային զանգեր, որոնք օգնում են ապահովել տվյալների կայունությունը
Ես եկել եմ այն եզրակացության, որ կան երեք մոտեցումներ, որոնք կարող են օգտագործվել I/O գործողություններ կատարելու համար, որոնք ապահովում են տվյալների կայունությունը: Նրանք բոլորը պահանջում են ֆունկցիայի կանչ 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(). Ես կասկածում եմ, որ խնդիրն այստեղ այն է, որ այս մոտեցումը դեր է խաղում այն փաստի մեջ, որ ավելի քիչ համակարգային զանգեր պետք է կատարվեն նույն խնդիրը լուծելու համար (մեկ զանգ երկուսի փոխարեն): Բայց կատարողականի տարբերությունը շատ փոքր է, այնպես որ դուք կարող եք ամբողջովին անտեսել այն և հավելվածում օգտագործել այնպիսի բան, որը չի բարդացնի դրա տրամաբանությունը:
Եթե ձեզ հետաքրքրում է տվյալների կայուն պահպանման թեման, ապա այստեղ կան մի քանի օգտակար նյութեր.
Ե՞րբ պետք է համաժամացնեք պարունակող գրացուցակը - հարցի պատասխանը, երբ օգտագործել fsync() գրացուցակների համար: Սա կարճ ասած, պարզվում է, որ դուք պետք է դա անեք նոր ֆայլ ստեղծելիս, և այս առաջարկության պատճառն այն է, որ Linux-ում կարող են լինել բազմաթիվ հղումներ նույն ֆայլին:
SQL Server Linux-ում. FUA Internals — ահա նկարագրությունը, թե ինչպես է մշտական տվյալների պահպանումն իրականացվում SQL Server-ում Linux հարթակում: Այստեղ կան մի քանի հետաքրքիր համեմատություններ Windows-ի և Linux-ի համակարգային զանգերի միջև: Ես գրեթե համոզված եմ, որ հենց այս նյութի շնորհիվ է, որ ես իմացա XFS-ի FUA օպտիմալացման մասին:
Դուք կորցրե՞լ եք տվյալները, որոնք կարծում էիք, որ ապահով կերպով պահված են սկավառակի վրա: