iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğu

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğu

2019-cu ilin payızında Mail.ru Cloud iOS komandasında çoxdan gözlənilən hadisə baş verdi. Proqram vəziyyətinin davamlı saxlanması üçün əsas məlumat bazası mobil dünya üçün olduqca ekzotik hala gəldi Lightning Memory-Mapped Database (LMDB). Kəsmə altında, diqqətinizi dörd hissədən ibarət ətraflı nəzərdən keçirməyə dəvət edirik. Əvvəlcə belə qeyri-trivial və çətin seçimin səbəblərindən danışaq. Sonra gəlin LMDB arxitekturasının mərkəzində duran üç balinanın nəzərdən keçirilməsinə keçək: yaddaşa uyğunlaşdırılmış fayllar, B + ağacı, tranzaksiya və multiversiyanı həyata keçirmək üçün kopyalama-on-yazma yanaşması. Nəhayət, desert üçün - praktik hissəsi. Burada biz aşağı səviyyəli açar-dəyər API-nin üstündə bir indeks də daxil olmaqla bir neçə cədvəldən ibarət əsas sxemin necə tərtib ediləcəyinə və həyata keçirilməsinə baxacağıq.

Məzmun

  1. İcra Motivasiyası
  2. LMDB-nin yerləşdirilməsi
  3. Üç balina LMDB
    3.1. Balina №1. Yaddaşa uyğunlaşdırılmış fayllar
    3.2. Balina # 2. B+-ağac
    3.3. Balina #3. surəti-yazılı
  4. Açar-dəyər API-nin üstündə məlumat sxeminin dizaynı
    4.1. Əsas abstraksiyalar
    4.2. Cədvəl modelləşdirmə
    4.3. Cədvəllər arasında əlaqələrin modelləşdirilməsi

1. İcra motivasiyası

İldə bir dəfə, 2015-ci ildə, tətbiqimizin interfeysinin nə qədər tez-tez gecikdiyinə dair bir metrik götürməyə diqqət yetirdik. Biz bunu sadəcə etməmişik. Bəzən tətbiqin istifadəçi hərəkətlərinə cavab verməməsi ilə bağlı daha çox şikayətlərimiz var: düymələr basılmır, siyahılar sürüşmür və s. Ölçmələrin mexanikası haqqında deyə danışdı AvitoTech-də, buna görə də burada yalnız nömrələrin sırasını verirəm.

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğu

Ölçmə nəticələri bizim üçün soyuq bir duş oldu. Məlum oldu ki, donmaların yaratdığı problemlər digərlərindən qat-qat çoxdur. Əgər bu faktı dərk etməzdən əvvəl keyfiyyətin əsas texniki göstəricisi qəzasız idisə, diqqətdən sonra dəyişdi donma pulsuz.

Quraraq donma ilə tablosuna və sərf etdik kəmiyyət и keyfiyyət onların səbəbləri təhlili, əsas düşmən aydın oldu - proqram əsas mövzu icra ağır iş məntiqi. Bu biabırçılığa təbii reaksiya onu iş axınına sövq etmək istəyi idi. Bu problemin sistemli həlli üçün biz yüngül aktyorlara əsaslanan çox yivli arxitekturaya müraciət etdik. Mən onun uyğunlaşmalarını iOS dünyasına həsr etdim iki ip kollektiv twitter və Habré haqqında məqalə. Mövcud hekayənin bir hissəsi olaraq, verilənlər bazası seçiminə təsir edən qərarın aspektlərini vurğulamaq istəyirəm.

Sistemin təşkilinin aktyor modeli güman edir ki, çoxillik onun ikinci mahiyyətinə çevrilir. İçindəki model obyektlər ip sərhədlərini keçməyi xoşlayır. Və bunu bəzən və bəzi yerlərdə deyil, demək olar ki, daim və hər yerdə edirlər.

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğu

Verilənlər bazası təqdim olunan diaqramda təməl daşı komponentlərindən biridir. Onun əsas vəzifəsi makro nümunəni həyata keçirməkdir Paylaşılan verilənlər bazası. Müəssisə dünyasında xidmətlər arasında məlumatların sinxronizasiyasını təşkil etmək üçün istifadə olunursa, aktyor arxitekturasında mövzular arasında məlumatlar. Beləliklə, çox yivli mühitdə işləmək hətta minimal çətinliklərə səbəb olmayan belə bir verilənlər bazasına ehtiyacımız var idi. Xüsusilə, bu o deməkdir ki, ondan əldə edilən obyektlər ən azı ipdən qorunmalıdır və ideal olaraq heç dəyişilməməlidir. Bildiyiniz kimi, sonuncu hər hansı bir qıfıllara müraciət etmədən eyni vaxtda bir neçə ipdən istifadə edilə bilər ki, bu da performansa faydalı təsir göstərir.

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğuVerilənlər bazası seçiminə təsir edən ikinci mühüm amil bulud API-miz oldu. Sinxronizasiyaya git yanaşmasından ilhamlandı. Onun kimi biz də hədəf aldıq oflayn ilk API, bulud müştəriləri üçün daha uyğun görünür. Güman edilirdi ki, onlar yalnız bir dəfə buludun tam vəziyyətini çıxaracaqlar, sonra isə əksər hallarda sinxronizasiya yuvarlanan dəyişikliklərlə baş verəcək. Təəssüf ki, bu imkan hələ də yalnız nəzəri zonadadır və praktikada müştərilər yamaqlarla işləməyi öyrənməyiblər. Bunun bir sıra obyektiv səbəbləri var ki, təqdimatı gecikdirməmək üçün mötərizədən kənarda qalacağıq. API "A" dedikdə və onun istehlakçısı "B" demədikdə nə baş verdiyinə dair dərsin ibrətamiz nəticələri indi daha maraqlıdır.

Beləliklə, bir çəkmə əmrini yerinə yetirərkən, yerli bir şəkilə yamaqlar tətbiq etmək əvəzinə, tam vəziyyətini tam server ilə müqayisə edən git-i təsəvvür etsəniz, sinxronizasiyanın necə olacağı barədə kifayət qədər dəqiq bir fikrə sahib olacaqsınız. bulud müştərilərində baş verir. Təxmin etmək asandır ki, onun həyata keçirilməsi üçün yaddaşda bütün server və yerli fayllar haqqında meta-məlumatla iki DOM ağacı ayırmaq lazımdır. Belə çıxır ki, əgər istifadəçi buludda 500 min fayl saxlayırsa, o zaman onu sinxronlaşdırmaq üçün 1 milyon qovşağı olan iki ağacı yenidən yaratmaq və məhv etmək lazımdır. Lakin hər bir qovşaq alt obyektlərin qrafikini ehtiva edən məcmudur. Bu baxımdan, profilləşdirmə nəticələri gözlənilirdi. Məlum oldu ki, birləşmə alqoritmini nəzərə almadan belə, çoxlu sayda kiçik obyektlərin yaradılması və sonra məhv edilməsi prosedurunun özü kifayət qədər qəpik baha başa gəlir.Vəziyyəti əsas sinxronizasiya əməliyyatının çoxlu sayda daxil olması ilə daha da ağırlaşdırır. istifadəçi skriptləri. Nəticədə, biz verilənlər bazası seçimində ikinci vacib meyarı - obyektlərin dinamik ayrılması olmadan CRUD əməliyyatlarını həyata keçirmək qabiliyyətini müəyyənləşdiririk.

Digər tələblər daha ənənəvidir və onların tam siyahısı aşağıdakı kimidir.

  1. İp təhlükəsizliyi.
  2. Çox emal. Vəziyyəti yalnız mövzular arasında deyil, həm də əsas proqram və iOS uzantıları arasında sinxronlaşdırmaq üçün eyni verilənlər bazası nümunəsindən istifadə etmək istəyi ilə diktə edilir.
  3. Saxlanılan obyektləri dəyişməyən obyektlər kimi təqdim etmək imkanı
  4. CRUD əməliyyatları daxilində dinamik ayırmaların olmaması.
  5. Əsas Xüsusiyyətlər üçün Tranzaksiya Dəstəyi ACIDAçar sözlər: atomiklik, tutarlılıq, izolyasiya və etibarlılıq.
  6. Ən populyar hallarda sürət.

Bu tələblər dəsti ilə SQLite yaxşı seçim idi və hələ də belədir. Ancaq alternativlərin öyrənilməsi çərçivəsində bir kitabla rastlaşdım "LevelDB ilə işə başlamaq". Onun rəhbərliyi altında real bulud ssenarilərində müxtəlif verilənlər bazaları ilə iş sürətini müqayisə edən etalon yazılmışdır. Nəticə ən vəhşi gözləntiləri aşdı. Ən populyar hallarda - bütün faylların çeşidlənmiş siyahısında kursorun alınması və müəyyən bir kataloq üçün bütün faylların çeşidlənmiş siyahısı - LMDB SQLite-dən 10 dəfə daha sürətli olduğu ortaya çıxdı. Seçim aydın oldu.

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğu

2. LMDB Yerləşdirmə

LMDB çox kiçik bir kitabxanadır (yalnız 10K sətir), verilənlər bazalarının ən aşağı fundamental təbəqəsini - yaddaşı həyata keçirir.

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğu

Yuxarıdakı diaqram göstərir ki, LMDB-ni daha yüksək səviyyələri həyata keçirən SQLite ilə müqayisə etmək, ümumiyyətlə, Core Data ilə SQLite-dən daha düzgün deyil. Eyni saxlama mühərriklərini bərabər rəqiblər kimi göstərmək daha ədalətli olardı - BerkeleyDB, LevelDB, Sophia, RocksDB və s. Hətta LMDB-nin SQLite üçün yaddaş mühərriki komponenti kimi çıxış etdiyi inkişaflar var. İlk belə təcrübə 2012-ci ildə sərf etdi müəllif LMDB Hovard Çu. Tapıntılar o qədər maraqlı oldu ki, onun təşəbbüsü OSS həvəskarları tərəfindən götürüldü və öz davamını LumoSQL. 2020-ci ilin yanvar ayında bu layihənin müəllifi Den Şirerdir təqdim etdi LinuxConfAu-da.

