iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligi

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligi

2019 yilning kuzida Mail.ru Cloud iOS jamoasida uzoq kutilgan voqea yuz berdi. Ilova holatini doimiy saqlash uchun asosiy ma'lumotlar bazasi mobil dunyo uchun juda ekzotik bo'lib qoldi Lightning Memory-Mapped Database (LMDB). Kesish ostida biz sizga to'rt qismda batafsil ko'rib chiqishni taklif qilamiz. Birinchidan, bunday noaniq va qiyin tanlovning sabablari haqida gapiraylik. Keyin biz LMDB arxitekturasining markazidagi uchta ustunni ko'rib chiqishga o'tamiz: xotira xaritasidagi fayllar, B+-daraxt, tranzaksiya va multiversiyani amalga oshirish uchun nusxa ko'chirish-yozish usuli. Va nihoyat, shirinlik uchun - amaliy qism. Unda biz past darajadagi kalit-qiymatli API-ning tepasida bir nechta jadvallar, jumladan indeksli jadvallar bilan ma'lumotlar bazasi sxemasini qanday loyihalash va amalga oshirishni ko'rib chiqamiz.

Mundarija

  1. Amalga oshirish uchun motivatsiya
  2. LMDB joylashuvi
  3. LMDB ning uchta ustuni
    3.1. №1 kit. Xotira bilan tuzilgan fayllar
    3.2. №2 kit. B+-daraxt
    3.3. №3 kit. Yozish bo'yicha nusxa ko'chirish
  4. Kalit-qiymati API ustidagi ma'lumotlar sxemasini loyihalash
    4.1. Asosiy abstraktsiyalar
    4.2. Jadvalni modellashtirish
    4.3. Jadvallar orasidagi munosabatlarni modellashtirish

1. Amalga oshirish uchun motivatsiya

2015-yilda bir yil, biz ilovamiz interfeysi qanchalik tez-tez kechikishini o‘lchashga qiynaldik. Biz buni bir sababga ko'ra qildik. Bizga tez-tez shikoyatlar kelib tushdi, ba'zida dastur foydalanuvchi harakatlariga javob berishni to'xtatadi: tugmalarni bosish mumkin emas, ro'yxatlar aylantirilmaydi va hokazo. O'lchovlar mexanikasi haqida dedi AvitoTech-da, shuning uchun men bu erda faqat raqamlar tartibini beraman.

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligi

O'lchov natijalari biz uchun sovuq dush bo'ldi. Ma'lum bo'lishicha, muzlashdan kelib chiqadigan muammolar boshqalarga qaraganda ko'proq. Agar bu haqiqatni tushunishdan oldin sifatning asosiy texnik ko'rsatkichi halokatsiz bo'lgan bo'lsa, diqqat markazidan keyin siljigan muzlashsiz.

Qurgan muzlashlar bilan asboblar paneli va sarflagandan keyin miqdoriy и sifatli ularning sabablarini tahlil qilganda, asosiy dushman aniq bo'ldi - dasturning asosiy qismida amalga oshirilgan og'ir biznes mantig'i. Bu sharmandalikka tabiiy munosabat uni ish oqimiga olib chiqish istagi edi. Ushbu muammoni tizimli ravishda hal qilish uchun biz engil aktyorlarga asoslangan ko'p tarmoqli arxitekturaga murojaat qildik. Men uni iOS dunyosiga moslashtirishga bag'ishladim ikkita ip jamoaviy Twitter va Habré haqidagi maqola. Joriy rivoyatning bir qismi sifatida men qarorning ma'lumotlar bazasini tanlashga ta'sir qilgan tomonlarini ta'kidlamoqchiman.

Tizimni tashkil etishning aktyor modeli ko'p oqim uning ikkinchi mohiyatiga aylanadi deb taxmin qiladi. Undagi model ob'ektlar oqim chegaralarini kesib o'tishni yaxshi ko'radi. Va ular buni ba'zan va u erda va u erda emas, balki deyarli doimo va hamma joyda qilishadi

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligi

Ma'lumotlar bazasi taqdim etilgan diagrammadagi asosiy komponentlardan biridir. Uning asosiy vazifasi makropatternni amalga oshirishdir Umumiy ma'lumotlar bazasi. Agar korxona dunyosida xizmatlar o'rtasida ma'lumotlar sinxronizatsiyasini tashkil qilish uchun foydalanilsa, u holda aktyor arxitekturasida - iplar orasidagi ma'lumotlar. Shunday qilib, bizga ko'p tarmoqli muhitda u bilan ishlashda minimal qiyinchiliklarga olib kelmaydigan ma'lumotlar bazasi kerak edi. Xususan, bu shuni anglatadiki, undan olingan ob'ektlar hech bo'lmaganda ip bilan xavfsiz bo'lishi kerak va ideal holda butunlay o'zgarmas bo'lishi kerak. Ma'lumki, ikkinchisi bir vaqtning o'zida bir nechta iplardan hech qanday qulflashga murojaat qilmasdan ishlatilishi mumkin, bu esa ishlashga foydali ta'sir ko'rsatadi.

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligiMa'lumotlar bazasini tanlashga ta'sir qilgan ikkinchi muhim omil bizning bulutli API edi. Bu git tomonidan qabul qilingan sinxronizatsiya yondashuvidan ilhomlangan. U kabi biz ham maqsad qilganmiz oflayn-birinchi API, bu bulutli mijozlar uchun ko'proq mos keladi. Ular bulutning to'liq holatini faqat bir marta chiqarib tashlaydi, keyin esa aksariyat hollarda sinxronizatsiya o'zgarishlarni tarqatish orqali sodir bo'ladi, deb taxmin qilingan. Afsuski, bu imkoniyat hali ham faqat nazariy zonada va mijozlar amalda yamoqlar bilan ishlashni o'rganmagan. Buning bir qator ob'ektiv sabablari bor, ularni kiritishni kechiktirmaslik uchun biz qavslarni ortda qoldiramiz. Endi, API "A" deb aytsa va uning iste'molchisi "B" deb aytmasa nima sodir bo'lishi haqidagi darsning ibratli xulosalari qiziqroq.

Shunday qilib, agar siz tortish buyrug'ini bajarishda mahalliy suratga yamoqlarni qo'llash o'rniga uning to'liq holatini to'liq server holati bilan taqqoslaydigan gitni tasavvur qilsangiz, bulutda sinxronizatsiya qanday sodir bo'lishi haqida juda aniq tasavvurga ega bo'lasiz. mijozlar. Buni amalga oshirish uchun siz xotirada barcha server va mahalliy fayllar haqida meta-ma'lumotlarga ega ikkita DOM daraxtini ajratishingiz kerakligini taxmin qilish oson. Ma'lum bo'lishicha, agar foydalanuvchi bulutda 500 ming faylni saqlasa, uni sinxronlashtirish uchun 1 million tugunli ikkita daraxtni qayta yaratish va yo'q qilish kerak bo'ladi. Ammo har bir tugun pastki ob'ektlar grafigini o'z ichiga olgan agregatdir. Shu nuqtai nazardan, profillash natijalari kutilgan edi. Ma'lum bo'lishicha, hatto birlashtirish algoritmini hisobga olmagan holda ham, juda ko'p sonli kichik ob'ektlarni yaratish va keyinchalik yo'q qilish jarayoni juda qimmatga tushadi.Vaziyat asosiy sinxronizatsiya operatsiyasining ko'p songa kiritilganligi bilan yanada og'irlashmoqda. foydalanuvchi skriptlari. Natijada, biz ma'lumotlar bazasini tanlashda ikkinchi muhim mezonni aniqlaymiz - ob'ektlarni dinamik taqsimlashsiz CRUD operatsiyalarini amalga oshirish qobiliyati.

Boshqa talablar ko'proq an'anaviy va ularning butun ro'yxati quyidagicha.

  1. Ip xavfsizligi.
  2. Ko'p ishlov berish. Vaziyatni nafaqat mavzular o'rtasida, balki asosiy dastur va iOS kengaytmalari o'rtasida ham sinxronlashtirish uchun bir xil ma'lumotlar bazasi namunasidan foydalanish istagi bilan bog'liq.
  3. Saqlangan ob'ektlarni o'zgarmas ob'ektlar sifatida ko'rsatish qobiliyati.​
  4. CRUD operatsiyalari ichida dinamik taqsimotlar yo'q.
  5. Asosiy xususiyatlar uchun tranzaksiyani qo'llab-quvvatlash ACID: atomiklik, mustahkamlik, izolyatsiya va ishonchlilik.
  6. Eng mashhur holatlarda tezlik.

Ushbu talablar to'plami bilan SQLite yaxshi tanlov bo'lgan va shunday bo'lib qoladi. Biroq, muqobillarni o'rganish doirasida men kitobga duch keldim "LevelDB bilan ishlashni boshlash". Uning rahbarligida haqiqiy bulutli stsenariylarda turli ma'lumotlar bazalari bilan ishlash tezligini taqqoslaydigan benchmark yozildi. Natija bizning kutganimizdan ham oshib ketdi. Eng mashhur holatlarda - barcha fayllarning tartiblangan ro'yxatida kursorni olish va ma'lum bir katalog uchun barcha fayllarning tartiblangan ro'yxati - LMDB SQLite'dan 10 baravar tezroq bo'lib chiqdi. Tanlov aniq bo'ldi.

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligi

2. LMDB joylashuvi

LMDB - bu juda kichik kutubxona (faqat 10K qator), u ma'lumotlar bazalarining eng past fundamental qatlamini - saqlashni amalga oshiradi.

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligi

Yuqoridagi diagramma shuni ko'rsatadiki, LMDB ni SQLite bilan taqqoslash, u ham yuqori darajalarni qo'llaydi, odatda SQLite bilan asosiy ma'lumotlarga qaraganda to'g'ri emas. BerkeleyDB, LevelDB, Sophia, RocksDB va boshqalarni teng raqobatchilar sifatida bir xil saqlash dvigatellarini keltirish adolatliroq bo'lar edi. Hatto LMDB SQLite uchun saqlash mexanizmi komponenti sifatida ishlaydigan ishlanmalar mavjud. Birinchi bunday tajriba 2012 yilda o'tkazilgan sarfladi LMDB tomonidan Xovard Chu. Natijalar Shu qadar qiziq bo'lib chiqdiki, uning tashabbusi OSS ishqibozlari tomonidan ko'tarilib, o'z davomini shaxsda topdi. LumoSQL. 2020 yil yanvar oyida ushbu loyiha muallifi Den Shirer edi taqdim etdi LinuxConfAu da.