LMDB-nin əsas istifadəsi proqram verilənlər bazası üçün mühərrik kimidir. Kitabxana öz görünüşünə görə tərtibatçılara borcludur OpenLDAPLayihələrinin əsası kimi BerkeleyDB-dən qəti şəkildə narazı olan . Təvazökar kitabxanadan uzaqlaşmaq btree, Howard Chu dövrümüzün ən məşhur alternativlərindən birini yarada bildi. O, çox gözəl hesabatını bu hekayəyə, eləcə də LMDB-nin daxili strukturuna həsr etdi. "The Lightning Memory-Mapped Database". Leonid Yuriev (aka yleo) Highload 2015-dəki çıxışında Positive Technologies-dən "LMDB mühərriki xüsusi çempiondur". O, ReOpenLDAP-ın tətbiqi ilə bağlı oxşar tapşırıq kontekstində LMDB haqqında danışır və LevelDB artıq müqayisəli tənqidlərə məruz qalıb. Tətbiq nəticəsində Positive Technologies hətta fəal inkişaf edən çəngəl əldə etdi MDBX çox dadlı xüsusiyyətləri, optimallaşdırmaları və səhv düzəlişləri.

LMDB tez-tez yaddaş kimi də istifadə olunur. Məsələn, Mozilla Firefox brauzeri seçdi bir sıra ehtiyaclar üçün və 9-cu versiyadan başlayaraq Xcode üstünlük verilir indeksləri saxlamaq üçün onun SQLite.

Mühərrik həmçinin mobil inkişaf dünyasında da öz yerini tutdu. Onun istifadəsinin izləri ola bilər tapmaq Telegram üçün iOS müştərisində. LinkedIn bir addım daha irəli getdi və LMDB-ni özünün yerli məlumat keşləmə çərçivəsi, Rocket Data üçün standart yaddaş olaraq seçdi. dedi 2016-cı ildə bir məqalədə.

LMDB Oracle-ın nəzarəti altında keçiddən sonra BerkeleyDB-nin buraxdığı nişdə günəşdə yer almaq üçün uğurla mübarizə aparır. Kitabxana öz növü ilə müqayisədə belə sürəti və etibarlılığı ilə sevilir. Bildiyiniz kimi, pulsuz nahar yoxdur və mən LMDB və SQLite arasında seçim edərkən qarşılaşmalı olduğunuz güzəşti vurğulamaq istərdim. Yuxarıdakı diaqram artan sürətə necə nail olunduğunu aydın şəkildə nümayiş etdirir. Birincisi, disk yaddaşının üstündə əlavə abstraksiya təbəqələri üçün ödəniş etmirik. Əlbəttə ki, yaxşı bir arxitekturada hələ də onlarsız edə bilməzsiniz və onlar qaçılmaz olaraq tətbiq kodunda görünəcəklər, lakin daha incə olacaqlar. Onların xüsusi proqram tərəfindən tələb olunmayan funksiyaları olmayacaq, məsələn, SQL dilində sorğular üçün dəstək. İkincisi, tətbiq əməliyyatlarının disk saxlama sorğularına uyğunlaşdırılmasını optimal şəkildə həyata keçirmək mümkün olur. Əgər SQLite mənim işimdə orta proqramın orta ehtiyaclarından irəli gəlir, onda siz proqram tərtibatçısı kimi əsas yükləmə ssenarilərini yaxşı bilirsiniz. Daha məhsuldar bir həll üçün həm ilkin həllin inkişafı, həm də sonrakı dəstək üçün artan qiymət etiketi ödəməli olacaqsınız.

3. Üç balina LMDB

LMDB-yə quş baxışı ilə baxdıqdan sonra daha dərinə getməyin vaxtı gəldi. Növbəti üç bölmə saxlama arxitekturasının dayandığı əsas balinaların təhlilinə həsr olunacaq:

  1. Disklə işləmək və daxili məlumat strukturlarının sinxronizasiyası mexanizmi kimi yaddaşa uyğunlaşdırılmış fayllar.
  2. B+-ağac saxlanılan verilənlər strukturunun təşkilatı kimi.
  3. ACID əməliyyat xüsusiyyətlərini və çoxversiyanı təmin etmək üçün bir yanaşma kimi kopyala-yazma.

3.1. Balina №1. Yaddaşa uyğunlaşdırılmış fayllar

Yaddaşla əlaqələndirilmiş fayllar o qədər vacib memarlıq elementidir ki, onlar hətta deponun adında da görünür. Saxlanılan məlumatlara girişin keşləşdirilməsi və sinxronizasiyası məsələləri tamamilə əməliyyat sisteminin ixtiyarındadır. LMDB özündə heç bir önbelleğe malik deyil. Bu, müəllif tərəfindən şüurlu bir qərardır, çünki birbaşa xəritələşdirilmiş fayllardan məlumatları oxumaq mühərrikin həyata keçirilməsində çoxlu küncləri kəsməyə imkan verir. Aşağıda onlardan bəzilərinin tam siyahısından uzaqdır.

  1. Bir neçə prosesdən işləyərkən yaddaşda verilənlərin ardıcıllığını qorumaq əməliyyat sisteminin məsuliyyətinə çevrilir. Növbəti hissədə bu mexanik ətraflı və şəkillərlə müzakirə olunur.
  2. Keşlərin olmaması LMDB-ni dinamik ayırmalarla bağlı yükdən tamamilə azad edir. Təcrübədə məlumatların oxunması göstəricini virtual yaddaşda düzgün ünvana təyin etməkdir və başqa heç nə yoxdur. Fantaziya kimi səslənir, lakin depo mənbəyində bütün calloc zəngləri depo konfiqurasiya funksiyasında cəmləşmişdir.
  3. Keşlərin olmaması həm də onlara daxil olmaq üçün sinxronizasiya ilə əlaqəli kilidlərin olmaması deməkdir. Eyni zamanda ixtiyari sayı mövcud ola bilən oxucular verilənlərə gedən yolda heç bir mutex ilə qarşılaşmırlar. Buna görə oxuma sürəti CPU sayı baxımından ideal xətti miqyaslılığa malikdir. LMDB-də yalnız dəyişdirmə əməliyyatları sinxronlaşdırılır. Eyni anda yalnız bir yazıçı ola bilər.
  4. Minimum keşləmə və sinxronizasiya məntiqi kodu çox yivli mühitdə işləməklə bağlı son dərəcə mürəkkəb tipli səhvlərdən xilas edir. Usenix OSDI 2014 konfransında iki maraqlı verilənlər bazası araşdırması oldu: "Bütün fayl sistemləri bərabər yaradılmayıb: qəzaya davamlı proqramların hazırlanmasının mürəkkəbliyi haqqında" и Əyləncə və Mənfəət üçün Verilənlər Bazalarına İşgəncə Vermək. Onlardan həm LMDB-nin görünməmiş etibarlılığı, həm də eyni SQLite-də onu üstələyən əməliyyatların ACID xassələrinin demək olar ki, qüsursuz həyata keçirilməsi haqqında məlumat əldə edə bilərsiniz.
  5. LMDB-nin minimalizmi onun kodunun maşın təsvirini prosessorun L1 yaddaş yaddaşına tam şəkildə yerləşdirməyə imkan verir ki, nəticədə yaranan sürət xüsusiyyətləri.

Təəssüf ki, iOS-da yaddaşla əlaqəli fayllar istədiyimiz qədər çəhrayı deyil. Onlarla əlaqəli çatışmazlıqlar haqqında daha şüurlu danışmaq üçün bu mexanizmin əməliyyat sistemlərində həyata keçirilməsinin ümumi prinsiplərini xatırlatmaq lazımdır.

Yaddaşa uyğunlaşdırılmış fayllar haqqında ümumi məlumat

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğuHər bir icra olunan proqramla əməliyyat sistemi proses adlanan obyekti əlaqələndirir. Hər bir prosesə işləmək üçün lazım olan hər şeyi yerləşdirdiyi bir sıra ünvanlar ayrılır. Ən aşağı ünvanlar kodu və sərt kodlu məlumat və resursları olan bölmələri ehtiva edir. Sonra bizə yığın kimi yaxşı məlum olan dinamik ünvan məkanının yuxarıya doğru böyüyən bloku gəlir. O, proqramın işləməsi zamanı görünən obyektlərin ünvanlarını ehtiva edir. Yuxarıda proqram yığını tərəfindən istifadə olunan yaddaş sahəsidir. Ya böyüyür, ya da kiçilir, başqa sözlə, ölçüsü də dinamik xarakter daşıyır. Yığın və yığın bir-birini itələməməsi və bir-birinə müdaxilə etməməsi üçün ünvan sahəsinin müxtəlif uclarında bir-birindən ayrılır.Üst və aşağı hissədə iki dinamik bölmə arasında bir deşik var. Bu orta hissədəki ünvanlar əməliyyat sistemi tərəfindən müxtəlif obyektlərin prosesi ilə əlaqələndirmək üçün istifadə olunur. Xüsusilə, o, müəyyən davamlı ünvanlar toplusunu diskdəki bir faylla əlaqələndirə bilər. Belə bir fayl yaddaşa uyğunlaşdırılmış fayl adlanır

Prosesə ayrılan ünvan sahəsi böyükdür. Teorik olaraq, ünvanların sayı yalnız sistemin bitliyi ilə müəyyən edilən göstəricinin ölçüsü ilə məhdudlaşır. Fiziki yaddaş ona 1-də 1 təyin edilsəydi, ilk proses bütün RAM-ı udacaq və hər hansı bir çox tapşırıqdan söhbət gedə bilməzdi.

Bununla belə, biz təcrübədən bilirik ki, müasir əməliyyat sistemləri eyni vaxtda istədiyiniz qədər prosesi işlədə bilir. Bu, yalnız kağız üzərindəki proseslərə çoxlu yaddaş ayırmaları, lakin əslində əsas fiziki yaddaşa yalnız burada və indi tələb olunan hissəni yükləmələri ilə mümkündür. Buna görə də proseslə əlaqəli yaddaş virtual adlanır.

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğu

Əməliyyat sistemi virtual və fiziki yaddaşı müəyyən ölçülü səhifələrdə təşkil edir. Virtual yaddaşın müəyyən səhifəsinə tələbat yaranan kimi əməliyyat sistemi onu fiziki yaddaşa yükləyir və onlar arasında yazışmaları xüsusi cədvələ qoyur. Boş yuvalar yoxdursa, əvvəllər yüklənmiş səhifələrdən biri diskə kopyalanır və tələb olunanı onun yerini tutur. Bir azdan qayıdacağımız bu prosedur dəyişdirmə adlanır. Aşağıdakı şəkil təsvir olunan prosesi göstərir. Bunun üzərinə 0 ünvanlı A səhifəsi yükləndi və 4 ünvanlı əsas yaddaş səhifəsinə yerləşdirildi. Bu fakt 0 nömrəli xanadakı yazışma cədvəlində öz əksini tapdı.​

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğu

Yaddaş xəritəli fayllarla hekayə tamamilə eynidir. Məntiqi olaraq, onlar guya davamlı və tamamilə virtual ünvan məkanında yerləşdirilir. Bununla belə, onlar fiziki yaddaşa səhifə-səhifə və yalnız tələb əsasında daxil olurlar. Belə səhifələrin modifikasiyası diskdəki faylla sinxronlaşdırılır. Beləliklə, siz sadəcə yaddaşda baytlarla işləyərək faylın I / O yerinə yetirə bilərsiniz - bütün dəyişikliklər əməliyyat sisteminin nüvəsi tərəfindən avtomatik olaraq orijinal fayla köçürüləcəkdir.
â € <
Aşağıdakı şəkil müxtəlif proseslərdən verilənlər bazası ilə işləyərkən LMDB-nin vəziyyətini necə sinxronlaşdırdığını nümayiş etdirir. Fərqli proseslərin virtual yaddaşını eyni fayla köçürməklə, biz faktiki olaraq əməliyyat sistemini onların ünvan məkanlarının müəyyən bloklarını LMDB-nin göründüyü yer olan bir-biri ilə keçidli sinxronizasiya etməyə məcbur edirik.
â € <

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğu

Əhəmiyyətli bir nüans ondan ibarətdir ki, LMDB məlumat faylını standart olaraq yazma sistemi çağırışı mexanizmi vasitəsilə dəyişdirir və faylın özü yalnız oxumaq rejimində göstərilir. Bu yanaşmanın iki mühüm nəticəsi var.

Birinci nəticə bütün əməliyyat sistemləri üçün ümumidir. Onun mahiyyəti yanlış kodla verilənlər bazasına təsadüfən ziyan vurmaqdan qorunma əlavə etməkdir. Bildiyiniz kimi, bir prosesin icra edilə bilən təlimatları onun ünvan məkanının istənilən yerindən məlumat əldə etmək üçün pulsuzdur. Eyni zamanda, indicə xatırladığımız kimi, faylın oxuma-yazma rejimində göstərilməsi o deməkdir ki, istənilən təlimat onu əlavə olaraq dəyişdirə bilər. Əgər o, bunu səhvən edirsə, məsələn, mövcud olmayan indeksdə massiv elementini faktiki olaraq üzərinə yazmağa çalışırsa, bu şəkildə o, bu ünvana uyğunlaşdırılmış faylı təsadüfən dəyişə bilər ki, bu da verilənlər bazası korlanmasına səbəb olacaq. Fayl yalnız oxumaq rejimində göstərilirsə, ona uyğun ünvan sahəsini dəyişdirmək cəhdi proqramın siqnalı ilə qəzaya uğramasına səbəb olacaq. SIGSEGV, və fayl toxunulmaz qalacaq.

İkinci nəticə artıq iOS-a xasdır. Nə müəllif, nə də başqa mənbələr bunu açıq şəkildə qeyd etmir, lakin onsuz LMDB bu mobil əməliyyat sistemində işləmək üçün yararsız olardı. Növbəti bölmə onun nəzərdən keçirilməsinə həsr edilmişdir.

iOS-da yaddaşa uyğunlaşdırılmış faylların xüsusiyyətləri

2018-ci ildə WWDC-də gözəl bir hesabat var idi iOS Yaddaş Dərin Dive. Burada deyilir ki, iOS-da fiziki yaddaşda yerləşən bütün səhifələr 3 növdən birinə aiddir: çirkli, sıxılmış və təmiz.

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğu

Təmiz yaddaş fiziki yaddaşdan təhlükəsiz şəkildə dəyişdirilə bilən səhifələr toplusudur. Tərkibindəki məlumatlar lazım olduqda orijinal mənbələrindən yenidən yüklənə bilər. Yalnız oxumaq üçün yaddaşla əlaqəli fayllar bu kateqoriyaya aiddir. iOS istənilən vaxt fayla uyğunlaşdırılmış səhifələri yaddaşdan boşaltmaqdan qorxmur, çünki onların diskdəki faylla sinxronizasiyasına zəmanət verilir.
â € <
Bütün dəyişdirilmiş səhifələr, harada yerləşməsindən asılı olmayaraq, çirkli yaddaşa daxil olur. Xüsusilə, onlarla əlaqəli virtual yaddaşa yazmaqla dəyişdirilmiş yaddaş xəritəli fayllar da bu şəkildə təsnif ediləcəkdir. LMDB-nin bayraqla açılması MDB_WRITEMAP, ona dəyişikliklər etdikdən sonra özünüz görə bilərsiniz

Tətbiq çox fiziki yaddaş tutmağa başlayan kimi iOS onun çirkli səhifələrini sıxır. Çirkli və sıxılmış səhifələrin tutduğu yaddaş toplusu proqramın yaddaş izi adlanır. Müəyyən bir hədd dəyərinə çatdıqda, OOM killer sistemi demonu prosesdən sonra gəlir və onu zorla dayandırır. Bu, iOS-un masaüstü əməliyyat sistemləri ilə müqayisədə özəlliyidir. Bunun əksinə olaraq, səhifələrin fiziki yaddaşdan diskə dəyişdirilməsi ilə yaddaş sahəsinin azaldılması iOS-da təmin edilmir.Sadəcə səbəblər haqqında təxmin etmək olar. Ola bilsin ki, səhifələrin diskə və arxaya intensiv şəkildə köçürülməsi proseduru mobil qurğular üçün çox enerji sərf edir və ya iOS SSD disklərində hüceyrələrin yenidən yazılması resursunu saxlayır və ya ola bilsin ki, dizaynerlər hər şeyin olduğu sistemin ümumi performansından razı deyillər. daim dəyişdirilir. Nə olursa olsun, fakt qalır.

Daha əvvəl qeyd olunan yaxşı xəbər odur ki, LMDB faylları yeniləmək üçün standart olaraq mmap mexanizmindən istifadə etmir. Buradan belə nəticə çıxır ki, göstərilən məlumatlar iOS tərəfindən təmiz yaddaş kimi təsnif edilir və yaddaşın izinə heç bir töhfə vermir. Bunu VM Tracker adlı Xcode alətindən istifadə etməklə yoxlamaq olar. Aşağıdakı ekran görüntüsü əməliyyat zamanı iOS Bulud tətbiqinin virtual yaddaş vəziyyətini göstərir. Başlanğıcda 2 LMDB nümunəsi işə salındı. Birinciyə öz faylını 1GiB virtual yaddaşa, ikincisinə isə 512MiB-ə köçürməyə icazə verildi. Hər iki anbarın müəyyən miqdarda daimi yaddaş tutmasına baxmayaraq, onların heç biri çirkli ölçüyə kömək etmir.

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğu

İndi pis xəbərin vaxtıdır. 64-bitlik masa üstü əməliyyat sistemlərindəki dəyişdirmə mexanizmi sayəsində hər bir proses, sabit diskdəki boş yer potensial dəyişdirməyə imkan verdiyi qədər virtual ünvan sahəsi tuta bilər. İOS-da svopun sıxılma ilə əvəz edilməsi nəzəri maksimumu kəskin şəkildə azaldır. İndi bütün canlı proseslər əsas (oxumaq RAM) yaddaşa uyğun olmalıdır və uyğun olmayanların hamısı məcburi dayandırılmaya məruz qalır. Yuxarıdakı kimi qeyd olunur hesabat, və rəsmi sənədlər. Nəticədə, iOS mmap vasitəsilə ayrıla bilən yaddaşın həcmini ciddi şəkildə məhdudlaşdırır. Budur burada bu sistem çağırışından istifadə edərək müxtəlif cihazlarda ayrıla biləcək yaddaşın həcminə dair empirik məhdudiyyətlərə baxa bilərsiniz. Smartfonların ən müasir modellərində iOS 2 gigabayt, iPad-in üst versiyalarında isə 4 səxavətli olmuşdur. Praktikada, əlbəttə ki, hər şeyin çox kədərli olduğu ən gənc dəstəklənən cihaz modellərinə diqqət yetirməlisiniz. Daha da pisi, VM Tracker-də tətbiqin yaddaş vəziyyətinə nəzər saldıqda, LMDB-nin yaddaş xəritəli yaddaşı iddia edən yeganə yaddaşdan uzaq olduğunu görəcəksiniz. Yaxşı parçalar sistem ayırıcıları, resurs faylları, təsvir çərçivələri və digər kiçik yırtıcılar tərəfindən yeyilir.

Buludda aparılan təcrübələr nəticəsində LMDB tərəfindən ayrılmış yaddaşın aşağıdakı kompromis dəyərləri ilə tanış olduq: 384 bitlik qurğular üçün 32 meqabayt və 768 bitlik cihazlar üçün 64 meqabayt. Bu həcm istifadə edildikdən sonra hər hansı dəyişdirmə əməliyyatları kodla tamamlanmağa başlayır MDB_MAP_FULL. Biz monitorinqimizdə belə səhvləri müşahidə edirik, lakin onlar bu mərhələdə diqqətdən kənarda qalacaq qədər kiçikdir.

Saxlama ilə həddindən artıq yaddaş istehlakının qeyri-aşkar səbəbi uzunmüddətli əməliyyatlar ola bilər. Bu iki hadisənin necə əlaqəli olduğunu anlamaq üçün qalan iki LMDB balinasını nəzərdən keçirməyə kömək edəcək.

3.2. Balina # 2. B+-ağac

Açar-dəyər anbarının üstündəki cədvəlləri təqlid etmək üçün onun API-də aşağıdakı əməliyyatlar mövcud olmalıdır:

  1. Yeni elementin daxil edilməsi.
  2. Verilmiş açarla elementi axtarın.
  3. Elementin silinməsi.
  4. Əsas intervalları sıralama qaydasında təkrarlayın.

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğuBütün dörd əməliyyatı asanlıqla həyata keçirə bilən ən sadə məlumat strukturu ikili axtarış ağacıdır. Onun qovşaqlarının hər biri uşaq düymələrin bütün alt çoxluğunu iki alt ağaca bölən açardır. Solda valideyndən kiçik olanlar, sağda isə daha böyük olanlar var. Sifariş edilmiş açar dəstini əldə etmək klassik ağac keçidlərindən biri ilə əldə edilir