LMDB asosan amaliy ma'lumotlar bazalari uchun vosita sifatida ishlatiladi. Kutubxona o'zining tashqi ko'rinishi uchun ishlab chiquvchilarga qarzdor OpenLDAP, ular BerkeleyDB dan o'z loyihasi uchun asos sifatida juda norozi edilar. Oddiy kutubxonadan boshlab btree, Xovard Chu bizning zamonamizning eng mashhur muqobillaridan birini yaratishga muvaffaq bo'ldi. U o'zining ajoyib hisobotini ushbu hikoyaga, shuningdek, LMDB ichki tuzilishiga bag'ishladi. "Lightning xotira xaritasidagi ma'lumotlar bazasi". Saqlash omborini zabt etishning yaxshi namunasini Leonid Yuryev (aka yleo) Positive Technologies kompaniyasidan Highload 2015-dagi hisobotida "LMDB dvigateli - bu maxsus chempion". Unda u LMDB haqida ReOpenLDAPni amalga oshirish bo'yicha shunga o'xshash vazifa kontekstida gapiradi va LevelDB allaqachon qiyosiy tanqidga uchragan. Amalga oshirish natijasida Positive Technologies hatto faol rivojlanayotgan vilkaga ega bo'ldi MDBX juda mazali xususiyatlar, optimallashtirish va xatolarni tuzatish.

LMDB ko'pincha asl xotira sifatida ishlatiladi. Masalan, Mozilla Firefox brauzeri tanladi u bir qator ehtiyojlar uchun va 9-versiyadan boshlab Xcode afzal indekslarni saqlash uchun uning SQLite.

Dvigatel mobil rivojlanish dunyosida ham o'z belgisini qoldirdi. Uning ishlatilishining izlari bo'lishi mumkin topish Telegram uchun iOS mijozida. LinkedIn bundan ham uzoqroqqa bordi va LMDB-ni o'zining uy sharoitida ishlab chiqarilgan ma'lumotlarni keshlash tizimi Rocket Data uchun standart xotira sifatida tanladi. aytdi 2016 yilda o'z maqolasida.

LMDB Oracle nazorati ostida bo'lganidan keyin BerkeleyDB tomonidan qoldirilgan tokchada quyoshdagi joy uchun muvaffaqiyatli kurashmoqda. Kutubxona, hatto tengdoshlari bilan solishtirganda, tezligi va ishonchliligi uchun seviladi. Ma'lumki, bepul tushlik yo'q va men LMDB va SQLite o'rtasida tanlov qilishda duch keladigan kelishuvni ta'kidlamoqchiman. Yuqoridagi diagramma tezlikni qanday oshirishni aniq ko'rsatib beradi. Birinchidan, biz diskni saqlash uchun qo'shimcha abstraksiya qatlamlari uchun to'lamaymiz. Yaxshi arxitektura hali ham ularsiz amalga oshirilmasligi aniq va ular muqarrar ravishda dastur kodida paydo bo'ladi, ammo ular ancha nozikroq bo'ladi. Ularda ma'lum bir dastur tomonidan talab qilinmaydigan xususiyatlar bo'lmaydi, masalan, SQL tilidagi so'rovlarni qo'llab-quvvatlash. Ikkinchidan, diskni saqlashga bo'lgan so'rovlar bo'yicha dastur operatsiyalarini xaritalashni optimal tarzda amalga oshirish mumkin bo'ladi. Agar SQLite mening ishimda o'rtacha ilovaning o'rtacha statistik ehtiyojlariga asoslangan bo'lsa, siz dastur ishlab chiqaruvchisi sifatida asosiy ish yuki stsenariylarini yaxshi bilasiz. Samaraliroq yechim uchun siz dastlabki yechimni ishlab chiqish uchun ham, uni keyingi qo'llab-quvvatlash uchun ham yuqori narxni to'lashingiz kerak bo'ladi.

3. LMDB ning uchta ustuni

LMDB ga qush nigohi bilan qaragandan so'ng, chuqurroq borish vaqti keldi. Keyingi uchta bo'lim saqlash arxitekturasi tayanadigan asosiy ustunlarni tahlil qilishga bag'ishlangan:

  1. Disk bilan ishlash va ichki ma'lumotlar tuzilmalarini sinxronlash mexanizmi sifatida xotira xaritasi.
  2. B+-daraxt saqlangan ma'lumotlar strukturasining tashkiloti sifatida.
  3. ACID tranzaksiya xususiyatlarini va multiversiyani ta'minlash uchun yondashuv sifatida nusxa ko'chirish.

3.1. №1 kit. Xotira bilan tuzilgan fayllar

Xotirada joylashgan fayllar shu qadar muhim me'moriy element bo'lib, ular hatto ombor nomida ham paydo bo'ladi. Saqlangan ma'lumotlarga kirishni keshlash va sinxronlashtirish masalalari to'liq operatsion tizimga yuklangan. LMDB o'zida hech qanday keshlarni o'z ichiga olmaydi. Bu muallifning ongli qaroridir, chunki xaritalangan fayllardan ma'lumotlarni to'g'ridan-to'g'ri o'qish dvigatelni amalga oshirishda juda ko'p burchaklarni kesishga imkon beradi. Quyida ulardan ba'zilarining to'liq ro'yxatidan uzoqdir.

  1. Saqlashda bir nechta jarayonlardan foydalanganda ma'lumotlarning izchilligini ta'minlash operatsion tizimning mas'uliyatiga aylanadi. Keyingi bo'limda ushbu mexanika batafsil va rasmlar bilan muhokama qilinadi.
  2. Keshlarning yo'qligi LMDB ni dinamik taqsimotlar bilan bog'liq bo'lgan qo'shimcha xarajatlardan butunlay yo'q qiladi. Amalda ma'lumotlarni o'qish virtual xotirada to'g'ri manzilga ko'rsatgichni o'rnatishni anglatadi va boshqa hech narsa emas. Bu ilmiy fantastikaga o'xshaydi, lekin saqlash manba kodida calloc-ga barcha qo'ng'iroqlar saqlash konfiguratsiyasi funksiyasida jamlangan.
  3. Keshlarning yo'qligi, shuningdek, ularga kirishni sinxronlashtirish bilan bog'liq qulflarning yo'qligini ham anglatadi. Bir vaqtning o'zida o'zboshimchalik bilan ko'p o'quvchilar bo'lishi mumkin bo'lgan o'quvchilar ma'lumotlarga borishda bitta mutexga duch kelmaydilar. Shu sababli, o'qish tezligi protsessorlar soniga asoslangan ideal chiziqli o'lchovga ega. LMDB da faqat o'zgartirish operatsiyalari sinxronlashtiriladi. Bir vaqtning o'zida faqat bitta yozuvchi bo'lishi mumkin.
  4. Minimal keshlash va sinxronizatsiya mantig'i ko'p tarmoqli muhitda ishlash bilan bog'liq o'ta murakkab turdagi xatolarni yo'q qiladi. Usenix OSDI 2014 konferentsiyasida ikkita qiziqarli ma'lumotlar bazasi tadqiqotlari bo'lib o'tdi: "Barcha fayl tizimlari teng yaratilmagan: halokatga uchragan dasturlarni yaratishning murakkabligi to'g'risida" и "Ma'lumotlar bazalarini o'yin-kulgi va foyda uchun qiynoqqa solish". Ulardan siz LMDB ning misli ko'rilmagan ishonchliligi va SQLite dan ustun bo'lgan ACID tranzaksiya xususiyatlarining deyarli benuqson amalga oshirilishi haqida ma'lumot olishingiz mumkin.
  5. LMDB ning minimalizmi uning kodining mashina ko'rinishini to'liq protsessorning L1 keshida keyingi tezlik xususiyatlariga ega bo'lishiga imkon beradi.

Afsuski, iOS-da, xotiraga moslashtirilgan fayllar bilan hamma narsa biz xohlagan darajada bulutsiz emas. Ular bilan bog'liq kamchiliklar haqida ko'proq ongli ravishda gapirish uchun ushbu mexanizmni operatsion tizimlarda amalga oshirishning umumiy tamoyillarini esga olish kerak.

Xotirada joylashgan fayllar haqida umumiy ma'lumot

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligiHar bir ishlayotgan dastur bilan operatsion tizim jarayon deb ataladigan ob'ektni bog'laydi. Har bir jarayon bir-biriga yaqin manzillar diapazoni ajratilgan bo'lib, unda u ishlashi uchun kerak bo'lgan hamma narsani joylashtiradi. Eng past manzillarda kodlar va qattiq kodlangan ma'lumotlar va resurslar mavjud bo'limlar mavjud. Keyinchalik, bizga uy nomi ostida yaxshi ma'lum bo'lgan dinamik manzil maydonining o'sib borayotgan bloki keladi. U dasturning ishlashi davomida paydo bo'ladigan ob'ektlarning manzillarini o'z ichiga oladi. Yuqorida ilovalar to'plami tomonidan ishlatiladigan xotira maydoni mavjud. U o'sadi yoki qisqaradi, boshqacha aytganda, uning hajmi ham dinamik xususiyatga ega. Stack va to'pning bir-biriga itarish va xalaqit berishiga yo'l qo'ymaslik uchun ular manzil maydonining turli uchlarida joylashgan.​ Yuqorida va pastda ikkita dinamik bo'lim o'rtasida teshik mavjud. Operatsion tizim turli ob'ektlarni jarayon bilan bog'lash uchun ushbu o'rta bo'limdagi manzillardan foydalanadi. Xususan, u ma'lum bir doimiy manzillar to'plamini diskdagi fayl bilan bog'lashi mumkin. Bunday fayl xotira xaritasi deb ataladi

Jarayonga ajratilgan manzil maydoni juda katta. Nazariy jihatdan, manzillar soni faqat tizimning bit sig'imi bilan belgilanadigan ko'rsatgichning o'lchami bilan chegaralanadi. Agar jismoniy xotira unga 1 dan 1 ga teng bo'lsa, birinchi jarayon butun operativ xotirani yutib yuboradi va hech qanday ko'p vazifa haqida gap bo'lmaydi.