İkili ağacların disk məlumat strukturu kimi effektiv olmasına mane olan iki əsas çatışmazlıq var. Birincisi, onların tarazlığının dərəcəsi gözlənilməzdir. Fərqli budaqların hündürlüyünün çox fərqli ola biləcəyi ağacları əldə etmək üçün əhəmiyyətli bir risk var ki, bu da gözlənilənlə müqayisədə axtarışın alqoritmik mürəkkəbliyini əhəmiyyətli dərəcədə pisləşdirir. İkincisi, qovşaqlar arasında çarpaz əlaqələrin çoxluğu binar ağacları yaddaşda lokallıqdan məhrum edir.Yaxın qovşaqlar (onlar arasındakı əlaqə baxımından) virtual yaddaşda tamamilə fərqli səhifələrdə yerləşə bilər. Nəticə etibarı ilə, hətta ağacda bir neçə qonşu qovşağın sadə keçidi belə, müqayisə edilən sayda səhifəyə baş çəkməyi tələb edə bilər. Bu, hətta ikili ağacların yaddaşdaxili məlumat strukturu kimi səmərəliliyindən bəhs edərkən belə problemdir, çünki prosessor keşində daim fırlanan səhifələr ucuz deyil. Diskdən qovşaqla əlaqəli səhifələrin tez-tez qaldırılmasına gəldikdə, işlər həqiqətən pisləşir. acınacaqlı.

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğuB-ağacları ikili ağacların təkamülü olmaqla, əvvəlki paraqrafda müəyyən edilmiş problemləri həll edir. Birincisi, onlar özlərini tarazlayırlar. İkincisi, onların qovşaqlarının hər biri uşaq düymələr dəstini 2-yə deyil, M sifarişli alt çoxluqlara bölür və M sayı bir neçə yüz və hətta minlərlə sıra ilə olduqca böyük ola bilər.

Bununla da:

  1. Hər node çoxlu sayda artıq sifariş edilmiş açarlara malikdir və ağaclar çox aşağıdır.
  2. Ağac yaddaşda yerləşmə xüsusiyyətini əldə edir, çünki dəyəri yaxın olan açarlar təbii olaraq bir və ya qonşu qovşaqlarda bir-birinin yanında yerləşir.
  3. Axtarış əməliyyatı zamanı ağacdan enərkən tranzit qovşaqların sayını azaldır.
  4. Aralıq sorğuları üçün oxunan hədəf qovşaqlarının sayını azaldır, çünki onların hər birində artıq çoxlu sayda sifarişli açar var.

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğu

LMDB məlumatların saxlanması üçün B ağacının B+ ağacı adlanan variantından istifadə edir. Yuxarıdakı diaqramda üç növ qovşaq göstərilir:

  1. Üstdə kök var. O, anbar daxilində verilənlər bazası anlayışından başqa heç nə həyata keçirmir. Tək bir LMDB nümunəsi daxilində xəritələnmiş virtual ünvan məkanını paylaşan çoxlu verilənlər bazası yarada bilərsiniz. Onların hər biri öz kökündən başlayır.
  2. Ən aşağı səviyyədə yarpaqlar (yarpaq) var. Verilənlər bazasında saxlanılan açar-dəyər cütlərini ehtiva edən yalnız onlardır. Yeri gəlmişkən, bu, B+-ağaclarının özəlliyidir. Normal bir B ağacı dəyər hissələrini bütün səviyyələrin qovşaqlarında saxlayırsa, B+-variasiyası yalnız ən aşağı olandadır. Bu faktı müəyyən etdikdən sonra biz LMDB-də istifadə olunan ağacın alt növünü sadəcə B-ağacı adlandıracağıq.
  3. Kök və yarpaqlar arasında naviqasiya (budaq) qovşaqları olan 0 və ya daha çox texniki səviyyə var. Onların vəzifəsi yarpaqlar arasında çeşidlənmiş açar dəstini bölməkdir.

Fiziki olaraq, qovşaqlar əvvəlcədən müəyyən edilmiş uzunluqda yaddaş bloklarıdır. Onların ölçüsü yuxarıda haqqında danışdığımız əməliyyat sistemindəki yaddaş səhifələrinin ölçüsünün qatıdır. Düyün quruluşu aşağıda göstərilmişdir. Başlıqda meta-məlumat var, onlardan ən bariz olanı, məsələn, yoxlama məbləğidir. Daha sonra məlumatların yerləşdiyi hüceyrələrin yerləşdiyi ofsetlər haqqında məlumat gəlir. Məlumatların rolu ya açarlar ola bilər, əgər söhbət naviqasiya qovşaqlarından gedirsə, ya da yarpaqlar vəziyyətində bütün açar-dəyər cütləri ola bilər.Siz işdə səhifələrin strukturu haqqında ətraflı oxuya bilərsiniz. "Yüksək Performanslı Açar Dəyərli Mağazaların Qiymətləndirilməsi".

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğu

Səhifə qovşaqlarının daxili məzmunu ilə məşğul olduqdan sonra, biz daha sonra LMDB B ağacını sadələşdirilmiş şəkildə aşağıdakı formada təqdim edəcəyik.

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğu

Düyünləri olan səhifələr diskdə ardıcıl olaraq yerləşdirilir. Sayı daha çox olan səhifələr faylın sonunda yerləşir. Sözdə meta səhifə (meta səhifə) bütün ağacların köklərini tapmaq üçün istifadə edilə bilən ofsetlər haqqında məlumat ehtiva edir. Fayl açıldığında, LMDB etibarlı meta səhifə axtarışında faylı səhifə-səhifə skan edir və onun vasitəsilə mövcud verilənlər bazalarını tapır.

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğu

İndi məlumatların təşkilinin məntiqi və fiziki quruluşu haqqında bir fikir əldə edərək, LMDB-nin üçüncü balinasını nəzərdən keçirməyə davam edə bilərik. Məhz onun köməyi ilə bütün saxlama modifikasiyaları tranzaksiya şəklində və bir-birindən təcrid olunmuş şəkildə baş verir və bütövlükdə verilənlər bazasına multiversiya xassəsini verir.

3.3. Balina #3. surəti-yazılı

Bəzi B-ağac əməliyyatları onun qovşaqlarında bir sıra dəyişikliklərin edilməsini nəzərdə tutur. Bir nümunə, artıq maksimum tutumuna çatmış bir node üçün yeni bir açar əlavə etməkdir. Bu halda, birincisi, qovşağı ikiyə bölmək, ikincisi, onun valideynində yeni fırlanan uşaq qovşağına keçid əlavə etmək lazımdır. Bu prosedur potensial olaraq çox təhlükəlidir. Əgər nədənsə (qəza, elektrik kəsilməsi və s.) seriyadan dəyişikliklərin yalnız bir hissəsi baş verərsə, o zaman ağac uyğunsuz vəziyyətdə qalacaq.

Verilənlər bazasını xətaya dözümlü etmək üçün ənənəvi həll yollarından biri, B ağacının yanına əlavə disk əsaslı məlumat strukturunu, həmçinin qabaqcadan yazma jurnalı (WAL) kimi tanınan əməliyyat jurnalını əlavə etməkdir. Bu bir fayldır, sonunda B ağacının özünün dəyişdirilməsindən əvvəl nəzərdə tutulan əməliyyat yazılır. Beləliklə, öz-özünə diaqnostika zamanı məlumatların pozulması aşkar edilərsə, verilənlər bazası özünü təmizləmək üçün jurnala müraciət edir.

LMDB səhvlərə dözümlülük mexanizmi kimi fərqli bir üsul seçmişdir ki, bu da “kopya-yazma” adlanır. Onun mahiyyəti ondan ibarətdir ki, mövcud səhifədəki məlumatları yeniləmək əvəzinə, əvvəlcə onu tamamilə kopyalayır və artıq surətdə bütün dəyişiklikləri edir.

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğu

Bundan əlavə, yenilənmiş məlumatların mövcud olması üçün onunla əlaqəli ana qovşaqda yenilənmiş qovşaqla əlaqəni dəyişdirmək lazımdır. Bunun üçün də dəyişdirilməsi lazım olduğundan, o da əvvəlcədən kopyalanır. Proses kökə qədər rekursiv şəkildə davam edir. Meta səhifədəki məlumatlar ən son dəyişdirilir

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğu

Yeniləmə proseduru zamanı birdən proses çökərsə, ya yeni meta səhifə yaradılmayacaq, ya da sona qədər diskə yazılmayacaq və onun yoxlama məbləği səhv olacaq. Bu iki halda, yeni səhifələr əlçatmaz olacaq və köhnələrə təsir etməyəcək. Bu, verilənlərin ardıcıllığını qorumaq üçün LMDB-nin qabaqcadan qeydlər yazma ehtiyacını aradan qaldırır. De-fakto, yuxarıda təsvir edilən diskdə məlumatların saxlanması strukturu eyni vaxtda öz funksiyasını yerinə yetirir. Açıq bir əməliyyat jurnalının olmaması LMDB-nin xüsusiyyətlərindən biridir və yüksək məlumat oxuma sürətini təmin edir.

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğu

Yalnız əlavə B-ağacı adlanan nəticə konstruksiya təbii olaraq əməliyyatların izolyasiyasını və çoxversiyanı təmin edir. LMDB-də hər bir açıq əməliyyatın onunla əlaqəli müasir ağac kökü var. Nə qədər ki, tranzaksiya tamamlanmayıb, onunla əlaqəli ağacın səhifələri heç vaxt dəyişdirilməyəcək və ya məlumatların yeni versiyaları üçün təkrar istifadə edilməyəcək.Beləliklə, siz istədiyiniz müddət ərzində müvafiq məlumat dəsti ilə işləyə bilərsiniz. yaddaş hazırda aktiv şəkildə yenilənməyə davam etsə belə, əməliyyatın açıldığı vaxt. Bu, LMDB-ni sevgilimiz üçün ideal məlumat mənbəyinə çevirən multiversiyalaşdırmanın mahiyyətidir UICollectionView. Bir əməliyyat açdıqdan sonra, heç bir şey qalmamaqdan qorxaraq, cari məlumatları tələsik yaddaşdaxili struktura köçürərək tətbiqin yaddaş izini artırmağa ehtiyac yoxdur. Bu xüsusiyyət LMDB-ni bu cür ümumi izolyasiya ilə öyünə bilməyən eyni SQLite-dən fərqləndirir. Sonuncuda iki əməliyyat açdıqdan və onlardan birində müəyyən bir qeydi sildikdən sonra, qalan ikincisində eyni qeyd artıq əldə edilə bilməz.

Sikkənin əks tərəfi virtual yaddaşın potensial olaraq əhəmiyyətli dərəcədə yüksək istehlakıdır. Slayd verilənlər bazasının müxtəlif versiyalarına baxan 3 açıq oxu əməliyyatı ilə eyni vaxtda dəyişdirilərsə, verilənlər bazası strukturunun necə görünəcəyini göstərir. LMDB faktiki əməliyyatlarla əlaqəli köklərdən əldə edilə bilən qovşaqlardan təkrar istifadə edə bilmədiyi üçün yaddaşın yaddaşda başqa dördüncü kök ayırmaqdan və onun altındakı dəyişdirilmiş səhifələri bir daha klonlaşdırmaqdan başqa seçimi yoxdur.

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğu

Burada yaddaşla əlaqəli fayllar bölməsini xatırlatmaq artıq olmaz. Görünür ki, virtual yaddaşın əlavə istehlakı bizi çox narahat etməməlidir, çünki bu, proqramın yaddaş izinə heç bir töhfə vermir. Bununla belə, eyni zamanda qeyd olundu ki, iOS onu ayırmaqda çox xəsisdir və biz master çiynindən serverdə və ya iş stolunda 1 terabaytlıq LMDB regionunu təmin edə bilmirik və bu funksiya haqqında ümumiyyətlə düşünmürük. Mümkün olduqda, əməliyyatların ömrünü mümkün qədər qısa saxlamağa çalışmalısınız.

4. Açar-dəyər API-nin üstündə verilənlər sxeminin layihələndirilməsi

LMDB tərəfindən təmin edilən əsas abstraksiyalara baxaraq API-ni təhlil etməyə başlayaq: mühit və verilənlər bazası, açarlar və dəyərlər, əməliyyatlar və kursorlar.

Kod siyahıları haqqında qeyd

LMDB ictimai API-dəki bütün funksiyalar öz işlərinin nəticəsini xəta kodu şəklində qaytarır, lakin bütün sonrakı siyahılarda qısalıq üçün onun yoxlanışı buraxılıb.Praktikada biz repozitoriya ilə qarşılıqlı əlaqə yaratmaq üçün öz kodumuzdan istifadə etdik. çəngəl C++ sarğıları lmdbxx, hansı səhvlər C++ istisnaları kimi reallaşır.

LMDB-ni iOS və ya macOS layihəsinə qoşmağın ən sürətli yolu kimi mən CocoaPod-umu təklif edirəm POSLMDB.

4.1. Əsas abstraksiyalar

Ətraf mühit

Struktur MDB_env is LMDB-nin daxili vəziyyətinin deposudur. Prefiksli funksiyalar ailəsi mdb_env onun bəzi xassələrini konfiqurasiya etməyə imkan verir. Ən sadə halda, mühərrikin işə salınması belə görünür.

mdb_env_create(env);​
mdb_env_set_map_size(*env, 1024 * 1024 * 512)​
mdb_env_open(*env, path.UTF8String, MDB_NOTLS, 0664);

Mail.ru Bulud tətbiqində biz yalnız iki parametr üçün standart dəyərləri dəyişdirdik.

Birincisi, saxlama faylının xəritələndiyi virtual ünvan sahəsinin ölçüsüdür. Təəssüf ki, hətta eyni cihazda, xüsusi dəyər qaçışdan qaçışa əhəmiyyətli dərəcədə dəyişə bilər. iOS-un bu xüsusiyyətini nəzərə almaq üçün maksimum yaddaş həcmini dinamik olaraq seçirik. Müəyyən bir dəyərdən başlayaraq, funksiyaya qədər ardıcıl olaraq yarıya düşür mdb_env_open başqa bir nəticə qaytarmayacaq ENOMEM. Teorik olaraq, bunun əksi var - əvvəlcə mühərrikə minimum yaddaş ayırın, sonra səhvlər alındıqda MDB_MAP_FULL, onu artırın. Bununla belə, bu, çox daha tikanlıdır. Səbəb funksiyadan istifadə edərək yaddaşın yenidən qurulması prosedurudur mdb_env_set_map_size əvvəllər mühərrikdən alınan bütün obyektləri (kursorlar, əməliyyatlar, açarlar və dəyərlər) etibarsız edir. Kodeksdə hadisələrin belə bir dönüşünün uçotu onun əhəmiyyətli dərəcədə çətinləşməsinə səbəb olacaqdır. Buna baxmayaraq, virtual yaddaş sizin üçün çox əzizdirsə, bu, çox irəli getmiş çəngələ baxmaq üçün bir səbəb ola bilər. MDBX, burada elan edilmiş xüsusiyyətlər arasında "avtomatik olaraq məlumat bazası ölçüsünün tənzimlənməsi" var.

Standart dəyəri bizə uyğun gəlməyən ikinci parametr, iplik təhlükəsizliyini təmin etmək mexanikasını tənzimləyir. Təəssüf ki, ən azı iOS 10-da yerli yaddaş dəstəyi ilə bağlı problemlər var. Bu səbəbdən yuxarıdakı misalda anbar bayraqla açılır MDB_NOTLS. Bundan əlavə, bu da tələb olunur çəngəl C++ sarğısı lmdbxxvə bu atributda dəyişənləri kəsmək üçün.

Verilənlər bazası

Verilənlər bazası yuxarıda bəhs etdiyimiz B-ağacının ayrıca bir nümunəsidir. Onun açılışı əvvəlcə bir az qəribə görünə bilən əməliyyatın içərisində baş verir.

MDB_txn *txn;​
MDB_dbi dbi;​
mdb_txn_begin(env, NULL, MDB_RDONLY, &txn);​
mdb_dbi_open(txn, NULL, MDB_CREATE, &dbi);​
mdb_txn_abort(txn);

Həqiqətən, LMDB-də əməliyyat xüsusi verilənlər bazası deyil, saxlama obyektidir. Bu konsepsiya müxtəlif verilənlər bazalarında yerləşən obyektlər üzərində atom əməliyyatlarını yerinə yetirməyə imkan verir. Teorik olaraq, bu, müxtəlif verilənlər bazaları şəklində cədvəllərin modelləşdirilməsi imkanını açır, lakin bir vaxtlar aşağıda ətraflı təsvir olunan başqa yolla getdim.

Açarlar və dəyərlər

Struktur MDB_val həm açar, həm də dəyər anlayışını modelləşdirir. Repozitoriyanın onların semantikası haqqında heç bir fikri yoxdur. Onun üçün fərqli olan bir şey sadəcə verilmiş ölçülü bayt massividir. Maksimum açar ölçüsü 512 baytdır.

typedef struct MDB_val {​
    size_t mv_size;​
    void *mv_data;​
} MDB_val;​​

Mağaza açarları artan qaydada çeşidləmək üçün müqayisə aparatından istifadə edir. Əgər onu özünüzlə əvəz etməsəniz, onları leksikoqrafik qaydada bayt-bayt sıralayan defolt istifadə olunacaq.

Əməliyyatlar

Əməliyyat cihazı ətraflı təsvir edilmişdir əvvəlki fəsil, buna görə də burada onların əsas xüsusiyyətlərini qısa bir sətirdə təkrarlayacağam:

  1. Bütün əsas xüsusiyyətlər üçün dəstək ACIDAçar sözlər: atomiklik, tutarlılıq, izolyasiya və etibarlılıq. Qeyd etməyə kömək edə bilmirəm ki, macOS və iOS-da davamlılıq baxımından MDBX-də düzəldilmiş bir səhv var. Ətraflı oxuya bilərsiniz README.
  2. Multithreading yanaşması "tək yazıçı/çox oxucu" sxemi ilə təsvir edilmişdir. Yazıçılar bir-birini bloklayır, amma oxucuları bloklamırlar. Oxucular yazıçıları və ya bir-birlərini bloklamırlar.
  3. Yuvalanmış əməliyyatlar üçün dəstək.
  4. Multiversiya dəstəyi.

LMDB-də multiversiya o qədər yaxşıdır ki, onu hərəkətdə nümayiş etdirmək istəyirəm. Aşağıdakı kod göstərir ki, hər bir tranzaksiya bütün sonrakı dəyişikliklərdən tamamilə təcrid olunaraq, açıldığı zaman aktual olan verilənlər bazası versiyası ilə işləyir. Anbarı işə salmaq və ona test qeydini əlavə etmək heç bir maraq doğurmur, buna görə də bu rituallar spoylerin altında qalır.

Test girişinin əlavə edilməsi

MDB_env *env;
MDB_dbi dbi;
MDB_txn *txn;

mdb_env_create(&env);
mdb_env_open(env, "./testdb", MDB_NOTLS, 0664);

mdb_txn_begin(env, NULL, 0, &txn);
mdb_dbi_open(txn, NULL, 0, &dbi);
mdb_txn_abort(txn);

char k = 'k';
MDB_val key;
key.mv_size = sizeof(k);
key.mv_data = (void *)&k;

int v = 997;
MDB_val value;
value.mv_size = sizeof(v);
value.mv_data = (void *)&v;

mdb_txn_begin(env, NULL, 0, &txn);
mdb_put(txn, dbi, &key, &value, MDB_NOOVERWRITE);
mdb_txn_commit(txn);

MDB_txn *txn1, *txn2, *txn3;
MDB_val val;

// Открываем 2 транзакции, каждая из которых смотрит
// на версию базы данных с одной записью.
mdb_txn_begin(env, NULL, 0, &txn1); // read-write
mdb_txn_begin(env, NULL, MDB_RDONLY, &txn2); // read-only

// В рамках первой транзакции удаляем из базы данных существующую в ней запись.
mdb_del(txn1, dbi, &key, NULL);
// Фиксируем удаление.
mdb_txn_commit(txn1);

// Открываем третью транзакцию, которая смотрит на
// актуальную версию базы данных, где записи уже нет.
mdb_txn_begin(env, NULL, MDB_RDONLY, &txn3);
// Убеждаемся, что запись по искомому ключу уже не существует.
assert(mdb_get(txn3, dbi, &key, &val) == MDB_NOTFOUND);
// Завершаем транзакцию.
mdb_txn_abort(txn3);

// Убеждаемся, что в рамках второй транзакции, открытой на момент
// существования записи в базе данных, её всё ещё можно найти по ключу.
assert(mdb_get(txn2, dbi, &key, &val) == MDB_SUCCESS);
// Проверяем, что по ключу получен не абы какой мусор, а валидные данные.
assert(*(int *)val.mv_data == 997);
// Завершаем транзакцию, работающей хоть и с устаревшей, но консистентной базой данных.
mdb_txn_abort(txn2);

İstəyə görə, SQLite ilə eyni hiyləni sınamağı və nə baş verdiyini görməyi məsləhət görürəm.