Biroq, bizning tajribamizdan bilamizki, zamonaviy operatsion tizimlar bir vaqtning o'zida kerakli darajada ko'p jarayonlarni amalga oshirishi mumkin. Buning sababi, ular faqat qog'ozdagi jarayonlarga juda ko'p xotira ajratadi, lekin aslida ular asosiy jismoniy xotiraga faqat shu erda va hozir talab qilinadigan qismni yuklaydilar. Shuning uchun jarayon bilan bog'liq xotira virtual deb ataladi.

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligi

Operatsion tizim virtual va jismoniy xotirani ma'lum hajmdagi sahifalarga joylashtiradi. Virtual xotiraning ma'lum bir sahifasiga talab paydo bo'lishi bilan operatsion tizim uni jismoniy xotiraga yuklaydi va ularni maxsus jadvalda moslashtiradi. Agar bo'sh joy bo'lmasa, avval yuklangan sahifalardan biri diskka ko'chiriladi va talab qilinadigani o'z o'rnini egallaydi. Biz qisqa vaqt ichida qaytamiz, bu protsedura almashtirish deb ataladi. Quyidagi rasm tasvirlangan jarayonni ko'rsatadi. Unda 0-manzilli A sahifasi yuklandi va 4-manzilli asosiy xotira sahifasiga joylashtirildi. Bu fakt 0-uyachadagi yozishmalar jadvalida aks ettirilgan.​

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligi

Hikoya xotiraga joylashtirilgan fayllar bilan aynan bir xil. Mantiqan, ular go'yoki doimiy va to'liq virtual manzil maydonida joylashgan. Biroq, ular jismoniy xotirani sahifaga va faqat so'rov bo'yicha kiritadilar. Bunday sahifalarni o'zgartirish diskdagi fayl bilan sinxronlashtiriladi. Shunday qilib, siz xotiradagi baytlar bilan oddiygina ishlash orqali fayl kiritish-chiqarishni amalga oshirishingiz mumkin - barcha o'zgarishlar operatsion tizim yadrosi tomonidan avtomatik ravishda manba faylga o'tkaziladi.
مور
Quyidagi rasmda LMDB turli jarayonlardagi ma'lumotlar bazasi bilan ishlashda o'z holatini qanday sinxronlashtirishi ko'rsatilgan. Turli jarayonlarning virtual xotirasini bitta faylga solishtirib, biz amalda operatsion tizimni LMDB ko'rinadigan manzil bo'shliqlarining ma'lum bloklarini bir-biri bilan o'tishli sinxronlashtirishga majbur qilamiz.
مور

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligi

Muhim nuance shundaki, LMDB sukut bo'yicha ma'lumotlar faylini yozish tizimi chaqiruv mexanizmi orqali o'zgartiradi va faylning o'zini faqat o'qish rejimida ko'rsatadi. Ushbu yondashuv ikkita muhim oqibatlarga olib keladi.

Birinchi natija barcha operatsion tizimlar uchun umumiydir. Uning mohiyati noto'g'ri kod bilan ma'lumotlar bazasiga tasodifiy zarar etkazishdan himoya qo'shishdir. Ma'lumki, jarayonning bajariladigan ko'rsatmalari uning manzil maydonining istalgan joyidan ma'lumotlarga kirish uchun bepul. Shu bilan birga, biz eslaganimizdek, faylni o'qish-yozish rejimida ko'rsatish har qanday ko'rsatma ham uni o'zgartirishi mumkinligini anglatadi. Agar u buni noto'g'ri qilsa, masalan, mavjud bo'lmagan indeksdagi massiv elementini qayta yozishga harakat qilsa, u tasodifan ushbu manzilga ko'rsatilgan faylni o'zgartirishi mumkin, bu ma'lumotlar bazasining buzilishiga olib keladi. Agar fayl faqat o'qish rejimida ko'rsatilsa, tegishli manzil maydonini o'zgartirishga urinish dasturni signal bilan favqulodda to'xtatishga olib keladi. SIGSEGV, va fayl butunligicha qoladi.

Ikkinchi natija allaqachon iOS uchun xosdir. Na muallif, na boshqa manbalar bu haqda aniq eslatib o'tmaydi, lekin usiz LMDB ushbu mobil operatsion tizimda ishlash uchun mos bo'lmaydi. Keyingi bo'lim uni ko'rib chiqishga bag'ishlangan.

IOS-da xotira xaritasidagi fayllarning o'ziga xos xususiyatlari

2018-yilda WWDC da ajoyib hisobot bor edi "iOS xotirasiga chuqur sho'ng'ish". Bu bizga iOS-da jismoniy xotirada joylashgan barcha sahifalar 3 turdagi: iflos, siqilgan va toza ekanligini aytadi.

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligi

Toza xotira - bu jismoniy xotiradan og'riqsiz olib tashlanishi mumkin bo'lgan sahifalar to'plami. Ularda mavjud bo'lgan ma'lumotlar kerak bo'lganda asl manbalardan qayta yuklanishi mumkin. Faqat o'qish uchun mo'ljallangan xotira xaritasidagi fayllar ushbu toifaga kiradi. iOS istalgan vaqtda faylga ko'rsatilgan sahifalarni xotiradan olib tashlashdan qo'rqmaydi, chunki ular diskdagi fayl bilan sinxronlashtirilishi kafolatlanadi.
مور
O'zgartirilgan barcha sahifalar dastlab qayerda joylashganligidan qat'i nazar, iflos xotirada qoladi. Xususan, ular bilan bog'langan virtual xotiraga yozish orqali o'zgartirilgan xotira xaritasidagi fayllar shu tarzda tasniflanadi. LMDB bayroq bilan ochilmoqda MDB_WRITEMAP, unga o'zgartirish kiritganingizdan so'ng, buni shaxsan tekshirishingiz mumkin.​

Ilova juda ko'p jismoniy xotirani egallay boshlagach, iOS uni iflos sahifalarni siqishga duchor qiladi. Nopok va siqilgan sahifalar egallagan umumiy xotira dasturning xotira izi deb ataladigan joyni tashkil qiladi. Muayyan chegara qiymatiga yetgandan so'ng, OOM qotil tizimi demoni jarayondan so'ng keladi va uni majburan tugatadi. Bu ish stoli operatsion tizimlariga nisbatan iOS ning o'ziga xos xususiyati. Bundan farqli o'laroq, sahifalarni jismoniy xotiradan diskka almashtirish orqali xotira hajmini kamaytirish iOS-da ta'minlanmagan. Sabablarini faqat taxmin qilish mumkin. Ehtimol, sahifalarni diskka va orqaga intensiv ravishda ko'chirish jarayoni mobil qurilmalar uchun juda ko'p energiya sarflaydi yoki iOS SSD drayvlardagi hujayralarni qayta yozish resursini tejaydi yoki dizaynerlar tizimning umumiy ishlashidan qoniqmagan bo'lishi mumkin. doimiy ravishda almashtiriladi. Qanday bo'lmasin, haqiqat haqiqat bo'lib qolmoqda.

Yuqorida aytib o'tilgan yaxshi xabar shundaki, LMDB sukut bo'yicha fayllarni yangilash uchun mmap mexanizmidan foydalanmaydi. Bu shuni anglatadiki, ko'rsatilgan ma'lumotlar iOS tomonidan toza xotira sifatida tasniflanadi va xotira iziga hissa qo'shmaydi. Buni VM Tracker deb nomlangan Xcode vositasi yordamida tekshirishingiz mumkin. Quyidagi skrinshotda ish paytida Cloud ilovasining iOS virtual xotirasi holati ko'rsatilgan. Boshida 2 ta LMDB nusxasi ishga tushirildi. Birinchisiga o'z faylini 1GiB virtual xotirada ko'rsatishga ruxsat berildi, ikkinchisiga - 512MiB. Ikkala ombor ham ma'lum miqdordagi doimiy xotirani egallashiga qaramay, ularning hech biri iflos hajmga hissa qo'shmaydi.

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligi

Va endi yomon xabarlar vaqti keldi. 64-bitli ish stoli operatsion tizimlarida almashtirish mexanizmi tufayli har bir jarayon potentsial almashtirish uchun qattiq diskdagi bo'sh joy imkoni boricha virtual manzil maydonini egallashi mumkin. IOS-da almashtirishni siqish bilan almashtirish nazariy maksimalni tubdan kamaytiradi. Endi barcha tirik jarayonlar asosiy (o'qilgan RAM) xotiraga mos kelishi kerak va mos kelmaydiganlarning hammasi to'xtatilishi kerak. Bu yuqorida aytib o'tilganidek, aytilgan hisobot, va rasmiy hujjatlar. Natijada, iOS mmap orqali ajratish mumkin bo'lgan xotira miqdorini keskin cheklaydi. Bu yerga shu yerda Ushbu tizim chaqiruvi yordamida turli qurilmalarda ajratilishi mumkin bo'lgan xotira miqdorining empirik chegaralarini ko'rishingiz mumkin. Eng zamonaviy smartfon modellarida iOS 2 gigabaytga, iPad-ning yuqori versiyalarida esa - 4 ga saxiy bo'ldi. Amalda, albatta, siz eng kam qo'llab-quvvatlanadigan qurilma modellariga e'tibor qaratishingiz kerak, bu erda hamma narsa juda achinarli. Bundan ham yomoni, VM Tracker-da ilovaning xotira holatiga qarab, siz LMDB xotira xaritasiga kiritilgan yagona dastur emasligini bilib olasiz. Yaxshi qismlar tizim taqsimlovchilari, resurs fayllari, tasvir ramkalari va boshqa kichikroq yirtqichlar tomonidan iste'mol qilinadi.

Bulutdagi tajribalar natijalariga ko'ra, biz LMDB tomonidan ajratilgan xotira uchun quyidagi kelishuv qiymatlariga keldik: 384 bitli qurilmalar uchun 32 megabayt va 768 bitli qurilmalar uchun 64 megabayt. Ushbu hajm tugagandan so'ng, har qanday o'zgartirish operatsiyalari kod bilan tugaydi MDB_MAP_FULL. Biz monitoringimizda bunday xatolarni kuzatamiz, ammo ular etarlicha kichikki, bu bosqichda ularni e'tiborsiz qoldirish mumkin.