Multiversioning iOS tərtibatçısının həyatına çox gözəl faydalar gətirir. Bu xüsusiyyətdən istifadə edərək, istifadəçi təcrübəsi mülahizələri əsasında ekran formaları üçün məlumat mənbəyi yeniləmə sürətini asanlıqla və təbii şəkildə tənzimləyə bilərsiniz. Məsələn, sistem media qalereyasından məzmunun avtomatik yüklənməsi kimi Mail.ru Cloud tətbiqinin belə bir xüsusiyyətini götürək. Yaxşı bir əlaqə ilə müştəri serverə saniyədə bir neçə fotoşəkil əlavə edə bilər. Hər yükləmədən sonra yeniləsəniz UICollectionView istifadəçinin buludunda media məzmunu ilə bu proses zamanı 60 fps və hamar sürüşməni unuda bilərsiniz. Ekranın tez-tez yenilənməsinin qarşısını almaq üçün əsasda məlumatların dəyişmə sürətini birtəhər məhdudlaşdırmalısınız UICollectionViewDataSource.

Verilənlər bazası multiversiyanı dəstəkləmirsə və yalnız cari vəziyyətlə işləməyə imkan verirsə, zamana uyğun məlumat snapshot yaratmaq üçün onu ya yaddaşdaxili məlumat strukturuna, ya da müvəqqəti cədvələ köçürməlisiniz. Bu yanaşmaların hər biri çox bahalıdır. Yaddaşda saxlama vəziyyətində biz həm qurulmuş obyektlərin saxlanması nəticəsində yaranan yaddaş xərclərini, həm də lazımsız ORM çevrilmələri ilə bağlı vaxt xərclərini alırıq. Müvəqqəti masaya gəldikdə, bu, yalnız qeyri-ciddi hallarda məna kəsb edən daha bahalı bir zövqdür.

Multiversioning LMDB sabit məlumat mənbəyini saxlamaq problemini çox zərif bir şəkildə həll edir. Yalnız bir əməliyyat açmaq kifayətdir və voila - biz onu tamamlayana qədər məlumat dəstinin düzəldilməsinə zəmanət verilir. Onun yeniləmə sürətinin məntiqi indi tamamilə təqdimat qatının əlindədir və əhəmiyyətli resurslara əlavə xərc tələb etmir.

Kursorlar

Kursorlar B-ağacını keçərək açar-dəyər cütləri üzərində nizamlı təkrarlama mexanizmini təmin edir. Onlarsız verilənlər bazasında indi müraciət etdiyimiz cədvəlləri effektiv şəkildə modelləşdirmək qeyri-mümkün olardı.

4.2. Cədvəl modelləşdirmə

Əsas sifariş xassəsi sizə əsas abstraksiyaların üstündə cədvəl kimi yüksək səviyyəli abstraksiya qurmağa imkan verir. Bu prosesi istifadəçinin bütün faylları və qovluqları haqqında məlumatın önbelleğe alındığı bulud müştərisinin əsas cədvəlinin nümunəsində nəzərdən keçirək.

Cədvəl sxemi

Qovluq ağacı olan cədvəlin strukturunun kəskinləşdirilməli olduğu ümumi ssenarilərdən biri verilmiş kataloqda yerləşən bütün elementləri seçməkdir.Bu cür səmərəli sorğular üçün yaxşı məlumatların təşkili modelidir. Yaxınlıq siyahısı. Onu açar-dəyər yaddaşının üstündə həyata keçirmək üçün fayl və qovluqların açarlarını elə çeşidləmək lazımdır ki, onlar ana qovluğa mənsubiyyətinə görə qruplaşdırılsın. Bundan əlavə, kataloqun məzmununu Windows istifadəçisinə tanış olan formada göstərmək üçün (əvvəlcə qovluqlar, sonra fayllar, hər ikisi əlifba sırası ilə sıralanır) açara müvafiq əlavə sahələri daxil etmək lazımdır.

Aşağıdakı şəkil, tapşırığa əsasən, bayt massivi kimi düymələrin necə görünə biləcəyini göstərir. Əvvəlcə əsas kataloq identifikatoru (qırmızı) olan baytlar, sonra növlə (yaşıl) və artıq adla (mavi) quyruqda yerləşdirilir.Defolt LMDB müqayisəçisi tərəfindən leksikoqrafik ardıcıllıqla çeşidlənərək, onlar sıralanır. tələb olunan yol. Eyni qırmızı prefiksi olan düymələrin ardıcıl olaraq keçməsi bizə əlavə emal tələb etmədən istifadəçi interfeysində (sağda) göstərilməli olduğu ardıcıllıqla onlarla əlaqəli dəyərləri verir.

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğu

Açarların və Dəyərlərin Seriyalaşdırılması

Dünyada obyektləri seriallaşdırmaq üçün bir çox üsul var. Sürətdən başqa heç bir tələbimiz olmadığı üçün özümüz üçün mümkün olan ən sürətli olanı seçdik - C dili strukturunun nümunəsinin tutduğu yaddaş zibilini.Beləliklə, kataloq elementinin açarı aşağıdakı strukturla modelləşdirilə bilər. NodeKey.

typedef struct NodeKey {​
    EntityId parentId;​
    uint8_t type;​
    uint8_t nameBuffer[256];​
} NodeKey;

Qorumaq NodeKey anbarda obyektə ehtiyac MDB_val göstəricini strukturun başlanğıcının ünvanındakı verilənlərə yerləşdirin və funksiya ilə onların ölçüsünü hesablayın sizeof.

MDB_val serialize(NodeKey * const key) {
    return MDB_val {
        .mv_size = sizeof(NodeKey),
        .mv_data = (void *)key
    };
}

Verilənlər bazası seçim meyarlarına dair birinci fəsildə mühüm seçim amili kimi CRUD əməliyyatlarının bir hissəsi kimi dinamik ayırmaların minimuma endirilməsini qeyd etdim. Funksiya kodu serialize LMDB vəziyyətində verilənlər bazasına yeni qeydlər daxil edildikdə onların necə tamamilə qarşısını almaq olar. Serverdən gələn bayt massivi əvvəlcə yığın strukturlarına çevrilir, sonra isə onlar cüzi şəkildə yaddaşa atılır. LMDB daxilində dinamik ayırmaların da olmadığını nəzərə alsaq, iOS standartları ilə fantastik bir vəziyyət əldə edə bilərsiniz - şəbəkədən diskə qədər məlumatlarla işləmək üçün yalnız yığın yaddaşından istifadə edin!

Binar komparator ilə açarların sifarişi

Açar sıra əlaqəsi komparator adlanan xüsusi funksiya ilə verilir. Mühərrik onlarda olan baytların semantikası haqqında heç nə bilmədiyi üçün standart müqayisəçinin düymələri bayt-bayt müqayisəsinə müraciət edərək leksikoqrafik ardıcıllıqla düzməkdən başqa seçimi yoxdur. Quruluşları tənzimləmək üçün istifadə etmək oyma balta ilə təraş etməyə bənzəyir. Ancaq sadə hallarda bu üsulu məqbul hesab edirəm. Alternativ aşağıda təsvir edilmişdir, amma burada yol boyu səpələnmiş bir neçə dırmığı qeyd edəcəyəm.

Yadda saxlamaq lazım olan ilk şey ibtidai məlumat növlərinin yaddaşda təmsil olunmasıdır. Beləliklə, bütün Apple cihazlarında tam dəyişənlər formatda saxlanılır Balaca Endian. Bu o deməkdir ki, ən az əhəmiyyətli bayt solda olacaq və siz onların bayt-bayt müqayisəsindən istifadə edərək tam ədədləri çeşidləyə bilməyəcəksiniz. Məsələn, bunu 0-dan 511-ə qədər rəqəmlər toplusu ilə etməyə çalışmaq aşağıdakı nəticə ilə nəticələnəcək.

// value (hex dump)
000 (0000)
256 (0001)
001 (0100)
257 (0101)
...
254 (fe00)
510 (fe01)
255 (ff00)
511 (ff01)

Bu problemi həll etmək üçün tam ədədlər bayt müqayisəçisinə uyğun formatda açarda saxlanmalıdır. Ailədən gələn funksiyalar lazımi çevrilməni həyata keçirməyə kömək edəcəkdir. hton* (xüsusilə htons misaldan iki baytlıq nömrələr üçün).

Proqramlaşdırmada sətirləri təmsil etmək üçün format, bildiyiniz kimi, bir bütövdür история. Sətirlərin semantikası, habelə onları yaddaşda təmsil etmək üçün istifadə olunan kodlaşdırma hər bir simvol üçün bir baytdan çox ola biləcəyini göstərirsə, standart müqayisəçidən istifadə etmək fikrindən dərhal imtina etmək daha yaxşıdır.

Nəzərə almaq lazım olan ikinci şeydir uyğunlaşma prinsipləri struktur sahəsi tərtibçisi. Onların sayəsində sahələr arasında yaddaşda zibil dəyərləri olan baytlar əmələ gələ bilər ki, bu da əlbəttə ki, bayt çeşidlənməsini pozur. Zibilləri aradan qaldırmaq üçün ya düzləşdirmə qaydalarını nəzərə alaraq sahələri ciddi şəkildə müəyyən edilmiş qaydada elan etməli, ya da struktur bəyannaməsində atributdan istifadə etməlisiniz. packed.

Xarici müqayisəçi ilə açarın sifarişi

Əsas müqayisə məntiqi ikili komparator üçün çox mürəkkəb ola bilər. Çoxlu səbəblərdən biri strukturların içərisində texniki sahələrin olmasıdır. Onların baş verməsini kataloq elementi üçün bizə artıq tanış olan açarın nümunəsində göstərəcəyəm.

typedef struct NodeKey {​
    EntityId parentId;​
    uint8_t type;​
    uint8_t nameBuffer[256];​
} NodeKey;

Bütün sadəliyinə baxmayaraq, əksər hallarda o, çox yaddaş sərf edir. Başlıq buferi 256 baytdır, baxmayaraq ki, orta hesabla fayl və qovluq adları nadir hallarda 20-30 simvoldan çox olur.

Yazının ölçüsünü optimallaşdırmaq üçün standart üsullardan biri onu həqiqi ölçüyə uyğunlaşdırmaq üçün "kəsmək"dir. Onun mahiyyəti ondan ibarətdir ki, bütün dəyişən uzunluqlu sahələrin məzmunu strukturun sonunda buferdə, uzunluqları isə ayrıca dəyişənlərdə saxlanılır.Bu yanaşmaya uyğun olaraq açar NodeKey aşağıdakı şəkildə çevrilir.

typedef struct NodeKey {​
    EntityId parentId;​
    uint8_t type;​
    uint8_t nameLength;​
    uint8_t nameBuffer[256];​
} NodeKey;

Bundan əlavə, serializasiya zamanı məlumat ölçüsü kimi göstərilmir sizeof bütün struktur və bütün sahələrin ölçüsü sabit uzunluq və buferin faktiki istifadə olunan hissəsinin ölçüsüdür.