Saqlash tomonidan ortiqcha xotira sarflanishining aniq bo'lmagan sababi uzoq muddatli tranzaktsiyalar bo'lishi mumkin. Ushbu ikki hodisaning qanday bog'liqligini tushunish uchun bizga LMDB ning qolgan ikkita ustunini ko'rib chiqish yordam beradi.

3.2. №2 kit. B+-daraxt

Kalit-qiymat xotirasi ustidagi jadvallarni taqlid qilish uchun uning API-da quyidagi operatsiyalar mavjud bo'lishi kerak:

  1. Yangi element kiritish.
  2. Berilgan kalit bilan elementni qidiring.
  3. Elementni olib tashlash.
  4. Tugmachalarni tartiblangan tartibda takrorlash.

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligiTo'rtta operatsiyani osonlik bilan amalga oshiradigan eng oddiy ma'lumotlar strukturasi ikkilik qidiruv daraxtidir. Uning har bir tugunlari butun ichki kalitlarni ikkita kichik daraxtga ajratuvchi kalitni ifodalaydi. Chapda ota-onadan kichikroq, o'ngda esa kattaroq bo'lganlar mavjud. Buyurtma qilingan kalitlar to'plamini olish klassik daraxt o'tishlaridan biri orqali erishiladi.​

Ikkilik daraxtlarning ikkita asosiy kamchiliklari bor, ular diskka asoslangan ma'lumotlar tuzilmasi sifatida samarali bo'lishiga to'sqinlik qiladi. Birinchidan, ularning muvozanat darajasini oldindan aytib bo'lmaydi. Turli novdalarning balandligi sezilarli darajada farq qilishi mumkin bo'lgan daraxtlarni olishning katta xavfi mavjud, bu esa kutilganidan ko'ra qidiruvning algoritmik murakkabligini sezilarli darajada yomonlashtiradi. Ikkinchidan, tugunlar orasidagi o'zaro bog'lanishlarning ko'pligi binar daraxtlarni xotiradagi joylashuvdan mahrum qiladi.Yopiq tugunlar (ular orasidagi bog'lanish nuqtai nazaridan) virtual xotiraning butunlay boshqa sahifalarida joylashgan bo'lishi mumkin. Natijada, hatto daraxtdagi bir nechta qo'shni tugunlarni oddiy kesib o'tish ham shunga o'xshash sahifalarni ko'rishni talab qilishi mumkin. Bu hatto ikkilik daraxtlarning xotiradagi ma'lumotlar strukturasi sifatida samaradorligi haqida gapirganda ham muammo, chunki protsessor keshidagi sahifalarni doimiy ravishda aylantirish arzon zavq emas. Diskdan tugunlar bilan bog'liq sahifalarni tez-tez olish haqida gap ketganda, vaziyat butunlay bo'ladi achinarli.

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligiB-daraxtlar ikkilik daraxtlarning evolyutsiyasi bo'lib, oldingi paragrafda aniqlangan muammolarni hal qiladi. Birinchidan, ular o'z-o'zini muvozanatlashtiradi. Ikkinchidan, ularning har bir tugunlari bolalar kalitlari to'plamini 2 ga emas, balki M tartibli kichik to'plamlarga ajratadi va M soni bir necha yuz yoki hatto minglab tartib bilan juda katta bo'lishi mumkin.

Shunday qilib:

  1. Har bir tugunda allaqachon buyurtma qilingan ko'p sonli kalitlar mavjud va daraxtlar juda qisqa.
  2. Daraxt xotirada joylashish xususiyatiga ega bo'ladi, chunki qiymati yaqin bo'lgan kalitlar tabiiy ravishda bir xil yoki qo'shni tugunlarda bir-birining yonida joylashgan.
  3. Qidiruv operatsiyasi paytida daraxtga tushishda tranzit tugunlari soni kamayadi.
  4. Diapazon so'rovlarida o'qiladigan maqsadli tugunlar soni kamayadi, chunki ularning har birida allaqachon ko'p sonli tartiblangan kalitlar mavjud.

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligi

LMDB ma'lumotlarni saqlash uchun B + daraxti deb ataladigan B daraxtining o'zgarishidan foydalanadi. Yuqoridagi diagrammada unda mavjud bo'lgan uch turdagi tugunlar ko'rsatilgan:

  1. Yuqorida ildiz joylashgan. Bu ombor ichidagi ma'lumotlar bazasi tushunchasidan boshqa narsani amalga oshirmaydi. Bitta LMDB misolida siz xaritalangan virtual manzil maydonini baham ko'radigan bir nechta ma'lumotlar bazalarini yaratishingiz mumkin. Ularning har biri o'z ildizidan boshlanadi.
  2. Eng past darajada barglar joylashgan. Ular va faqat ular ma'lumotlar bazasida saqlangan kalit-qiymat juftlarini o'z ichiga oladi. Aytgancha, bu B + - daraxtlarning o'ziga xos xususiyati. Agar oddiy B daraxti qiymat qismlarini barcha darajadagi tugunlarda saqlasa, u holda B + o'zgarishi faqat eng pastda bo'ladi. Ushbu faktni aniqlab bo'lgach, biz LMDB-da ishlatiladigan daraxtning pastki turini oddiygina B-daraxt deb ataymiz.
  3. Ildiz va barglar o'rtasida navigatsiya (tarmoq) tugunlari bo'lgan 0 yoki undan ortiq texnik darajalar mavjud. Ularning vazifasi saralangan kalitlar to'plamini barglar o'rtasida bo'lishdir.

Jismoniy jihatdan tugunlar oldindan belgilangan uzunlikdagi xotira bloklaridir. Ularning o'lchami biz yuqorida muhokama qilgan operatsion tizimdagi xotira sahifalarining o'lchamidan ko'pdir. Tugun tuzilishi quyida ko'rsatilgan. Sarlavha meta-ma'lumotni o'z ichiga oladi, ulardan eng aniqi, masalan, nazorat summasi. Keyinchalik ma'lumotlarga ega hujayralar joylashgan ofsetlar haqida ma'lumot keladi. Ma'lumotlar kalitlar bo'lishi mumkin, agar biz navigatsiya tugunlari haqida gapiradigan bo'lsak, yoki barglar holatida butun kalit-qiymat juftlari bo'lishi mumkin.​ Ishdagi sahifalar tuzilishi haqida ko'proq o'qishingiz mumkin. "Yuqori samarali kalit-qiymatli do'konlarni baholash".

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligi

Sahifa tugunlarining ichki mazmunini ko'rib chiqqach, biz LMDB B daraxtini quyidagi shaklda soddalashtirilgan tarzda taqdim etamiz.

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligi

Tugunlari bo'lgan sahifalar diskda ketma-ket joylashgan. Yuqori raqamlangan sahifalar faylning oxirida joylashgan. Meta-sahifada barcha daraxtlarning ildizlarini topish mumkin bo'lgan ofsetlar haqida ma'lumot mavjud. Faylni ochishda LMDB yaroqli meta-sahifani izlash uchun faylni sahifadan oxirigacha skanerlaydi va u orqali mavjud ma'lumotlar bazalarini topadi.

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligi

Endi ma'lumotlarni tashkil etishning mantiqiy va jismoniy tuzilishi haqida tasavvurga ega bo'lgan holda, biz LMDB ning uchinchi ustunini ko'rib chiqishga o'tishimiz mumkin. Aynan uning yordami bilan barcha saqlash modifikatsiyalari tranzaktsion tarzda va bir-biridan ajratilgan holda sodir bo'lib, ma'lumotlar bazasiga umuman multiversiya xususiyatini beradi.

3.3. №3 kit. Yozish bo'yicha nusxa ko'chirish

Ba'zi B-daraxt operatsiyalari uning tugunlariga bir qator o'zgarishlar kiritishni o'z ichiga oladi. Bir misol, allaqachon maksimal quvvatiga yetgan tugunga yangi kalit qo'shishdir. Bunday holda, birinchidan, tugunni ikkiga bo'lish, ikkinchidan, uning ota-onasidagi yangi tugun tuguniga havola qo'shish kerak. Ushbu protsedura potentsial juda xavflidir. Agar biron sababga ko'ra (halokat, elektr uzilishi va h.k.) ketma-ketlikdagi o'zgarishlarning faqat bir qismi sodir bo'lsa, u holda daraxt mos kelmaydigan holatda qoladi.

Ma'lumotlar bazasini nosozliklarga chidamli qilishning an'anaviy echimlaridan biri B-daraxt yoniga diskdagi qo'shimcha ma'lumotlar strukturasini qo'shishdir - tranzaktsiyalar jurnali, shuningdek, oldindan yozish jurnali (WAL) sifatida ham tanilgan. Bu fayl bo'lib, uning oxirida mo'ljallangan operatsiya B-daraxtning o'zini o'zgartirishdan oldin yoziladi. Shunday qilib, agar o'z-o'zini tashxislash paytida ma'lumotlarning buzilishi aniqlansa, ma'lumotlar bazasi o'zini tartibga solish uchun jurnalga murojaat qiladi.

LMDB o'zining xatolarga chidamlilik mexanizmi sifatida boshqa usulni tanladi, bu ko'chirish-on-write. Uning mohiyati shundaki, u mavjud sahifadagi ma'lumotlarni yangilash o'rniga, avval uni to'liq nusxalaydi va nusxadagi barcha o'zgarishlarni amalga oshiradi.

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligi

Keyinchalik, yangilangan ma'lumotlar mavjud bo'lishi uchun uning asosiy tugunida joriy bo'lgan tugunga havolani o'zgartirish kerak. Buning uchun u ham o'zgartirilishi kerakligi sababli, u ham oldindan ko'chiriladi. Jarayon ildizga qadar rekursiv davom etadi. O'zgartirish kerak bo'lgan oxirgi narsa - bu meta-sahifadagi ma'lumotlar.​

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligi

Agar yangilash jarayonida to'satdan jarayon buzilib qolsa, yangi meta-sahifa yaratilmaydi yoki u diskka to'liq yozilmaydi va uning nazorat summasi noto'g'ri bo'ladi. Ushbu ikki holatda ham yangi sahifalarga kirish imkonsiz bo'ladi, lekin eski sahifalarga ta'sir qilmaydi. Bu ma'lumotlarning izchilligini ta'minlash uchun LMDB ga oldindan yozish zaruriyatini yo'q qiladi. Haqiqatan ham, yuqorida tavsiflangan diskda ma'lumotlarni saqlash tuzilishi bir vaqtning o'zida o'z vazifasini oladi. Aniq tranzaksiya jurnalining yo'qligi LMDB ning yuqori ma'lumotlarni o'qish tezligini ta'minlovchi xususiyatlaridan biridir.

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligi

Olingan dizayn, faqat qo'shimchalar uchun B-daraxt deb ataladi, tabiiyki, tranzaksiya izolyatsiyasi va ko'p versiyalilikni ta'minlaydi. LMDB da har bir ochiq tranzaksiya joriy daraxt ildizi bilan bog'lanadi. Tranzaksiya tugamaguncha, u bilan bog'langan daraxt sahifalari hech qachon o'zgartirilmaydi yoki ma'lumotlarning yangi versiyalari uchun qayta ishlatilmaydi.Shunday qilib, siz o'sha paytda tegishli bo'lgan ma'lumotlar to'plami bilan xohlaganingizcha ishlashingiz mumkin. tranzaktsiya ochildi, garchi xotira hozirda faol ravishda yangilanishda davom etsa ham. Bu multiversiyaning mohiyati bo'lib, LMDB ni bizning sevgilimiz uchun ideal ma'lumotlar manbai qiladi UICollectionView. Tranzaktsiyani ochgandan so'ng, hech narsa qolmasligidan qo'rqib, joriy ma'lumotlarni tezkor ravishda ba'zi bir xotira tuzilmalariga quyish orqali ilovaning xotira hajmini oshirishning hojati yo'q. Bu xususiyat LMDB-ni bir xil SQLite-dan ajratib turadi, u bunday umumiy izolyatsiya bilan maqtana olmaydi. Ikkinchisida ikkita tranzaktsiyani ochib, ulardan birida ma'lum bir yozuvni o'chirib tashlaganingizdan so'ng, qolgan ikkinchisida bir xil yozuvni endi olish mumkin bo'lmaydi.

Tanganing teskari tomoni virtual xotiraning potentsial sezilarli darajada yuqori iste'molidir. Slayd ma'lumotlar bazasining turli versiyalarini ko'rib chiqadigan 3 ta ochiq o'qish tranzaktsiyalari bilan bir vaqtda o'zgartirilsa, ma'lumotlar bazasi tuzilishi qanday ko'rinishini ko'rsatadi. LMDB joriy tranzaktsiyalar bilan bog'liq bo'lgan ildizlardan kirish mumkin bo'lgan tugunlarni qayta ishlata olmaganligi sababli, do'konda xotirada yana to'rtinchi ildizni ajratish va uning ostidagi o'zgartirilgan sahifalarni yana bir marta klonlashdan boshqa iloji yo'q.

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligi

Bu erda xotira xaritasidagi fayllar bo'limini eslash foydali bo'ladi. Ko'rinishidan, virtual xotiraning qo'shimcha iste'moli bizni unchalik tashvishlantirmasligi kerak, chunki bu dasturning xotira iziga hissa qo'shmaydi. Biroq, shu bilan birga, iOS uni taqsimlashda juda ziqna ekanligi ta'kidlandi va biz server yoki ish stolida bo'lgani kabi, LMDB hududini 1 terabayt bilan ta'minlay olmaymiz va bu xususiyat haqida umuman o'ylamaymiz. Iloji bo'lsa, tranzaktsiyalar muddatini iloji boricha qisqaroq qilishga harakat qilishingiz kerak.

4. API kalit-qiymati ustidagi ma'lumotlar sxemasini loyihalash

Keling, API tahlilimizni LMDB tomonidan taqdim etilgan asosiy abstraksiyalarni ko'rib chiqishdan boshlaylik: muhit va ma'lumotlar bazalari, kalitlar va qiymatlar, tranzaksiyalar va kursorlar.

Kod ro'yxati haqida eslatma

Ommaviy LMDB API’dagi barcha funksiyalar o‘z ishining natijasini xato kodi ko‘rinishida qaytaradi, ammo keyingi barcha ro‘yxatlarda qisqalik uchun uni tekshirish o‘tkazib yuborilgan. sanchqi C++ o'ramlari lmdbxx, unda xatolar C++ istisnolari sifatida amalga oshiriladi.

LMDB-ni iOS yoki macOS uchun loyihaga ulashning eng tezkor usuli sifatida men CocoaPod-ni taklif qilaman. POSLMDB.

4.1. Asosiy abstraktsiyalar

Atrof-muhit

tuzilma MDB_env LMDB ning ichki holatining ombori hisoblanadi. Prefiksli funksiyalar oilasi mdb_env uning ayrim xususiyatlarini sozlash imkonini beradi. Eng oddiy holatda, dvigatelni ishga tushirish shunday ko'rinadi.

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

Mail.ru Cloud ilovasida biz faqat ikkita parametrning standart qiymatlarini o'zgartirdik.

Birinchisi, saqlash fayli ko'rsatilgan virtual manzil maydonining o'lchamidir. Afsuski, hatto bir xil qurilmada ham o'ziga xos qiymat ishga tushirishdan boshlab sezilarli darajada farq qilishi mumkin. iOS ning ushbu xususiyatini hisobga olish uchun maksimal saqlash hajmi dinamik ravishda tanlanadi. Muayyan qiymatdan boshlab, funktsiyaga qadar ketma-ket ikkiga bo'linadi mdb_env_open dan farqli natijani qaytarmaydi ENOMEM. Nazariy jihatdan, teskari yo'l ham bor - avval dvigatelga minimal xotira ajrating, so'ngra xatoliklar yuzaga kelganda, MDB_MAP_FULL, uni oshiring. Biroq, u ancha tikanli. Sababi, funksiya yordamida xotirani qayta taqsimlash (remap) tartibi mdb_env_set_map_size avval dvigateldan olingan barcha ob'ektlarni (kursorlar, operatsiyalar, kalitlar va qiymatlar) bekor qiladi. Kodeksda voqealarning bu aylanishini hisobga olish uning sezilarli darajada murakkablashishiga olib keladi. Agar virtual xotira siz uchun juda muhim bo'lsa, bu juda oldinda bo'lgan vilkalarni diqqat bilan ko'rib chiqish uchun sabab bo'lishi mumkin. MDBX, bu erda e'lon qilingan xususiyatlar orasida "ma'lumotlar bazasi hajmini avtomatik sozlash" mavjud.

Standart qiymati bizga mos kelmagan ikkinchi parametr ipning xavfsizligini ta'minlash mexanikasini tartibga soladi. Afsuski, hech bo'lmaganda iOS 10-da tarmoqni mahalliy saqlashni qo'llab-quvvatlash bilan bog'liq muammolar mavjud. Shu sababli, yuqoridagi misolda ombor bayroq bilan ochiladi MDB_NOTLS. Bunga qo'shimcha ravishda, bu ham zarur edi sanchqi C++ o'rami lmdbxxushbu atributga ega va undagi o'zgaruvchilarni kesish uchun.

Ma'lumotlar bazalari

Ma'lumotlar bazasi alohida B-daraxt namunasi bo'lib, biz yuqorida muhokama qildik. Uning ochilishi avvaliga biroz g'alati tuyulishi mumkin bo'lgan tranzaksiya ichida sodir bo'ladi.

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);

Haqiqatan ham, LMDB-dagi tranzaktsiya ma'lum bir ma'lumotlar bazasi ob'ekti emas, balki saqlash ob'ektidir. Ushbu kontseptsiya turli xil ma'lumotlar bazalarida joylashgan ob'ektlar ustida atom operatsiyalarini bajarishga imkon beradi. Nazariy jihatdan, bu turli xil ma'lumotlar bazalari ko'rinishidagi jadvallarni modellashtirish imkoniyatini ochadi, lekin bir vaqtning o'zida men quyida batafsil tavsiflangan boshqa yo'lni tanladim.

Kalitlar va qiymatlar

tuzilma MDB_val kalit va qiymat tushunchasini modellashtiradi. Omborda ularning semantikasi haqida hech qanday tasavvur yo'q. Uning uchun boshqa narsa ma'lum hajmdagi baytlar massividir. Maksimal kalit hajmi 512 bayt.

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

Taqqoslovchidan foydalanib, do'kon kalitlarni o'sish tartibida tartiblaydi. Agar siz uni o'zingizniki bilan almashtirmasangiz, ularni leksikografik tartibda bayt-bayt saralaydigan sukut bo'yicha foydalaniladi.

Jurnallar

Tranzaksiya tuzilishi batafsil tavsiflangan oldingi bob, shuning uchun men ularning asosiy xususiyatlarini qisqacha takrorlayman:

  1. Barcha asosiy xususiyatlarni qo'llab-quvvatlaydi ACID: atomiklik, mustahkamlik, izolyatsiya va ishonchlilik. MDBX-da tuzatilgan macOS va iOS-ning chidamliligi bo'yicha xatolik borligini ta'kidlay olmayman. Ularda ko'proq o'qishingiz mumkin README.
  2. Ko'p oqimga yondashuv "bitta yozuvchi / bir nechta o'quvchi" sxemasi bilan tavsiflanadi. Yozuvchilar bir-birlarini to'sib qo'yishadi, lekin o'quvchilarni to'sib qo'ymaydilar. O'quvchilar yozuvchilarni yoki bir-birlarini bloklamaydilar.
  3. Ichki tranzaktsiyalarni qo'llab-quvvatlash.
  4. Multiversiyani qo'llab-quvvatlash.

LMDB-dagi multiversiya shunchalik yaxshiki, men uni amalda ko'rsatmoqchiman. Quyidagi koddan siz har bir tranzaksiya ma'lumotlar bazasining ochilgan paytda joriy bo'lgan versiyasi bilan ishlashini va keyingi barcha o'zgarishlardan butunlay ajratilganligini ko'rishingiz mumkin. Saqlashni boshlash va unga sinov yozuvini qo'shish hech qanday qiziqarli narsani anglatmaydi, shuning uchun bu marosimlar buzuvchi ostida qoladi.