MDB_val serialize(NodeKey * const key) {
    return MDB_val {
        .mv_size = offsetof(NodeKey, nameBuffer) + key->nameLength,
        .mv_data = (void *)key
    };
}

Refaktorinq nəticəsində biz açarların tutduğu məkanda əhəmiyyətli qənaət əldə etdik. Ancaq texniki sahəyə görə nameLength, standart ikili müqayisəedici artıq əsas müqayisə üçün uyğun deyil. Əgər onu özümüzlə əvəz etməsək, onda adın uzunluğu çeşidlənmədə adın özündən daha çox prioritet amil olacaq.

LMDB hər bir verilənlər bazasının öz əsas müqayisə funksiyasına malik olmasına imkan verir. Bu funksiyadan istifadə etməklə edilir mdb_set_compare açılışdan əvvəl. Aşkar səbəblərə görə verilənlər bazası bütün ömrü boyu dəyişdirilə bilməz. Girişdə müqayisəedici ikili formatda iki açar alır və çıxışda müqayisənin nəticəsini qaytarır: kiçik (-1), (1)-dən böyük və ya bərabər (0). üçün psevdokod NodeKey belə görünür.

int compare(MDB_val * const a, MDB_val * const b) {​
    NodeKey * const aKey = (NodeKey * const)a->mv_data;​
    NodeKey * const bKey = (NodeKey * const)b->mv_data;​
    return // ...
}​

Verilənlər bazasındakı bütün açarlar eyni tipdə olduğu müddətcə, onların bayt təsvirini açarın tətbiq strukturunun növünə qeyd-şərtsiz köçürmək qanunidir. Burada bir nüans var, ancaq "Oxuma qeydləri" alt bölməsində bir az aşağıda müzakirə ediləcək.

Dəyər Serializasiyası

Saxlanılan qeydlərin açarları ilə LMDB son dərəcə intensiv işləyir. Hər hansı bir tətbiq əməliyyatı çərçivəsində bir-biri ilə müqayisə edilir və bütün həllin performansı müqayisə cihazının sürətindən asılıdır. İdeal bir dünyada, standart ikili komparator açarları müqayisə etmək üçün kifayət olmalıdır, lakin həqiqətən özünüzdən istifadə etməli idinizsə, onda açarların seriyadan çıxarılması proseduru mümkün qədər sürətli olmalıdır.

Məlumat bazası qeydin (dəyərin) Dəyər hissəsi ilə xüsusilə maraqlanmır. Onun bayt təsvirindən obyektə çevrilməsi yalnız proqram kodu tərəfindən artıq tələb olunduqda, məsələn, onu ekranda göstərmək üçün baş verir. Bu, nisbətən nadir hallarda baş verdiyi üçün, bu prosedurun sürətinə olan tələblər o qədər də kritik deyil və onun həyata keçirilməsində biz rahatlığa diqqət yetirməkdə daha sərbəstik.Məsələn, hələ endirilməmiş fayllar haqqında metaməlumatları seriallaşdırmaq üçün istifadə edirik. NSKeyedArchiver.

NSData *data = serialize(object);​
MDB_val value = {​
    .mv_size = data.length,​
    .mv_data = (void *)data.bytes​
};

Ancaq performansın əhəmiyyətli olduğu vaxtlar var. Məsələn, istifadəçi buludunun fayl strukturu haqqında meta-məlumatı saxlayarkən, biz eyni obyekt yaddaş zibilindən istifadə edirik. Onların seriallaşdırılmış təqdimatını yaratmaq vəzifəsinin əsas məqamı kataloqun elementlərinin sinif iyerarxiyası ilə modelləşdirilməsidir.

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğu

Onu C dilində həyata keçirmək üçün varislərin spesifik sahələri ayrı-ayrı strukturlara ayrılır və birlik tipli sahə vasitəsilə onların baza ilə əlaqəsi dəqiqləşdirilir. Birliyin faktiki məzmunu tip texniki atribut vasitəsilə müəyyən edilir.

typedef struct NodeValue {​
    EntityId localId;​
    EntityType type;​
    union {​
        FileInfo file;​
        DirectoryInfo directory;​
    } info;​
    uint8_t nameLength;​
    uint8_t nameBuffer[256];​
} NodeValue;​

Qeydlərin əlavə edilməsi və yenilənməsi

Seriallaşdırılmış açar və dəyər mağazaya əlavə edilə bilər. Bunun üçün funksiyadan istifadə olunur mdb_put.

// key и value имеют тип MDB_val​
mdb_put(..., &key, &value, MDB_NOOVERWRITE);

Konfiqurasiya mərhələsində, depoya eyni açarla birdən çox qeydi saxlamağa icazə verilə və ya qadağan edilə bilər.​ Əgər açarların təkrarlanması qadağandırsa, o zaman qeyd daxil edərkən artıq mövcud qeydin yenilənməsinə icazə verilib-verilmədiyini müəyyən edə bilərsiniz. Əgər köhnəlmə yalnız koddakı xəta nəticəsində baş verə bilərsə, o zaman bayrağı göstərərək bundan sığortalana bilərsiniz. NOOVERWRITE.

Oxuma qeydləri

LMDB-də qeydləri oxumaq funksiyası belədir mdb_get. Açar-dəyər cütü əvvəllər boşaldılmış strukturlarla təmsil olunursa, bu prosedur belə görünür.

NodeValue * const readNode(..., NodeKey * const key) {​
    MDB_val rawKey = serialize(key);​
    MDB_val rawValue;​
    mdb_get(..., &rawKey, &rawValue);​
    return (NodeValue * const)rawValue.mv_data;​
}

Təqdim olunan siyahı, strukturların zibilxanası vasitəsilə serializasiyanın təkcə yazarkən deyil, həm də məlumatları oxuyarkən dinamik ayırmalardan qurtulmağa necə imkan verdiyini göstərir. Funksiyadan irəli gəlir mdb_get göstərici verilənlər bazasında obyektin bayt təsvirini saxladığı virtual yaddaş ünvanına tam olaraq baxır. Əslində, biz məlumatların çox yüksək sürətli oxunmasını təmin edən demək olar ki, pulsuz olaraq bir növ ORM alırıq. Yanaşmanın bütün gözəlliyi ilə, onunla əlaqəli bir neçə xüsusiyyətləri xatırlamaq lazımdır.

  1. Yalnız oxunan əməliyyat üçün dəyər strukturuna işarənin yalnız əməliyyat bağlanana qədər etibarlı qalmasına zəmanət verilir. Daha əvvəl qeyd edildiyi kimi, obyektin yerləşdiyi B ağacının səhifələri, kopyala-yazma prinsipi sayəsində, ən azı bir əməliyyat onlara istinad etdiyi müddətcə dəyişməz qalır. Eyni zamanda, onlarla əlaqəli son əməliyyat başa çatan kimi, səhifələr yeni məlumatlar üçün yenidən istifadə edilə bilər. Əgər obyektlərin onları yaradan tranzaksiyadan sağ qalması lazımdırsa, o zaman onlar hələ də kopyalanmalıdır.
  2. Readwrite əməliyyatı üçün nəticə struktur-dəyərinin göstəricisi yalnız ilk dəyişdirmə proseduruna (məlumatların yazılması və ya silinməsi) qədər etibarlı olacaq.
  3. Quruluşuna baxmayaraq NodeValue tam hüquqlu deyil, lakin kəsilmişdir ("Xarici müqayisəçi ilə açarların sifarişi" bölməsinə baxın), göstərici vasitəsilə onun sahələrinə asanlıqla daxil ola bilərsiniz. Əsas odur ki, diqqətdən kənarda qalmasın!
  4. Heç bir halda qəbul edilmiş göstərici vasitəsilə strukturu dəyişdirə bilməzsiniz. Bütün dəyişikliklər yalnız metod vasitəsilə edilməlidir mdb_put. Bununla belə, bunu etmək üçün bütün arzularla, bu işləməyəcək, çünki bu strukturun yerləşdiyi yaddaş sahəsi yalnız oxumaq rejimində xəritələnir.
  5. Məsələn, funksiyadan istifadə edərək maksimum yaddaş ölçüsünü artırmaq üçün faylı prosesin ünvan sahəsinə dəyişdirin mdb_env_set_map_size bütövlükdə bütün əməliyyatları və əlaqəli qurumları tamamilə etibarsız edir və xüsusilə obyektləri oxumaq üçün göstəricilər.

Nəhayət, daha bir xüsusiyyət o qədər məkrlidir ki, onun mahiyyətinin açıqlanması sadəcə bir məqama sığmır. B-ağacı ilə bağlı fəsildə onun səhifələrinin yaddaşda təşkili sxemini verdim. Buradan belə çıxır ki, seriallaşdırılmış məlumatlarla buferin başlanğıc ünvanı tamamilə ixtiyari ola bilər. Buna görə də onlara göstərici strukturda əldə edilir MDB_val və strukturun göstəricisinə ötürülməsi ümumiyyətlə düzlənmir. Eyni zamanda, bəzi çiplərin arxitekturaları (iOS vəziyyətində, bu armv7-dir) hər hansı bir məlumatın ünvanının maşın sözünün ölçüsünün çoxlu olmasını və ya başqa sözlə, sistemin bitliyini tələb edir. (armv7 üçün bu 32 bitdir). Başqa sözlə, əməliyyat kimi *(int *foo)0x800002 onların üzərində qaçmağa bərabər tutulur və hökmlə edama səbəb olur EXC_ARM_DA_ALIGN. Belə kədərli aqibətdən qaçmağın iki yolu var.

Birincisi, məlumatları əvvəlcədən bilinən bir quruluşa köçürməkdir. Məsələn, fərdi müqayisəçidə bu, aşağıdakı kimi əks olunacaq.

int compare(MDB_val * const a, MDB_val * const b) {
    NodeKey aKey, bKey;
    memcpy(&aKey, a->mv_data, a->mv_size);
    memcpy(&bKey, b->mv_data, b->mv_size);
    return // ...
}

Alternativ bir yol, açarı və dəyəri olan strukturların bir atributdan istifadə edərək uyğunlaşdırıla bilməyəcəyi barədə tərtibçini əvvəlcədən xəbərdar etməkdir. aligned(1). ARM-də eyni təsir ola bilər nail olmaq və paketlənmiş atributdan istifadə etməklə. Quruluşun tutduğu məkanın optimallaşdırılmasına da töhfə verdiyini nəzərə alsaq, bu üsul mənə üstünlük verir, baxmayaraq ki, приводит məlumat əldə etmək əməliyyatlarının dəyərini artırmaq.