Sinov yozuvini qo'shish

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);

Men sizga SQLite bilan bir xil hiylani sinab ko'rishingizni va nima sodir bo'lishini ko'rishingizni tavsiya qilaman.

Multiversion iOS dasturchisi hayotiga juda yaxshi imtiyozlar olib keladi. Ushbu xususiyatdan foydalanib, foydalanuvchi tajribasini hisobga olgan holda, ekran shakllari uchun ma'lumotlar manbasini yangilash tezligini osongina va tabiiy ravishda sozlashingiz mumkin. Masalan, tizim media galereyasidan kontentni avtomatik yuklash kabi Mail.ru Cloud ilovasining xususiyatini olaylik. Yaxshi ulanish bilan mijoz serverga soniyada bir nechta fotosuratlarni qo'shishi mumkin. Har bir yuklashdan keyin yangilasangiz UICollectionView foydalanuvchi bulutidagi media kontenti bilan siz ushbu jarayon davomida 60 kadr / sekundni unutishingiz va silliq siljishni unutishingiz mumkin. Tez-tez ekran yangilanishining oldini olish uchun siz qandaydir tarzda asosiy ma'lumotlarning o'zgarishi tezligini cheklashingiz kerak UICollectionViewDataSource.

Agar ma'lumotlar bazasi ko'p versiyani qo'llab-quvvatlamasa va faqat joriy holat bilan ishlashga imkon bersa, ma'lumotlarning vaqtga to'g'ri keladigan suratini yaratish uchun siz uni xotiradagi ma'lumotlar tuzilmasi yoki vaqtinchalik jadvalga nusxalashingiz kerak. Ushbu yondashuvlarning har biri juda qimmat. Xotirada saqlash holatida biz qurilgan ob'ektlarni saqlash natijasida kelib chiqadigan xotirada ham, ortiqcha ORM o'zgarishlari bilan bog'liq vaqt o'tishi bilan ham xarajatlarni olamiz. Vaqtinchalik stolga kelsak, bu yanada qimmatroq zavq, faqat ahamiyatsiz bo'lmagan holatlarda mantiqiy.

LMDB ning ko'p versiyali yechimi barqaror ma'lumotlar manbasini saqlash muammosini juda oqlangan tarzda hal qiladi. Bitta tranzaktsiyani ochish kifoya - biz uni tugatmagunimizcha, ma'lumotlar to'plami tuzatilishi kafolatlanadi. Uning yangilanish tezligi mantig'i endi butunlay taqdimot qatlamining qo'lida bo'lib, muhim resurslarni sarflamaydi.

Kursorlar

Kursorlar B-daraxt o'tish orqali kalit-qiymat juftliklarini tartibli takrorlash mexanizmini ta'minlaydi. Ularsiz biz hozir murojaat qiladigan ma'lumotlar bazasidagi jadvallarni samarali modellashtirish mumkin emas edi.

4.2. Jadvalni modellashtirish

Kalitlarni tartiblash xususiyati sizga asosiy abstraktsiyalar ustidagi jadval kabi yuqori darajadagi abstraksiyani qurish imkonini beradi. Keling, ushbu jarayonni barcha foydalanuvchi fayllari va papkalari haqidagi ma'lumotlarni keshlaydigan bulutli mijozning asosiy jadvali misolida ko'rib chiqaylik.

Jadval sxemasi

Jildlar daraxti bilan jadval tuzilmasi moslashtirilishi kerak bo'lgan umumiy stsenariylardan biri ma'lum bir katalog ichida joylashgan barcha elementlarni tanlashdir.Ushbu turdagi samarali so'rovlar uchun yaxshi ma'lumotlarni tashkil etish modeli. Yaqinlik ro'yxati. Uni kalit-qiymatli xotiraning yuqori qismida amalga oshirish uchun fayl va papkalarning kalitlarini shunday saralash kerakki, ular ota-katalogga a'zoligiga qarab guruhlanadi. Bundan tashqari, katalog tarkibini Windows foydalanuvchisiga tanish bo'lgan shaklda ko'rsatish uchun (birinchi papkalar, so'ngra ikkalasi ham alifbo tartibida tartiblangan fayllar), kalitga tegishli qo'shimcha maydonlarni kiritish kerak.

Quyidagi rasmda topshiriqdan kelib chiqib, bayt massivi ko'rinishidagi kalitlarning ko'rinishi qanday ko'rinishini ko'rsatadi. Bosh katalog identifikatori (qizil) bo'lgan baytlar avval, so'ngra turi (yashil) va dumida nomi (ko'k) bilan joylashtiriladi.Sukut bo'yicha LMDB solishtiruvchisi tomonidan leksikografik tartibda saralangan holda, ular quyidagi tartibda joylashtiriladi. talab qilinadigan usul. Bir xil qizil prefiksli tugmachalarni ketma-ket bosib o'tish bizga qo'shimcha ishlov berishni talab qilmasdan, foydalanuvchi interfeysida (o'ngda) ko'rsatilishi kerak bo'lgan tartibda ularning tegishli qiymatlarini beradi.

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligi

Kalitlar va qiymatlarni ketma-ketlashtirish

Dunyoda ob'ektlarni ketma-ketlashtirishning ko'plab usullari ixtiro qilingan. Tezlikdan boshqa talabimiz bo‘lmagani uchun biz o‘zimiz uchun imkon qadar tezroqni tanladik – Si tili strukturasining namunasi egallagan xotira bo‘g‘ini.Shunday qilib, katalog elementining kalitini quyidagi struktura bilan modellashtirish mumkin. NodeKey.

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

Saqlamoq NodeKey ob'ektda zarur bo'lgan saqlashda MDB_val ma'lumotlar ko'rsatgichini strukturaning boshi manziliga joylashtiring va funksiya bilan ularning hajmini hisoblang sizeof.

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

Ma'lumotlar bazasini tanlash mezonlari bo'yicha birinchi bobda men muhim tanlash omili sifatida CRUD operatsiyalarida dinamik taqsimotlarni minimallashtirishni eslatib o'tdim. Funktsiya kodi serialize LMDB holatida ma'lumotlar bazasiga yangi yozuvlarni kiritishda qanday qilib ulardan butunlay qochish mumkinligini ko'rsatadi. Serverdan kiruvchi baytlar massivi birinchi navbatda stek tuzilmalariga aylantiriladi, so'ngra ular arzimas tarzda saqlashga tashlanadi. LMDB ichida dinamik taqsimotlar ham mavjud emasligini hisobga olsak, iOS standartlari bo'yicha fantastik holatga erishishingiz mumkin - tarmoqdan diskgacha bo'lgan butun yo'l bo'ylab ma'lumotlar bilan ishlash uchun faqat stek xotirasidan foydalaning!

Ikkilik komparator bilan kalitlarga buyurtma berish

Kalit tartib munosabatlari komparator deb ataladigan maxsus funktsiya tomonidan belgilanadi. Dvigatel ulardagi baytlarning semantikasi haqida hech narsa bilmasligi sababli, standart taqqoslash tugmachalarini bayt-bayt taqqoslashga murojaat qilib, leksikografik tartibda joylashtirishdan boshqa tanlovga ega emas. Undan tuzilmalarni tartibga solish uchun foydalanish maydalagich boltasi bilan tarashga o'xshaydi. Biroq, oddiy holatlarda men bu usulni maqbul deb bilaman. Muqobil variant quyida tasvirlangan, ammo bu erda men ushbu yo'l bo'ylab tarqalgan bir nechta rakelarni qayd etaman.

Esda tutish kerak bo'lgan birinchi narsa - bu ibtidoiy ma'lumotlar turlarining xotira tasviri. Shunday qilib, barcha Apple qurilmalarida butun o'zgaruvchilar formatda saqlanadi Kichkina Endian. Bu shuni anglatadiki, eng kam ahamiyatli bayt chap tomonda bo'ladi va bayt-bayt taqqoslash yordamida butun sonlarni tartiblash mumkin bo'lmaydi. Misol uchun, buni 0 dan 511 gacha bo'lgan raqamlar to'plami bilan bajarishga urinish quyidagi natijani beradi.

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

Bu muammoni hal qilish uchun butun sonlar kalitda bayt-bayt komparatoriga mos formatda saqlanishi kerak. Oilaning funktsiyalari sizga kerakli o'zgarishlarni amalga oshirishga yordam beradi hton* (ayniqsa htons misoldagi ikki baytli raqamlar uchun).

Dasturlashda satrlarni ifodalash formati, siz bilganingizdek, bir butundir tarix. Agar satrlarning semantikasi, shuningdek ularni xotirada aks ettirish uchun ishlatiladigan kodlash har bir belgi uchun bir baytdan ortiq bo'lishi mumkinligini ko'rsatsa, u holda standart taqqoslagichdan foydalanish g'oyasidan darhol voz kechish yaxshiroqdir.

Yodda tutish kerak bo'lgan ikkinchi narsa moslashtirish tamoyillari struktura maydoni kompilyatori. Ular tufayli maydonlar orasidagi xotirada axlat qiymatlari bo'lgan baytlar hosil bo'lishi mumkin, bu, albatta, bayt-baytlarni saralashni buzadi. Chiqindilarni yo'q qilish uchun siz qat'iy belgilangan tartibda maydonlarni e'lon qilishingiz, tekislash qoidalarini yodda tutishingiz yoki tuzilma deklaratsiyasida atributdan foydalanishingiz kerak. packed.

Tashqi komparator bilan kalitlarga buyurtma berish

Kalit taqqoslash mantig'i ikkilik komparator uchun juda murakkab bo'lishi mumkin. Ko'pgina sabablardan biri - tuzilmalar ichida texnik maydonlarning mavjudligi. Men ularning paydo bo'lishini bizga allaqachon tanish bo'lgan katalog elementi kaliti misolida ko'rsataman.

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

Oddiyligiga qaramay, aksariyat hollarda u juda ko'p xotirani iste'mol qiladi. Nom uchun bufer 256 baytni egallaydi, garchi o'rtacha fayl va papka nomlari kamdan-kam hollarda 20-30 belgidan oshadi.

Yozuv hajmini optimallashtirishning standart usullaridan biri uni haqiqiy o'lchamga "qirqish" dir. Uning mohiyati shundan iboratki, barcha o'zgaruvchan uzunlikdagi maydonlar tarkibi strukturaning oxiridagi buferda saqlanadi va ularning uzunligi alohida o'zgaruvchilarda saqlanadi.​ Ushbu yondashuvga ko'ra, kalit NodeKey quyidagicha aylantiriladi.

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

Bundan tashqari, seriyalashda ma'lumotlar hajmi ko'rsatilmaydi sizeof butun tuzilma va barcha maydonlarning o'lchami belgilangan uzunlik va buferning amalda ishlatiladigan qismining o'lchamidir.

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

Refaktoring natijasida biz kalitlar egallagan joyni sezilarli darajada tejashga erishdik. Biroq, texnik soha tufayli nameLength, standart ikkilik komparator endi kalit taqqoslash uchun mos emas. Agar biz uni o'zimiz bilan almashtirmasak, unda nomning uzunligi nomning o'zidan ko'ra saralashda ustuvor omil bo'ladi.

LMDB har bir ma'lumotlar bazasiga o'zining kalit taqqoslash funktsiyasiga ega bo'lish imkonini beradi. Bu funksiya yordamida amalga oshiriladi mdb_set_compare ochilishdan oldin. Aniq sabablarga ko'ra, uni ma'lumotlar bazasining ishlash muddati davomida o'zgartirib bo'lmaydi. Taqqoslovchi kirish sifatida ikkilik formatdagi ikkita kalitni oladi va chiqishda taqqoslash natijasini qaytaradi: (-1 dan kichik), katta (1) yoki (0) ga teng. uchun psevdokod NodeKey shunday ko'rinadi.

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 // ...
}​

Ma'lumotlar bazasidagi barcha kalitlar bir xil turdagi ekan, ularning bayt ko'rinishini so'zsiz dastur kaliti strukturasi turiga o'tkazish qonuniydir. Bu erda bitta nuance bor, lekin u quyida "O'qish yozuvlari" bo'limida muhokama qilinadi.

Qiymatlarni ketma-ketlashtirish

LMDB saqlangan yozuvlar kalitlari bilan juda intensiv ishlaydi. Ularni bir-biri bilan taqqoslash har qanday qo'llaniladigan operatsiya doirasida sodir bo'ladi va butun yechimning ishlashi komparatorning tezligiga bog'liq. Ideal dunyoda standart ikkilik komparator kalitlarni solishtirish uchun etarli bo'lishi kerak, ammo agar siz o'zingiznikidan foydalanishingiz kerak bo'lsa, undagi kalitlarni seriyadan chiqarish tartibi iloji boricha tezroq bo'lishi kerak.

Ma'lumotlar bazasi yozuvning qiymat qismiga (qiymatiga) ayniqsa qiziqmaydi. Uni bayt tasviridan ob'ektga o'tkazish faqat dastur kodi tomonidan talab qilinganida, masalan, uni ekranda ko'rsatish uchun sodir bo'ladi. Bu nisbatan kamdan-kam hollarda sodir bo'lganligi sababli, ushbu protsedura uchun tezlik talablari unchalik muhim emas va uni amalga oshirishda biz qulayliklarga e'tibor qaratishimiz mumkin.Masalan, hali yuklab olinmagan fayllar haqidagi metama'lumotlarni ketma-ketlashtirish uchun biz foydalanamiz. NSKeyedArchiver.

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

Biroq, ishlash hali ham muhim bo'lgan paytlar bor. Masalan, foydalanuvchi bulutining fayl tuzilishi haqidagi metama'lumotni saqlashda biz bir xil ob'ektlar xotirasidan foydalanamiz. Ularning ketma-ket ko'rinishini yaratish vazifasining asosiy jihati shundaki, katalog elementlari sinflar ierarxiyasi tomonidan modellashtirilgan.

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligi

Uni C tilida amalga oshirish uchun merosxo'rlarning o'ziga xos sohalari alohida tuzilmalarga joylashtiriladi va ularning asosiy bilan aloqasi tip birlashma maydoni orqali belgilanadi. Birlashmaning haqiqiy tarkibi texnik atribut turi orqali belgilanadi.

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

Yozuvlarni qo'shish va yangilash

Seriyalashtirilgan kalit va qiymat do'konga qo'shilishi mumkin. Buning uchun funksiyadan foydalaning mdb_put.

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

Konfiguratsiya bosqichida saqlash bir xil kalit bilan bir nechta yozuvlarni saqlashga ruxsat berilishi yoki taqiqlanishi mumkin.Agar kalitlarni takrorlash taqiqlangan bo'lsa, u holda yozuvni kiritishda siz mavjud yozuvni yangilashga ruxsat yoki yo'qligini aniqlashingiz mumkin. Agar eskirish faqat koddagi xatolik natijasida yuzaga kelsa, siz bayroqni ko'rsatish orqali o'zingizni undan himoya qilishingiz mumkin. NOOVERWRITE.

Yozuvlarni o'qish

LMDB da yozuvlarni o'qish uchun funksiyadan foydalaning mdb_get. Agar kalit-qiymat juftligi ilgari tashlab yuborilgan tuzilmalar bilan ifodalangan bo'lsa, unda bu protsedura quyidagicha ko'rinadi.

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

Taqdim etilgan ro'yxat strukturani dump orqali ketma-ketlashtirish nafaqat yozishda, balki ma'lumotlarni o'qishda ham dinamik taqsimotlardan xalos bo'lish imkonini beradi. Funktsiyadan olingan mdb_get ko'rsatkich ma'lumotlar bazasi ob'ektning bayt tasvirini saqlaydigan virtual xotira manziliga aniq qaraydi. Darhaqiqat, biz juda yuqori ma'lumotlarni o'qish tezligini deyarli bepul ta'minlaydigan ORM turini olamiz. Yondashuvning barcha go'zalligiga qaramay, u bilan bog'liq bo'lgan bir nechta xususiyatlarni esga olish kerak.

  1. Faqat o'qish uchun tranzaksiya uchun qiymat tuzilmasi ko'rsatkichi tranzaksiya yopilgunga qadar amalda bo'lishi kafolatlanadi. Yuqorida ta'kidlab o'tilganidek, ob'ekt joylashgan B-daraxt sahifalari, yozishga nusxa ko'chirish printsipi tufayli, kamida bitta tranzaksiyaga havola qilinsa, o'zgarishsiz qoladi. Shu bilan birga, ular bilan bog'langan oxirgi tranzaksiya tugashi bilan sahifalar yangi ma'lumotlar uchun qayta ishlatilishi mumkin. Agar ob'ektlar ularni yaratgan tranzaktsiyadan omon qolishlari kerak bo'lsa, ular hali ham nusxalanishi kerak.
  2. Qayta yozish tranzaktsiyasi uchun natijada olingan qiymat tuzilishiga ko'rsatgich faqat birinchi o'zgartirish protsedurasigacha (ma'lumotlarni yozish yoki o'chirish) amal qiladi.
  3. Tuzilishi bo'lsa-da NodeValue to'liq emas, balki kesilgan ("Tashqi taqqoslagich yordamida kalitlarga buyurtma berish" bo'limiga qarang), siz uning maydonlariga ko'rsatgich orqali xavfsiz kirishingiz mumkin. Asosiysi, undan voz kechmaslik!
  4. Hech qanday holatda strukturani qabul qilingan ko'rsatgich orqali o'zgartirmaslik kerak. Barcha o'zgarishlar faqat usul orqali amalga oshirilishi kerak mdb_put. Biroq, buni qanchalik qiyin bo'lmasin, buning iloji bo'lmaydi, chunki ushbu tuzilma joylashgan xotira maydoni faqat o'qish rejimida xaritaga kiritilgan.
  5. Masalan, funktsiyadan foydalanib, maksimal saqlash hajmini oshirish maqsadida faylni jarayon manzil maydoniga o'zgartiring mdb_env_set_map_size barcha operatsiyalarni va umuman bog'liq shaxslarni to'liq bekor qiladi va xususan, ayrim ob'ektlarga ishora qiladi.

Va nihoyat, yana bir xususiyat shu qadar hiyla-nayrangki, uning mohiyatini ochib berish faqat boshqa paragrafga to'g'ri kelmaydi. B-daraxt haqidagi bobda men uning sahifalari xotirada qanday joylashtirilganligi diagrammasini berdim. Bundan kelib chiqadiki, ketma-ketlashtirilgan ma'lumotlarga ega bufer boshlanishining manzili mutlaqo o'zboshimchalik bilan bo'lishi mumkin. Shu sababli, strukturada ularga ko'rsatgich olingan MDB_val va strukturaga ko'rsatgichga qisqartirilsa, u umumiy holatda tekislanmagan bo'lib chiqadi. Shu bilan birga, ba'zi chiplarning arxitekturasi (iOS holatida bu armv7) har qanday ma'lumotlarning manzili mashina so'zining o'lchamiga yoki boshqacha qilib aytganda, tizimning bit hajmiga ko'p bo'lishini talab qiladi ( armv7 uchun bu 32 bit). Boshqacha qilib aytganda, operatsiya kabi *(int *foo)0x800002 ular bo'yicha qochishga teng va hukm bilan ijroga olib keladi EXC_ARM_DA_ALIGN. Bunday ayanchli taqdirdan qochishning ikki yo'li bor.

Birinchisi, ma'lumotlarni aniq moslashtirilgan tuzilishga oldindan nusxalash uchun mo'ljallangan. Masalan, moslashtirilgan komparatorda bu quyidagicha aks etadi.

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 // ...
}

Muqobil usul - kompilyatorni kalit-qiymat tuzilmalari atributlarga mos kelmasligi haqida oldindan xabardor qilishdir. aligned(1). ARM-da siz xuddi shunday ta'sirga ega bo'lishingiz mumkin erishish va packed atributidan foydalanish. Bu struktura egallagan joyni optimallashtirishga ham yordam berishini hisobga olsak, bu usul menga afzalroq tuyuladi, garchi Privodit ma'lumotlarga kirish operatsiyalari narxining oshishiga.

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

Diapazon so'rovlari

Yozuvlar guruhini takrorlash uchun LMDB kursor abstraktsiyasini taqdim etadi. Keling, bizga allaqachon tanish bo'lgan foydalanuvchi bulutli metama'lumotlariga ega jadval misolidan foydalanib, u bilan qanday ishlashni ko'rib chiqaylik.

Katalogdagi fayllar ro'yxatini ko'rsatishning bir qismi sifatida uning asosiy fayllari va papkalari bog'langan barcha kalitlarni topish kerak. Oldingi bo'limlarda biz kalitlarni tartibladik NodeKey shunday qilib, ular birinchi navbatda ota-katalogning identifikatori bilan tartibga solinadi. Shunday qilib, texnik jihatdan, jildning mazmunini olish vazifasi kursorni berilgan prefiksli tugmalar guruhining yuqori chegarasiga qo'yish va keyin pastki chegaraga takrorlashdan iborat.

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligi

Yuqori chegarani to'g'ridan-to'g'ri ketma-ket qidirish orqali topish mumkin. Buning uchun kursor ma'lumotlar bazasidagi kalitlarning butun ro'yxatining boshiga joylashtiriladi va uning ostida asosiy katalog identifikatori bo'lgan kalit paydo bo'lguncha oshiriladi. Ushbu yondashuvning ikkita aniq kamchiliklari bor:

  1. Chiziqli qidiruv murakkabligi, ma'lumki, umuman daraxtlarda va ayniqsa, B-daraxtda logarifmik vaqtda amalga oshirilishi mumkin.
  2. Bekorga, qidirilayotgan sahifadan oldingi barcha sahifalar fayldan asosiy xotiraga ko'tariladi, bu juda qimmat.

Yaxshiyamki, LMDB API kursorni dastlab joylashtirishning samarali usulini taqdim etadi.Buni amalga oshirish uchun qiymati intervalning yuqori chegarasida joylashgan kalitdan aniq yoki unga teng bo'lgan kalitni yaratish kerak. Masalan, yuqoridagi rasmdagi ro'yxatga nisbatan, biz qaysi maydonda kalitni yaratishimiz mumkin parentId 2 ga teng bo'ladi, qolganlari esa nol bilan to'ldiriladi. Bunday qisman to'ldirilgan kalit funktsiya kiritishiga beriladi mdb_cursor_get operatsiyani ko'rsatadi 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);

Agar kalitlar guruhining yuqori chegarasi topilsa, u holda biz uchrashgunimizcha yoki kalit boshqasiga duch kelguncha uni takrorlaymiz. parentId, yoki kalitlar umuman tugamaydi

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​​

Qizig'i shundaki, mdb_cursor_get yordamida iteratsiyaning bir qismi sifatida biz nafaqat kalitni, balki qiymatni ham olamiz. Agar namuna olish shartlarini bajarish uchun siz boshqa narsalar qatorida yozuvning qiymat qismidagi maydonlarni tekshirishingiz kerak bo'lsa, ularga qo'shimcha imo-ishoralarsiz kirish mumkin.

4.3. Jadvallar orasidagi munosabatlarni modellashtirish

Hozirgacha biz bitta jadvalli ma'lumotlar bazasini loyihalash va u bilan ishlashning barcha jihatlarini ko'rib chiqishga muvaffaq bo'ldik. Aytishimiz mumkinki, jadval bir xil turdagi kalit-qiymat juftliklaridan tashkil topgan tartiblangan yozuvlar to'plamidir. Agar siz kalitni to'rtburchaklar shaklida va tegishli qiymatni parallelepiped sifatida ko'rsatsangiz, siz ma'lumotlar bazasining vizual diagrammasini olasiz.

مور

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligi

Biroq, haqiqiy hayotda juda kam qon to'kish bilan o'tish mumkin. Ko'pincha ma'lumotlar bazasida, birinchidan, bir nechta jadvallar bo'lishi kerak, ikkinchidan, ularda asosiy kalitdan farqli tartibda tanlash kerak. Ushbu oxirgi bo'lim ularni yaratish va o'zaro bog'liqlik masalalariga bag'ishlangan.

Indeks jadvallari

Bulutli ilovada "Galereya" bo'limi mavjud. U butun bulutdagi media kontentini sana bo'yicha saralab ko'rsatadi. Bunday tanlovni optimal tarzda amalga oshirish uchun asosiy jadvalning yonida siz yangi turdagi kalitlarga ega boshqasini yaratishingiz kerak. Ularda asosiy saralash mezoni bo'lib xizmat qiladigan fayl yaratilgan sana ko'rsatilgan maydon bo'ladi. Yangi kalitlar asosiy jadvaldagi kalitlar bilan bir xil ma'lumotlarga murojaat qilganligi sababli, ular indeks kalitlari deb ataladi. Quyidagi rasmda ular to'q sariq rangda ta'kidlangan.

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligi

Bitta ma'lumotlar bazasida turli jadvallarning kalitlarini bir-biridan ajratish uchun ularning barchasiga qo'shimcha texnik maydon tableId qo'shildi. Buni saralashning eng yuqori ustuvorligiga aylantirib, biz kalitlarni birinchi navbatda jadvallar bo'yicha, jadvallar ichida esa - o'z qoidalarimiz bo'yicha guruhlashga erishamiz.

Indeks kaliti asosiy kalit bilan bir xil ma'lumotlarga murojaat qiladi. Birlamchi kalitning qiymat qismining nusxasini u bilan bog'lash orqali ushbu xususiyatni to'g'ridan-to'g'ri amalga oshirish bir necha nuqtai nazardan maqbul emas:

  1. Qabul qilingan joy bo'yicha metama'lumotlar juda boy bo'lishi mumkin.
  2. Ishlash nuqtai nazaridan, tugunning metama'lumotlarini yangilashda siz ikkita tugmachani ishlatib, uni qayta yozishingiz kerak bo'ladi.
  3. Kodni qo'llab-quvvatlash nuqtai nazaridan, agar biz kalitlardan biri uchun ma'lumotlarni yangilashni unutib qo'ysak, biz saqlashda ma'lumotlarning nomuvofiqligining qiyin xatosini olamiz.

Keyinchalik, ushbu kamchiliklarni qanday bartaraf etishni ko'rib chiqamiz.

Jadvallar orasidagi munosabatlarni tashkil qilish

Naqsh indeks jadvalini asosiy jadval bilan bog'lash uchun juda mos keladi "qiymat sifatida kalit". Nomidan ko'rinib turibdiki, indeks yozuvining qiymat qismi asosiy kalit qiymatining nusxasidir. Ushbu yondashuv birlamchi yozuvning qiymat qismining nusxasini saqlash bilan bog'liq yuqorida aytib o'tilgan barcha kamchiliklarni bartaraf etadi. Yagona xarajat shundaki, indeks kaliti bo'yicha qiymat olish uchun siz ma'lumotlar bazasiga bitta so'rov o'rniga 2 ta so'rov qilishingiz kerak. Sxematik ravishda olingan ma'lumotlar bazasi sxemasi shunday ko'rinadi.

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligi

Jadvallar orasidagi munosabatlarni tashkil qilishning yana bir namunasi "ortiqcha kalit". Uning mohiyati kalitga qo'shimcha atributlarni qo'shishdan iborat bo'lib, ular saralash uchun emas, balki bog'langan kalitni qayta yaratish uchun zarurdir.Mail.ru Cloud ilovasida undan foydalanishning haqiqiy misollari mavjud, ammo chuqur sho'ng'imaslik uchun. muayyan iOS ramkalari kontekstida men xayoliy, ammo aniqroq misol keltiraman.​

Bulutli mobil mijozlarda foydalanuvchi boshqa odamlar bilan baham ko'rgan barcha fayl va papkalarni ko'rsatadigan sahifa mavjud. Bunday fayllar nisbatan kam bo'lgani uchun va ular bilan bog'liq bo'lgan oshkoralik to'g'risida juda ko'p turli xil aniq ma'lumotlar mavjud (kimga ruxsat berilgan, qanday huquqlar va boshqalar), faylning qiymat qismini yuklash oqilona bo'lmaydi. u bilan asosiy jadvalga yozing. Biroq, bunday fayllarni oflayn rejimda ko'rsatishni istasangiz, uni hali ham biror joyda saqlashingiz kerak. Tabiiy yechim - buning uchun alohida jadval yaratish. Quyidagi diagrammada uning kalitiga "P" prefiksi qo'yilgan va "propname" o'rnini egallagan "ommaviy ma'lumot" yanada aniqroq qiymat bilan almashtirilishi mumkin.

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligi

Yangi jadval yaratilgan saqlash uchun barcha noyob metama'lumotlar yozuvning qiymat qismida joylashtirilgan. Shu bilan birga, siz asosiy jadvalda allaqachon saqlangan fayl va papkalar haqidagi ma'lumotlarni takrorlashni xohlamaysiz. Buning o'rniga, ortiqcha ma'lumotlar "P" tugmachasiga "tugun identifikatori" va "vaqt tamg'asi" maydonlari ko'rinishida qo'shiladi. Ularning yordami bilan siz indeks kalitini yaratishingiz mumkin, undan siz asosiy kalitni olishingiz mumkin, undan nihoyat tugun metama'lumotlarini olishingiz mumkin.

Xulosa

Biz LMDBni amalga oshirish natijalarini ijobiy baholaymiz. Shundan so'ng, ilovalarni muzlatish soni 30% ga kamaydi.

iOS ilovalaridagi LMDB kalit-qiymat bazasining yorqinligi va qashshoqligi

Bajarilgan ish natijalari iOS jamoasidan tashqarida aks sado berdi. Hozirda Android ilovasidagi asosiy "Fayllar" bo'limlaridan biri ham LMDB dan foydalanishga o'tdi va boshqa qismlar yo'lda. Kalit-qiymat ombori amalga oshirilgan C tili dastlab C++ da o'zaro platformalar atrofida dastur asosini yaratishda yaxshi yordam berdi. Olingan C++ kutubxonasini Objective-C va Kotlin-dagi platforma kodi bilan uzluksiz ulash uchun kod generatori ishlatilgan. Jinni Dropbox-dan, lekin bu butunlay boshqacha hikoya.

Manba: www.habr.com

a Izoh qo'shish