typedef struct __attribute__((packed)) NodeKey {
    uint8_t parentId;
    uint8_t type;
    uint8_t nameLength;
    uint8_t nameBuffer[256];
} NodeKey;

Diapazon Sorğuları

Bir qrup qeyd üzərində təkrarlamaq üçün LMDB kursor abstraksiyasını təmin edir. Bizə artıq tanış olan istifadəçi bulud metadatası olan cədvəl nümunəsindən istifadə edərək onunla necə işləməyə baxaq.

Kataloqda faylların siyahısını göstərməyin bir hissəsi olaraq, onun uşaq faylları və qovluqlarının əlaqəli olduğu bütün açarları tapmalısınız. Əvvəlki alt bölmələrdə biz açarları sıraladıq NodeKey belə ki, onlar ilk olaraq ana kataloq ID-si ilə sıralanır. Beləliklə, texniki olaraq, qovluğun məzmununu əldə etmək vəzifəsi kursoru verilmiş prefiksi olan düymələr qrupunun yuxarı sərhəddinə yerləşdirməyə, sonra isə aşağı sərhədə iterasiyaya qədər azaldılır.

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğu

Ardıcıl axtarışla yuxarı həddi "alında" tapa bilərsiniz. Bunun üçün kursor verilənlər bazasındakı açarların bütün siyahısının əvvəlində yerləşdirilir və sonra onun altında əsas kataloq identifikatoru olan açar görünənə qədər artırılır. Bu yanaşmanın 2 açıq çatışmazlığı var:

  1. Axtarışın xətti mürəkkəbliyi, bildiyiniz kimi, ümumiyyətlə ağaclarda və xüsusən də B ağacında, loqarifmik zamanda edilə bilər.
  2. Boş yerə, istədiyinizdən əvvəlki bütün səhifələr fayldan əsas yaddaşa qaldırılır ki, bu da olduqca bahalıdır.

Xoşbəxtlikdən, LMDB API kursoru ilkin olaraq yerləşdirməyin səmərəli yolunu təmin edir. Bunun üçün siz elə bir açar yaratmalısınız ki, onun dəyəri mütləq intervalın yuxarı sərhəddində yerləşən açardan az və ya ona bərabər olacaqdır. . Məsələn, yuxarıdakı şəkildəki siyahı ilə əlaqədar olaraq, sahənin olduğu bir açar edə bilərik parentId 2-yə bərabər olacaq, qalanların hamısı sıfırlarla doldurulur. Belə qismən doldurulmuş açar funksiyanın girişinə verilir mdb_cursor_get əməliyyatı göstərir MDB_SET_RANGE.

NodeKey upperBoundSearchKey = {​
    .parentId = 2,​
    .type = 0,​
    .nameLength = 0​
};​
MDB_val value, key = serialize(upperBoundSearchKey);​
MDB_cursor *cursor;​
mdb_cursor_open(..., &cursor);​
mdb_cursor_get(cursor, &key, &value, MDB_SET_RANGE);

Düymələr qrupunun yuxarı sərhəddi tapılarsa, ya biz, ya da açar başqası ilə qarşılaşana qədər onun üzərində təkrar edirik. parentId, yoxsa açarlar heç tükənməyəcək

do {​
    rc = mdb_cursor_get(cursor, &key, &value, MDB_NEXT);​
    // processing...​
} while (MDB_NOTFOUND != rc && // check end of table​
         IsTargetKey(key));    // check end of keys group​​

Nə gözəl, mdb_cursor_get istifadə edərək təkrarlamanın bir hissəsi olaraq biz yalnız açarı deyil, həm də dəyəri alırıq. Seçim şərtlərini yerinə yetirmək üçün, digər şeylər arasında, qeydin dəyər hissəsindən sahələri yoxlamaq lazımdırsa, onda onlar əlavə jestlər olmadan özləri üçün olduqca əlçatandırlar.

4.3. Cədvəllər arasında əlaqələrin modelləşdirilməsi

Bu günə qədər biz bir cədvəlli verilənlər bazası ilə dizaynın və işləməyin bütün aspektlərini nəzərdən keçirməyə nail olduq. Deyə bilərik ki, cədvəl eyni tipli açar-dəyər cütlərindən ibarət çeşidlənmiş qeydlər toplusudur. Əgər siz açarı düzbucaqlı kimi və onunla əlaqəli dəyərini qutu kimi göstərsəniz, verilənlər bazasının vizual diaqramını alırsınız.

â € <

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğu

Ancaq real həyatda bu qədər az qanla yaşamaq nadir hallarda olur. Çox vaxt verilənlər bazasında, birincisi, bir neçə cədvəlin olması, ikincisi, onlarda əsas açardan fərqli qaydada seçimlərin aparılması tələb olunur. Bu son bölmə onların yaradılması və qarşılıqlı əlaqəsi məsələlərinə həsr edilmişdir.

İndeks cədvəlləri

Bulud proqramında "Qalereya" bölməsi var. O, tarixə görə çeşidlənmiş bütün buluddan media məzmununu göstərir. Belə bir seçimin optimal şəkildə həyata keçirilməsi üçün əsas cədvəlin yanında yeni bir növ açarla başqa birini yaratmalısınız. Onlar əsas çeşidləmə meyarı kimi çıxış edəcək faylın yaradılma tarixi olan sahəni ehtiva edəcəklər. Yeni açarlar əsas cədvəldəki açarlarla eyni verilənlərə istinad etdiyi üçün onlar indeks açarları adlanır. Aşağıdakı şəkildə onlar narıncı ilə vurğulanır.

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğu

Eyni verilənlər bazası daxilində müxtəlif cədvəllərin açarlarını bir-birindən ayırmaq üçün onların hamısına əlavə texniki sahə tableId əlavə edilmişdir. Onu çeşidləmə üçün ən yüksək prioritet etməklə, biz açarları əvvəlcə cədvəllər üzrə qruplaşdıracağıq və artıq cədvəllərin içərisində öz qaydalarımıza uyğun olaraq qruplaşdıracağıq.

İndeks açarı əsas açarla eyni verilənlərə istinad edir. İlkin açarın dəyər hissəsinin surətini onunla əlaqələndirməklə bu əmlakın sadə şəkildə həyata keçirilməsi bir anda bir neçə nöqteyi-nəzərdən suboptimaldır:

  1. Məkan tutduğu nöqteyi-nəzərdən metadata kifayət qədər zəngin ola bilər.
  2. Performans baxımından, qovşaq metadatasını yeniləyərkən iki açarın üzərinə yazmalı olacaqsınız.
  3. Kod dəstəyi baxımından, nəhayət, açarlardan biri üçün məlumatları yeniləməyi unutsaq, yaddaşda məlumat uyğunsuzluğu ilə bağlı incə bir səhv alacağıq.

Sonra bu çatışmazlıqları necə aradan qaldıracağımızı nəzərdən keçirəcəyik.

Cədvəllər arasında əlaqələrin təşkili

Nümunə indeks cədvəlini əsas cədvəllə əlaqələndirmək üçün yaxşı uyğun gəlir "dəyər kimi açar". Adından da göründüyü kimi, indeks qeydinin dəyər hissəsi əsas açar dəyərinin surətidir. Bu yanaşma yuxarıda sadalanan əsas qeydin dəyər hissəsinin surətinin saxlanması ilə bağlı bütün çatışmazlıqları aradan qaldırır. Yeganə ödəniş odur ki, indeks açarı ilə dəyəri əldə etmək üçün verilənlər bazasına bir yox, 2 sorğu etməlisiniz. Sxematik olaraq nəticədə verilənlər bazası sxemi aşağıdakı kimidir.

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğu

Cədvəllər arasında əlaqələrin təşkili üçün başqa bir nümunə "artıq açar". Onun mahiyyəti açara əlavə atributlar əlavə etməkdir ki, onlar çeşidləmək üçün deyil, əlaqəli açarı yenidən yaratmaq üçün lazımdır.Lakin Mail.ru Bulud proqramında onun istifadəsinə dair real nümunələr var, lakin bu proqrama dərindən girməmək üçün. xüsusi iOS çərçivələrinin kontekstində mən uydurma, lakin daha başa düşülən bir nümunə verəcəyəm.

Bulud mobil müştərilərinin istifadəçinin digər insanlarla paylaşdığı bütün faylları və qovluqları göstərən səhifəsi var. Bu cür fayllar nisbətən az olduğundan və onlarla bağlı aşkarlıq haqqında bir çox xüsusi məlumat olduğundan (kimə giriş verilir, hansı hüquqlarla və s.), onu faylın dəyər hissəsi ilə yükləmək rasional olmaz. əsas cədvəldəki rekord. Bununla belə, bu cür faylları oflayn rejimdə göstərmək istəyirsinizsə, onda siz hələ də bir yerdə saxlamalısınız. Təbii bir həll onun üçün ayrıca bir masa yaratmaqdır. Aşağıdakı diaqramda onun açarına "P" prefiksi qoyulub və "propname" yertutanı daha konkret dəyər "ictimai məlumat" ilə əvəz edilə bilər.​

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğu

Yeni cədvəlin yaradıldığı bütün unikal metadata qeydin dəyər hissəsinə köçürülür. Eyni zamanda, mən artıq əsas cədvəldə saxlanılan fayl və qovluqlar haqqında məlumatları təkrarlamaq istəmirəm. Bunun əvəzinə, lazımsız məlumatlar "P" düyməsinə "qovşaq ID" və "zaman möhürü" sahələri şəklində əlavə olunur. Onların sayəsində siz əsas açarı əldə edə biləcəyiniz bir indeks açarı qura bilərsiniz, bununla da nəhayət, node metadatasını əldə edə bilərsiniz.

Nəticə

LMDB tətbiqinin nəticələrini müsbət qiymətləndiririk. Bundan sonra tətbiqlərin dondurulmalarının sayı 30% azalıb.

iOS proqramlarında əsas dəyər verilənlər bazası LMDB-nin parlaqlığı və yoxsulluğu

Görülən işlərin nəticələri iOS komandası xaricində cavab tapdı. Hazırda Android proqramında əsas “Fayllar” bölmələrindən biri də LMDB istifadəsinə keçib və digər hissələr yoldadır. Açar-dəyər yaddaşının tətbiq olunduğu C dili ilkin olaraq C++ dilində tətbiqi çarpaz platforma ilə əlaqələndirmək üçün yaxşı kömək oldu. Nəticədə C++ kitabxanasının Objective-C və Kotlin-də platforma kodu ilə problemsiz əlaqəsi üçün kod generatorundan istifadə edilmişdir. Cinni Dropbox-dan, amma bu başqa hekayədir.

Mənbə: www.habr.com

Добавить комментарий