Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOS

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOS

Ing musim gugur 2019, ana acara sing ditunggu-tunggu ing tim Mail.ru Cloud iOS. Database utama kanggo panyimpenan terus-terusan saka negara aplikasi wis dadi eksotis banget kanggo jagad seluler Database Peta-Memori Kilat (LMDB). Ing ngisor potong kita menehi review rinci babagan patang bagean. Pisanan, ayo ngomong babagan alasan kanggo pilihan sing ora pati penting lan angel. Banjur kita bakal nerusake menyang nimbang telung pilar ing jantung arsitektur LMDB: file memori-dipetake, B + -wit, pendekatan copy-on-write kanggo ngleksanakake transactionality lan multiversion. Pungkasan, kanggo panganan cuci mulut - bagean praktis. Ing kono kita bakal weruh carane ngrancang lan ngleksanakake skema basis data kanthi sawetara tabel, kalebu indeks, ing ndhuwur API nilai kunci tingkat rendah.

Isi

  1. Motivasi kanggo implementasine
  2. LMDB Positioning
  3. Telung pilar LMDB
    3.1. Paus #1. File sing dipetakan memori
    3.2. Paus #2. B+ - wit
    3.3. Paus #3. Copy-on-write
  4. Ngrancang skema data ing ndhuwur API kunci-nilai
    4.1. abstraksi dhasar
    4.2. Modeling Tabel
    4.3. Hubungan modeling antarane tabel

1. Motivasi kanggo implementasine

Setaun ing 2015, kita njupuk alangan kanggo ngukur sepira kerepe antarmuka aplikasi kita lags. Kita nindakake iki kanthi alesan. Kita wis nampa keluhan luwih kerep sing kadhangkala aplikasi mandheg nanggapi tumindak pangguna: tombol ora bisa dipencet, dhaptar ora nggulung, etc. Babagan mekanika pangukuran marang ing AvitoTech, mula aku mung menehi urutan nomer.

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOS

Asil pangukuran dadi udan adhem kanggo kita. Ternyata ana akeh masalah liyane sing disebabake beku tinimbang liyane. Yen sadurunge nyadari kasunyatan iki indikator teknis utama kualitas ora kacilakan, banjur sawise fokus dipindhah ing freeze.

Wis dibangun dashboard karo freezes lan sawise mbuwang kuantitatif и kualitatif analisis alasan sing, mungsuh utama dadi cetha - logika bisnis abot dieksekusi ing utas utama aplikasi. Reaksi alami kanggo aib iki yaiku kepinginan kanggo nyurung menyang aliran kerja. Kanggo ngrampungake masalah iki kanthi sistematis, kita nggunakake arsitektur multi-threaded adhedhasar aktor entheng. Aku darmabakti kanggo adaptasi kanggo donya iOS rong benang ing Twitter kolektif lan artikel ing Habré. Minangka bagéan saka narasi saiki, aku pengin nandheske aspek-aspek keputusan sing mengaruhi pilihan database.​

Model aktor organisasi sistem nganggep manawa multithreading dadi inti sing nomer loro. Objek model ing kono seneng ngliwati wates stream. Lan padha nindakake iki ora kadhangkala lan ing kene, nanging meh terus-terusan lan ing endi wae

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOS

Basis data minangka salah sawijining komponen dhasar ing diagram sing ditampilake. Tugas utamane yaiku ngetrapake pola makro Basis data sing dienggo bareng. Yen ing donya perusahaan digunakake kanggo ngatur sinkronisasi data antarane layanan, banjur ing kasus arsitektur aktor - data antarane Utas. Mangkono, kita butuh database sing ora bakal nyebabake kesulitan minimal nalika nggarap ing lingkungan multi-threaded. Utamane, iki tegese obyek sing dipikolehi kudu paling ora aman, lan saenipun ora bisa diganti. Kaya sing sampeyan ngerteni, sing terakhir bisa digunakake kanthi bebarengan saka pirang-pirang benang tanpa nggunakake kunci apa wae, sing duweni efek sing bermanfaat ing kinerja.

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOSFaktor penting nomer loro sing mengaruhi pilihan database yaiku API awan kita. Iki diilhami dening pendekatan sinkronisasi sing diadopsi dening git. Kaya dheweke, kita ngarahake offline-pisanan API, sing katon luwih cocok kanggo klien awan. Dianggep manawa mung bakal ngompa kabeh awan sapisan, banjur sinkronisasi ing mayoritas kasus bakal kedadeyan liwat owah-owahan. Sayange, kesempatan iki isih mung ing zona teoretis, lan klien durung sinau cara nggarap patch ing praktik. Ana sawetara alasan objektif kanggo iki, sing, supaya ora nundha introduksi, kita bakal ninggalake kurung. Saiki, sing luwih menarik yaiku kesimpulan pelajaran babagan apa sing kedadeyan nalika API ujar "A" lan konsumen ora ujar "B".

Dadi, yen sampeyan mbayangno git, sing, nalika nglakokake perintah tarik, tinimbang nglamar patch menyang snapshot lokal, mbandhingake status lengkap karo negara server lengkap, mula sampeyan bakal duwe ide sing cukup akurat babagan sinkronisasi ing awan. klien. Iku gampang kanggo guess sing kanggo ngleksanakake, sampeyan kudu nyedhiakke loro wit DOM ing memori karo meta-informasi babagan kabeh server lan file lokal. Pranyata yen pangguna nyimpen 500 ewu file ing méga, mula kanggo nyinkronake kudu nggawe maneh lan ngrusak rong wit kanthi 1 yuta kelenjar. Nanging saben simpul minangka agregat sing ngemot grafik subobyek. Ing cahya iki, asil profiling wis samesthine. Ternyata, sanajan ora nggatekake algoritma penggabungan, prosedur nggawe lan numpes obyek cilik sing akeh banget regane cukup sen. saka skrip pangguna. Akibaté, kita ndandani kritéria penting kapindho ing milih database - kemampuan kanggo ngleksanakake operasi CRUD tanpa alokasi dinamis obyek.

Syarat liyane luwih tradisional lan dhaptar kabeh kaya ing ngisor iki.

  1. Keamanan thread.
  2. Multiprocessing. Dictated dening kepinginan kanggo nggunakake conto database padha kanggo nyinkronake negara ora mung antarane Utas, nanging uga antarane aplikasi utama lan ekstensi iOS.
  3. Kemampuan kanggo makili entitas sing disimpen minangka obyek sing ora bisa diganti.​
  4. Ora ana alokasi dinamis ing operasi CRUD.
  5. Dhukungan transaksi kanggo sifat dhasar ACID: atomicity, konsistensi, isolasi lan linuwih.
  6. Kacepetan ing kasus sing paling populer.

Kanthi set syarat kasebut, SQLite tetep dadi pilihan sing apik. Nanging, minangka bagéan saka sinau alternatif, aku ketemu buku "Miwiti karo LevelDB". Ing pimpinan dheweke, pathokan ditulis mbandhingake kacepetan kerja karo macem-macem database ing skenario maya nyata. Asil ngluwihi pangarepan wildest kita. Ing kasus sing paling populer - njupuk kursor ing dhaptar kabeh file sing diurutake lan dhaptar kabeh file sing diurutake kanggo direktori tartamtu - LMDB dadi 10 kaping luwih cepet tinimbang SQLite. Pilihan dadi ketok.

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOS

2. LMDB Positioning

LMDB minangka perpustakaan sing cilik banget (mung 10K baris) sing ngetrapake lapisan dhasar dhasar paling murah - panyimpenan.

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOS

Diagram ing ndhuwur nuduhake yen mbandhingake LMDB karo SQLite, sing uga ngetrapake tingkat sing luwih dhuwur, umume ora luwih bener tinimbang SQLite karo Data Inti. Iku bakal luwih adil kanggo nyebut mesin panyimpenan padha saingan witjaksono - BerkeleyDB, LevelDB, Sophia, RocksDB, etc.. Malah ana pembangunan ngendi LMDB tumindak minangka komponen mesin panyimpenan kanggo SQLite. Eksperimen kasebut pisanan ana ing 2012 ngginakaken dening LMDB Howard Chu. Результаты ternyata menarik banget, mula inisiatif kasebut dijupuk dening para penggemar OSS, lan nemokake kelanjutane ing wong kasebut. LumoSQL. Ing Januari 2020, penulis proyek iki yaiku Den Shearer diwenehi iku ing LinuxConfAu.

LMDB utamané digunakake minangka mesin kanggo database aplikasi. Perpustakaan kasebut duwe utang marang para pangembang OpenLDAP, sing banget ora marem karo BerkeleyDB minangka basis kanggo proyek. Miwiti saka perpustakaan andhap asor btree, Howard Chu bisa nggawe salah sawijining alternatif sing paling populer ing jaman saiki. Dheweke ngaturake laporan sing apik banget kanggo crita iki, uga kanggo struktur internal LMDB. "Database Pemetaan Memori Kilat". Conto apik kanggo nelukake fasilitas panyimpenan dituduhake dening Leonid Yuryev (aka yleo) saka Positive Technologies ing laporan ing Highload 2015 "Mesin LMDB minangka juara khusus". Ing kono, dheweke ngomong babagan LMDB ing konteks tugas sing padha kanggo ngetrapake ReOpenLDAP, lan LevelDB wis kena kritik komparatif. Minangka asil implementasine, Positive Technologies malah duwe garpu sing aktif berkembang MDBX karo fitur banget sedhep, optimizations lan bugfixes.

LMDB asring digunakake minangka panyimpenan. Contone, browser Mozilla Firefox milih iku kanggo sawetara kabutuhan, lan, miwiti saka versi 9, Xcode disenengi SQLite kanggo nyimpen indeks.

Mesin kasebut uga nggawe tandha ing jagad pangembangan seluler. Tilak saka nggunakake bisa kanggo golek ing klien iOS kanggo Telegram. LinkedIn luwih maju lan milih LMDB minangka panyimpenan standar kanggo kerangka caching data asale saka Rocket Data, babagan iki. marang ing artikel ing 2016.

LMDB kasil nglawan papan ing srengenge ing ceruk sing ditinggalake BerkeleyDB sawise dikendhaleni Oracle. Perpustakaan kasebut ditresnani amarga kacepetan lan linuwih, sanajan dibandhingake karo kanca-kancane. Kaya sing sampeyan ngerteni, ora ana nedha awan gratis, lan aku pengin nandheske trade-off sing kudu sampeyan adhepi nalika milih antarane LMDB lan SQLite. Diagram ing ndhuwur kanthi jelas nuduhake carane nambah kacepetan. Kaping pisanan, kita ora mbayar lapisan tambahan abstraksi ing ndhuwur panyimpenan disk. Cetha yen arsitektur apik isih ora bisa nindakake tanpa wong, lan mesthi bakal katon ing kode aplikasi, nanging bakal luwih subtler. Dheweke ora bakal ngemot fitur sing ora dibutuhake dening aplikasi tartamtu, contone, dhukungan kanggo pitakon ing basa SQL. Kapindho, dadi bisa ngleksanakake pemetaan operasi aplikasi kanthi optimal ing panjalukan kanggo panyimpenan disk. Yen SQLite ing karyaku adhedhasar kabutuhan statistik rata-rata aplikasi rata-rata, mula sampeyan, minangka pangembang aplikasi, ngerti babagan skenario beban kerja utama. Kanggo solusi sing luwih produktif, sampeyan kudu mbayar rega tambah kanggo pangembangan solusi awal lan kanggo dhukungan sabanjure.

3. Telung pilar LMDB

Sawise ndeleng LMDB saka mripat manuk, wektune luwih jero. Telung bagean sabanjure bakal ditrapake kanggo analisis pilar utama sing ana ing arsitektur panyimpenan:

  1. File sing dipetakan memori minangka mekanisme kanggo nggarap disk lan nyinkronake struktur data internal.
  2. B + -tree minangka organisasi saka struktur data sing disimpen.
  3. Copy-on-write minangka pendekatan kanggo nyedhiyakake properti transaksi ACID lan multiversi.

3.1. Paus #1. File sing dipetakan memori

File sing dipetakan ing memori minangka unsur arsitektur sing penting sing malah katon ing jeneng gudang. Masalah caching lan sinkronisasi akses menyang informasi sing disimpen kabeh ditinggalake menyang sistem operasi. LMDB ora ngemot cache ing dhewe. Iki minangka kaputusan sadar dening penulis, amarga maca data langsung saka file sing dipetakan ngidini sampeyan ngethok akeh sudhut ing implementasine mesin. Ngisor iki adoh saka dhaftar lengkap sawetara saka wong-wong mau.

  1. Njaga konsistensi data ing panyimpenan nalika nggarap saka sawetara proses dadi tanggung jawab sistem operasi. Ing bagean sabanjure, mekanika iki dibahas kanthi rinci lan nganggo gambar.
  2. Ora ana cache rampung ngilangi LMDB saka overhead sing ana gandhengane karo alokasi dinamis. Maca data ing laku tegese nyetel pointer menyang alamat sing bener ing memori virtual lan ora liya. Muni kaya fiksi ilmiah, nanging ing kode sumber panyimpenan kabeh telpon calloc klempakan ing fungsi konfigurasi panyimpenan.
  3. Ora ana cache uga tegese ora ana kunci sing ana gandhengane karo sinkronisasi akses. Pembaca, sing bisa uga ana jumlah pembaca sing sewenang-wenang ing wektu sing padha, ora nemoni mutex siji ing dalan menyang data. Amarga iki, kacepetan maca nduweni skalabilitas linear sing cocog adhedhasar jumlah CPU. Ing LMDB, mung operasi modifikasi sing disinkronake. Mung ana siji penulis ing wektu.
  4. Logika cache lan sinkronisasi minimal ngilangi jinis kesalahan sing rumit banget sing ana gandhengane karo kerja ing lingkungan multi-threaded. Ana rong studi basis data sing menarik ing konferensi Usenix OSDI 2014: "Kabeh Sistem File Ora Digawe Podo: Ing Kerumitan Nggawe Aplikasi sing Konsisten Kacilakan" и "Database Nyiksa kanggo Kesenengan lan Keuntungan". Saka wong-wong mau, sampeyan bisa ngumpulake informasi babagan linuwih LMDB sing durung ana sadurunge lan implementasine properti transaksi ACID sing meh sampurna, sing luwih unggul tinimbang SQLite.
  5. Minimalis LMDB ngidini perwakilan mesin saka kode kasebut rampung ing cache L1 prosesor kanthi karakteristik kacepetan.

Sayange, ing iOS, kanthi file sing dipetakan ing memori, kabeh ora kaya awan kaya sing dikarepake. Kanggo pirembagan bab shortcomings gadhah wong luwih sadar, iku perlu kanggo elinga prinsip umum ngleksanakake mekanisme iki ing sistem operasi.

Informasi umum babagan file sing dipetakan memori

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOSKanthi saben aplikasi sing mlaku, sistem operasi nggandhengake entitas sing diarani proses. Saben proses diparengake sawetara alamat sing cedhak karo kabeh sing dibutuhake kanggo operasi. Ing alamat paling ngisor ana bagean karo kode lan data hard-kode lan sumber daya. Sabanjure teka papan alamat dinamis sing akeh, sing dikenal kanthi jeneng tumpukan. Isine alamat entitas sing katon sajrone operasi program. Ing sisih ndhuwur ana area memori sing digunakake dening tumpukan aplikasi. Iku salah siji mundak akeh utawa kontrak; ing tembung liyane, ukurane uga nduweni sifat dinamis. Kanggo nyegah tumpukan lan tumpukan saka push lan interfering karo saben liyane, padha dumunung ing ends beda saka papan alamat. Ana bolongan antarane rong bagean dinamis ing ndhuwur lan ngisor. Sistem operasi nggunakake alamat ing bagean tengah iki kanggo nggandhengake macem-macem entitas karo proses. Utamane, bisa nggandhengake alamat terus-terusan tartamtu karo file ing disk. File kasebut diarani memori-mapped.​

Ruang alamat sing diparengake kanggo proses kasebut ageng. Secara teoritis, jumlah alamat mung diwatesi kanthi ukuran pointer, sing ditemtokake dening kapasitas bit sistem. Yen memori fisik dipetakan menyang 1-kanggo-1, banjur proses sing sepisanan bakal ngrusak kabeh RAM, lan ora ana sing ngomong babagan akeh tugas.​

Nanging, saka pengalaman kita ngerti manawa sistem operasi modern bisa nglakokake akeh proses sing dikarepake. Iki bisa amarga kasunyatan sing padha mung nyedhiakke akeh memori kanggo pangolahan ing kertas, nanging ing kasunyatan padha mbukak menyang memori fisik utama mung bagean sing dikarepake kene lan saiki. Mulane, memori sing ana gandhengane karo proses diarani virtual.

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOS

Sistem operasi ngatur memori virtual lan fisik menyang kaca kanthi ukuran tartamtu. Sanalika kaca tartamtu saka memori virtual dikarepake, mbukak sistem operasi menyang memori fisik lan cocog ing meja khusus. Yen ora ana slot gratis, salah sawijining kaca sing dimuat sadurunge disalin menyang disk, lan sing dikarepake bakal ditindakake. Prosedur iki, sing bakal kita bali maneh, diarani swapping. Tokoh ing ngisor iki nggambarake proses sing diterangake. Ing kaca A kanthi alamat 0 dimuat lan diselehake ing kaca memori utama kanthi alamat 4. Kasunyatan iki dibayangke ing tabel korespondensi ing nomer sel 0.​

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOS

Crita iki persis padha karo file sing dipetakan menyang memori. Secara logis, mesthine terus-terusan lan kabeh ana ing ruang alamat virtual. Nanging, dheweke mlebu kaca memori fisik kanthi kaca lan mung dijaluk. Modifikasi kaca kasebut disinkronake karo file ing disk. Kanthi cara iki, sampeyan bisa nindakake file I/O kanthi mung nggarap bita ing memori - kabeh owah-owahan bakal kanthi otomatis ditransfer dening kernel sistem operasi menyang file sumber.​

Gambar ing ngisor iki nuduhake carane LMDB nyinkronake negara nalika nggarap database saka macem-macem proses. Kanthi pemetaan memori virtual saka macem-macem pangolahan menyang file sing padha, kita de facto mewajibake sistem operasi kanggo transitively nyinkronake blok tartamtu saka spasi alamat karo saben liyane, ngendi LMDB katon.​

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOS

Nuansa penting yaiku LMDB, kanthi standar, ngowahi file data liwat mekanisme telpon sistem nulis, lan nampilake file kasebut ing mode mung diwaca. Pendekatan iki nduweni rong akibat penting.

Konsekuensi pisanan umum kanggo kabeh sistem operasi. Intine yaiku nambahake proteksi marang karusakan sing ora disengaja ing basis data kanthi kode sing salah. Kaya sing sampeyan ngerteni, instruksi sing bisa dieksekusi saka proses gratis kanggo ngakses data saka ngendi wae ing papan alamat. Ing wektu sing padha, kaya sing dakelingake, nampilake file ing mode maca-tulis tegese instruksi apa wae uga bisa ngowahi. Yen dheweke nindakake iki kanthi salah, nyoba, contone, kanggo bener nimpa unsur array ing indeks sing ora ana, mula dheweke bisa kanthi ora sengaja ngganti file sing dipetakan menyang alamat iki, sing bakal nyebabake korupsi database. Yen file ditampilake ing mode mung-maca, banjur nyoba ngganti ruang alamat sing cocog bakal nyebabake mandap darurat program kanthi sinyal. SIGSEGV, lan file bakal tetep utuh.

Konsekuensi kapindho wis khusus kanggo iOS. Sanadyan penulis utawa sumber liyane cetha nyebutake, nanging tanpa iku LMDB ora bakal cocok kanggo mbukak ing sistem operasi seluler iki. Bagean sabanjure ditrapake kanggo pertimbangan.

Spesifik file sing dipetakan memori ing iOS

Ana laporan apik banget ing WWDC ing 2018 "iOS Memory Deep Dive". Kita ngandhani yen ing iOS, kabeh kaca sing ana ing memori fisik minangka salah siji saka 3 jinis: reged, kompres lan resik.

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOS

Memori sing resik minangka koleksi kaca sing bisa dibongkar tanpa lara saka memori fisik. Data sing ana bisa dimuat maneh yen dibutuhake saka sumber asline. File sing diwaca mung ing memori sing dipetakan kalebu ing kategori iki. iOS ora wedi mbongkar kaca sing dipetakan menyang file saka memori sawayah-wayah, amarga padha dijamin bakal diselarasake karo file ing disk.

Kabeh kaca sing diowahi dadi memori sing reged, ora ketompo ing ngendi asale. Utamane, file sing dipetakan ing memori sing diowahi kanthi nulis menyang memori virtual sing ana gandhengane bakal diklasifikasikake kanthi cara iki. Mbukak LMDB nganggo flag MDB_WRITEMAP, sawise nggawe pangowahan, sampeyan bisa verifikasi iki kanthi pribadi.​

Sanalika aplikasi wiwit njupuk memori fisik sing akeh banget, iOS menehi kompresi kaca sing reged. Memori total sing dikuwasani dening kaca sing reged lan dikompres minangka jejak memori aplikasi sing diarani. Sawise tekan nilai ambang tartamtu, daemon sistem pembunuh OOM teka sawise proses kasebut lan mungkasi kanthi paksa. Iki minangka keanehan iOS dibandhingake karo sistem operasi desktop. Ing kontras, nyuda jejak memori kanthi ngganti kaca saka memori fisik menyang disk ora kasedhiya ing iOS. Alasane mung bisa ditebak. Mbok menawa prosedur mindhah kaca kanthi intensif menyang disk lan bali banget akeh energi kanggo piranti seluler, utawa iOS nyimpen sumber daya nulis ulang sel ing drive SSD, utawa bisa uga para desainer ora puas karo kinerja sistem sakabèhé, ing ngendi kabeh ana. terus tukaran. Apamaneh, kasunyatane tetep dadi kasunyatan.

Kabar apik, sing wis kasebut sadurunge, yaiku LMDB kanthi standar ora nggunakake mekanisme mmap kanggo nganyari file. Iki tegese data sing ditampilake diklasifikasikake dening iOS minangka memori sing resik lan ora nyumbang kanggo jejak memori. Sampeyan bisa verifikasi iki nggunakake alat Xcode sing diarani VM Tracker. Gambar ing ngisor iki nuduhake kahanan memori virtual iOS aplikasi Cloud sajrone operasi. Ing wiwitan, 2 conto LMDB diwiwiti. Pisanan diijini kanggo nampilake file ing 1GiB memori virtual, kaloro - 512MiB. Senadyan kasunyatan sing loro storages Occupy jumlah tartamtu saka memori penduduk, ora ana kontribusi kanggo ukuran reged.

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOS

Lan saiki wektu kanggo kabar ala. Thanks kanggo mekanisme swap ing sistem operasi desktop 64-dicokot, saben proses bisa manggoni minangka akeh papan alamat virtual minangka papan free hard disk kanggo swap potensial ngidini. Ngganti swap nganggo kompresi ing iOS kanthi radikal nyuda maksimal teoritis. Saiki kabeh proses urip kudu pas menyang memori utama (maca RAM), lan kabeh sing ora cocog kudu dipeksa kanggo mungkasi. Iki wis kasebut ing ndhuwur kasebut nglaporakelan ing dokumentasi resmi. Akibaté, iOS banget mbatesi jumlah memori sing kasedhiya kanggo alokasi liwat mmap. kene kene Sampeyan bisa ndeleng watesan empiris saka jumlah memori sing bisa diparengake ing piranti beda nggunakake telpon sistem iki. Ing model smartphone paling modern, iOS wis dadi loman dening 2 gigabyte, lan ing versi ndhuwur iPad - dening 4. Ing laku, mesthi, sampeyan kudu fokus ing model piranti paling didhukung, ngendi kabeh iku sedih banget. Luwih elek, kanthi ndeleng status memori aplikasi ing VM Tracker, sampeyan bakal nemokake manawa LMDB adoh saka siji-sijine sing ngaku dadi peta memori. Potongan sing apik dipangan dening alokasi sistem, file sumber daya, kerangka gambar, lan predator cilik liyane.

Adhedhasar asil eksperimen ing Cloud, kita entuk nilai kompromi ing ngisor iki kanggo memori sing diwenehake dening LMDB: 384 megabyte kanggo piranti 32-bit lan 768 kanggo piranti 64-bit. Sawise volume iki digunakake munggah, sembarang operasi modifikasi wiwit mungkasi karo kode MDB_MAP_FULL. Kita mirsani kesalahan kasebut ing ngawasi, nanging cukup cilik yen ing tahap iki bisa diabaikan.

A alesan non-jelas kanggo konsumsi memori gedhe banget dening panyimpenan bisa dadi transaksi long-urip. Kanggo mangerteni carane loro fenomena iki disambungake, kita bakal dibantu kanthi nimbang rong pilar LMDB sing isih ana.

3.2. Paus #2. B+ - wit

Kanggo niru tabel ing ndhuwur panyimpenan nilai kunci, operasi ing ngisor iki kudu ana ing API:

  1. Nglebokake unsur anyar.
  2. Telusuri unsur kanthi kunci sing diwenehake.
  3. Mbusak unsur.
  4. Iterate liwat interval tombol ing urutan padha diurutake.

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOSStruktur data paling gampang sing bisa gampang ngleksanakake kabeh papat operasi yaiku wit telusuran binar. Saben simpul kasebut nggambarake kunci sing mbagi kabeh subset kunci anak dadi rong subtree. Sing kiwa isine sing luwih cilik tinimbang wong tuwa, lan sing tengen ngemot sing luwih gedhe. Entuk set kunci sing dipesen bisa ditindakake liwat salah sawijining traversal wit klasik

Wit binar duwe rong cacat dhasar sing nyegah supaya ora efektif minangka struktur data basis disk. Kaping pisanan, tingkat keseimbangane ora bisa ditebak. Ana risiko gedhe kanggo entuk wit-witan sing dhuwure cabang-cabang sing beda-beda bisa beda-beda, sing bisa nambah kerumitan telusuran algoritma dibandhingake karo sing dikarepake. Kapindho, turah mbrawah saka cross-links antarane simpul deprives wit binar saka lokalitas ing memori, Node cedhak (ing syarat-syarat sambungan antarane wong-wong mau) bisa dumunung ing kaca temen beda ing memori virtual. Akibaté, malah traversal prasaja saka sawetara simpul tetanggan ing wit mbutuhake ngunjungi nomer iso dibandhingke kaca. Iki minangka masalah sanajan kita ngomong babagan efektifitas wit binar minangka struktur data ing memori, amarga terus-terusan muter kaca ing cache prosesor ora nyenengake. Nalika nerangake kerep njupuk kaca sing digandhengake karo simpul saka disk, kahanan dadi rampung sedhih.

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOSB-wit, minangka evolusi saka wit binar, ngrampungake masalah sing diidentifikasi ing paragraf sadurunge. Kaping pisanan, dheweke ngimbangi awake dhewe. Kapindho, saben simpul kasebut misahake set tombol anak ora dadi 2, nanging dadi subset sing diurutake M, lan nomer M bisa cukup gedhe, kanthi urutan sawetara atus, utawa malah ewonan.

kanthi mangkono:

  1. Saben simpul ngemot nomer akeh tombol wis dhawuh lan wit cendhak banget.
  2. Wit kasebut entuk properti lokalitas lokasi ing memori, amarga kunci sing cedhak regane alamiah ana ing jejere simpul sing padha utawa tetanggan.
  3. Jumlah simpul transit nalika mudhun wit sajrone operasi telusuran suda.
  4. Jumlah simpul target sing diwaca sajrone pitakon kisaran dikurangi, amarga saben simpul kasebut wis ngemot akeh tombol sing diurutake.

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOS

LMDB nggunakake variasi saka B-wit disebut wit B + kanggo nyimpen data. Diagram ing ndhuwur nuduhake telung jinis simpul sing ana ing njero:

  1. Ing sisih ndhuwur ana oyod. Iku materializes ora luwih saka konsep database nang gudang. Ing siji conto LMDB, sampeyan bisa nggawe sawetara database sing nuduhake papan alamat virtual sing dipetakan. Saben wong diwiwiti saka oyod dhewe.
  2. Ing tingkat paling ngisor yaiku godhong. Dheweke lan mung ngemot pasangan kunci-nilai sing disimpen ing database. Miturut cara, iki peculiarity saka B + -wit. Yen B-wit biasa nyimpen bagean nilai ing kelenjar kabeh tingkat, banjur B + variasi mung ing paling. Sawise ndandani kasunyatan iki, kita bakal nyebut subtipe wit sing digunakake ing LMDB mung minangka B-wit.
  3. Antarane oyod lan godhong ana 0 utawa luwih tingkat teknis kanthi simpul navigasi (cabang). Tugase yaiku mbagi set kunci sing diurutake ing antarane godhong.

Secara fisik, simpul minangka blok memori kanthi dawa sing wis ditemtokake. Ukurane minangka pirang-pirang ukuran kaca memori ing sistem operasi, sing kita rembugan ing ndhuwur. Struktur simpul kapacak ing ngisor iki. Header ngemot informasi meta, sing paling jelas yaiku checksum. Sabanjure nerangake informasi babagan offset ing ngendi sel karo data dumunung. Data kasebut bisa dadi salah siji kunci, yen kita ngomong babagan simpul navigasi, utawa kabeh pasangan nilai kunci ing kasus godhong.​ Sampeyan bisa maca liyane babagan struktur kaca ing karya. "Evaluasi Toko Nilai Kunci Kinerja Tinggi".

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOS

Sawise urusan karo isi internal simpul kaca, kita bakal luwih makili LMDB B-wit ing proses simplified ing wangun ing ngisor iki.

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOS

Kaca-kaca kanthi simpul dumunung kanthi urutan ing disk. Kaca kanthi nomer sing luwih dhuwur dumunung ing pungkasan file. Kaca meta sing diarani ngemot informasi babagan offset sing bisa ditemokake oyod kabeh wit. Nalika mbukak file, LMDB mindhai kaca file miturut kaca saka pungkasan nganti wiwitan kanggo nggoleki kaca meta sing bener lan liwat iku nemokake database sing wis ana.​

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOS

Saiki, duwe ide babagan struktur logis lan fisik organisasi data, kita bisa nerusake nimbang pilar katelu LMDB. Iku kanthi bantuan sing kabeh modifikasi panyimpenan dumadi transactionally lan ing isolasi saka saben liyane, menehi database minangka kabèh properti saka multiversion.

3.3. Paus #3. Copy-on-write

Sawetara operasi B-tree melu nggawe sawetara owah-owahan ing node. Salah sawijining conto yaiku nambahake kunci anyar menyang simpul sing wis tekan kapasitas maksimal. Ing kasus iki, perlu, sepisanan, pamisah simpul dadi loro, lan kapindho, kanggo nambah link menyang simpul anak budding anyar ing wong tuwa. Prosedur iki bisa mbebayani banget. Yen sakperangan alesan (kacilakan, pemadaman listrik, lan liya-liyane) mung bagean saka owah-owahan saka seri kasebut, mula wit kasebut bakal tetep ora konsisten.

Salah sawijining solusi tradisional kanggo nggawe database fault-tolerant yaiku nambahake struktur data on-disk tambahan ing jejere B-tree - log transaksi, uga dikenal minangka write-ahead log (WAL). Iku file ing pungkasan sing operasi dimaksudaké ditulis strictly sadurunge ngowahi B-wit dhewe. Mangkono, yen korupsi data dideteksi sajrone diagnosa dhewe, database takon log supaya bisa diatur.

LMDB wis milih cara sing beda minangka mekanisme toleransi fault, disebut copy-on-write. Intine yaiku tinimbang nganyari data ing kaca sing wis ana, mula nyalin kabeh lan nggawe kabeh modifikasi ing salinan kasebut.​

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOS

Sabanjure, supaya data sing dianyari kasedhiya, perlu ngganti link menyang simpul sing wis dadi saiki ing simpul induk. Amarga iki uga kudu diowahi, mula uga disalin sadurunge. Proses kasebut terus rekursif nganti tekan oyod. Bab pungkasan sing kudu diganti yaiku data ing kaca meta.​

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOS

Yen dumadakan proses tubrukan sajrone prosedur nganyari, banjur kaca meta anyar ora bakal digawe, utawa ora bakal ditulis ing disk kanthi lengkap, lan checksum bakal salah. Ing salah siji saka rong kasus iki, kaca anyar ora bisa diakses, nanging kaca lawas ora bakal kena pengaruh. Iki ngilangi kabutuhan LMDB kanggo nulis log ahead kanggo njaga konsistensi data. De facto, struktur panyimpenan data ing disk kasebut ing ndhuwur bebarengan njupuk fungsi. Ora ana log transaksi sing eksplisit minangka salah sawijining fitur LMDB sing nyedhiyakake kacepetan maca data sing dhuwur

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOS

Desain sing diasilake, sing diarani wit B mung append, kanthi alami nyedhiyakake isolasi transaksi lan multi-versi. Ing LMDB, saben transaksi mbukak digandhengake karo ROOT wit sing relevan saiki. Nganti transaksi rampung, kaca wit sing digandhengake karo iku ora bakal diganti utawa digunakake maneh kanggo versi anyar saka data.Mangkono, sampeyan bisa bisa kanggo anggere sampeyan seneng karo persis set data sing cocog ing wektu. transaksi dibukak, malah yen panyimpenan terus aktif dianyari ing wektu iki. Iki minangka inti saka multiversi, nggawe LMDB minangka sumber data sing cocog kanggo kekasih kita UICollectionView. Sawise mbukak transaksi, ora perlu nambah jejak memori aplikasi kanthi cepet-cepet ngompa data saiki menyang sawetara struktur memori, amarga wedi yen ora ana apa-apa. Fitur iki mbedakake LMDB saka SQLite sing padha, sing ora bisa gumunggung total isolasi kasebut. Sawise mbukak rong transaksi ing sing terakhir lan mbusak rekaman tartamtu ing salah sijine, ora bakal bisa entuk rekaman sing padha ing sing liyane.

Sisih flip saka koin yaiku konsumsi memori virtual sing luwih dhuwur. Slide nuduhake apa struktur database bakal katon kaya yen diowahi bebarengan karo 3 mbukak mbukak transaksi looking ing versi beda saka database. Amarga LMDB ora bisa nggunakake maneh simpul sing bisa digayuh saka oyod sing ana gandhengane karo transaksi saiki, toko ora duwe pilihan kajaba nyedhiakke root papat liyane ing memori lan maneh kloning kaca sing diowahi ing ngisor iki.​

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOS

Ing kene bakal migunani kanggo ngelingi bagean ing file sing dipetakan memori. Iku misale jek sing konsumsi tambahan saka memori virtual ngirim ora sumelang ing kita akeh, awit iku ora kontribusi kanggo tilas memori saka aplikasi. Nanging, ing wektu sing padha, dicathet yen iOS banget pelit kanggo nyedhiyakake, lan kita ora bisa, kaya ing server utawa desktop, nyedhiyakake wilayah LMDB 1 terabyte lan ora mikir babagan fitur iki. Yen bisa, sampeyan kudu nyoba kanggo nggawe umur transaksi sakcepete.

4. Ngrancang skema data ing ndhuwur API key-value

Ayo miwiti analisis API kanthi ndeleng abstraksi dhasar sing diwenehake dening LMDB: lingkungan lan basis data, kunci lan nilai, transaksi lan kursor.

Cathetan babagan dhaptar kode

Kabeh fungsi ing API LMDB umum ngasilake asil karyane ing bentuk kode kesalahan, nanging ing kabeh dhaptar sabanjure verifikasi kasebut diilangi kanggo ringkesan. garpu C++ pambungkus lmdbxx, ing endi kesalahan diwujudake minangka pengecualian C ++.

Minangka cara paling cepet kanggo nyambungake LMDB menyang proyek kanggo iOS utawa macOS, aku nyaranake CocoaPod POSLMDB.

4.1. abstraksi dhasar

Lingkungan

struktur MDB_env minangka gudang saka negara internal LMDB. kulawarga fungsi prefixed mdb_env ngidini sampeyan ngatur sawetara properti. Ing kasus sing paling gampang, initialization engine katon kaya iki.

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

Ing aplikasi Cloud Mail.ru, kita ngganti nilai standar mung rong parameter.

Sing pertama yaiku ukuran ruang alamat virtual sing disimpen ing file panyimpenan. Sayange, sanajan ing piranti sing padha, nilai tartamtu bisa beda-beda sacara signifikan saka roto kanggo mbukak. Kanggo nganggep fitur iOS iki, volume panyimpenan maksimal dipilih kanthi dinamis. Miwiti saka nilai tartamtu, dipérang dadi setengah nganti fungsi kasebut mdb_env_open ora bakal bali asil beda saka ENOMEM. Ing teori, ana uga cara ngelawan - pisanan nyedhiyakake minimal memori kanggo mesin, lan banjur, nalika kesalahan ditampa, MDB_MAP_FULL, nambah. Nanging, iku luwih eri. Alesané iku prosedur kanggo re allocating memori (remap) nggunakake fungsi mdb_env_set_map_size mbatalake kabeh entitas (kursor, transaksi, kunci lan nilai) sing sadurunge ditampa saka mesin. Nggatekake acara kasebut ing kode kasebut bakal nyebabake komplikasi sing signifikan. Nanging, yen memori virtual penting banget kanggo sampeyan, mula iki bisa uga dadi alesan kanggo ndeleng garpu sing wis maju. MDBX, ing antarane fitur sing diumumake ana "pangaturan ukuran database on-the-fly otomatis".

Parameter kapindho, nilai standar sing ora cocog karo kita, ngatur mekanika kanggo njamin safety thread. Sayange, paling iOS 10 duwe masalah karo dhukungan kanggo panyimpenan lokal thread. Mulane, ing conto ing ndhuwur, gudang dibukak nganggo gendera MDB_NOTLS. Saliyane iki, iku uga perlu garpu C++ pambungkus lmdbxxkanggo Cut metu variabel karo atribut iki lan ing.

Databases

Database minangka conto B-wit kapisah, sing kita rembugan ing ndhuwur. Bukaan kasebut ana ing njero transaksi, sing bisa uga katon aneh ing wiwitan.

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

Pancen, transaksi ing LMDB minangka entitas panyimpenan, dudu entitas basis data tartamtu. Konsep iki ngidini sampeyan nindakake operasi atom ing entitas sing ana ing basis data sing beda. Ing teori, iki mbukak kemungkinan modeling tabel ing wangun database beda, nanging ing siji wektu aku njupuk dalan beda, diterangake ing rinci ing ngisor iki.

Tombol lan nilai

struktur MDB_val model konsep saka loro tombol lan nilai. Repositori ora ngerti babagan semantike. Kanggo dheweke, liyane mung sawetara bait saka ukuran tartamtu. Ukuran tombol maksimal yaiku 512 bita.

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

Nggunakake komparator, toko ngurutake kunci kanthi urutan munggah. Yen sampeyan ora ngganti karo sampeyan dhewe, sing standar bakal digunakake, sing ngurutake byte-by-byte ing urutan leksikografis.​

Transaksi

Struktur transaksi diterangake kanthi rinci ing bab sadurunge, dadi ing kene aku bakal mbaleni maneh sifat utama:

  1. Ndhukung kabeh sifat dhasar ACID: atomicity, konsistensi, isolasi lan linuwih. Aku ora bisa ngerteni manawa ana bug babagan daya tahan ing macOS lan iOS sing wis didandani ing MDBX. Sampeyan bisa maca liyane ing README.
  2. Pendekatan kanggo multithreading diterangake kanthi skema "penulis tunggal / akeh pembaca". Penulis mblokir saben liyane, nanging ora ngalangi pembaca. Pembaca ora ngalangi panulis utawa saben liyane.
  3. Dhukungan kanggo transaksi nested.
  4. Dhukungan multiversi.

Multiversion ing LMDB apik banget aku pengin nuduhake ing tumindak. Saka kode ing ngisor iki, sampeyan bisa ndeleng manawa saben transaksi bisa digunakake kanthi persis versi database sing saiki nalika dibukak, diisolasi saka kabeh owah-owahan sabanjure. Miwiti panyimpenan lan nambah rekaman tes kasebut ora nuduhake apa-apa sing menarik, mula ritual kasebut ditinggalake ing spoiler.

Nambahake entri test

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

Aku nyaranake sampeyan nyoba trick padha karo SQLite lan ndeleng apa mengkono.

Multiversion ndadekke tunjangan apik banget kanggo urip pangembang iOS. Nggunakake properti iki, sampeyan bisa kanthi gampang lan alami nyetel tingkat nganyari sumber data kanggo formulir layar, adhedhasar pertimbangan pengalaman pangguna. Contone, ayo njupuk fitur aplikasi Mail.ru Cloud kayata isi otomatis saka galeri media sistem. Kanthi sambungan sing apik, klien bisa nambah sawetara foto saben detik menyang server. Yen sampeyan nganyari sawise saben download UICollectionView karo isi media ing maya pangguna, sampeyan bisa lali babagan 60 fps lan nggulung Gamelan sak proses iki. Kanggo nyegah nganyari layar sing kerep, sampeyan kudu mbatesi tingkat owah-owahan ing basis data UICollectionViewDataSource.

Yen database ora ndhukung multiversion lan ngijini sampeyan kanggo bisa mung karo negara saiki, banjur kanggo nggawe gambar asli seko wektu-stabil saka data sampeyan kudu nyalin menyang sawetara struktur data ing-memori utawa kanggo tabel sauntara. Sembarang pendekatan iki larang banget. Ing kasus panyimpenan ing memori, kita entuk biaya ing memori, sing disebabake dening nyimpen obyek sing dibangun, lan ing wektu sing ana gandhengane karo transformasi ORM sing berlebihan. Kanggo meja sementara, iki minangka kesenengan sing luwih larang, mung ing kasus sing ora pati penting.

Solusi multiversi LMDB ngatasi masalah njaga sumber data sing stabil kanthi cara sing elegan. Cukup mung mbukak transaksi lan voila - nganti rampung, set data dijamin bakal didandani. Logika kanggo kacepetan nganyari saiki kabeh ana ing tangan lapisan presentasi, tanpa sumber daya sing signifikan.

Kursor

Kursor nyedhiyakake mekanisme kanggo ngulang kanthi tertib liwat pasangan kunci-nilai liwat traversal B-tree. Tanpa wong-wong mau, ora mungkin model tabel ing basis data kanthi efektif, sing saiki diuripake.

4.2. Modeling Tabel

Properti pesenan kunci ngidini sampeyan nggawe abstraksi tingkat dhuwur kayata tabel ing ndhuwur abstraksi dhasar. Ayo dipikirake proses iki nggunakake conto tabel utama klien awan, sing nyimpen informasi babagan kabeh file lan folder pangguna.

Skema tabel

Salah sawijining skenario umum sing kudu disesuaikan karo struktur tabel kanthi wit folder yaiku milih kabeh unsur sing ana ing direktori tartamtu. Model organisasi data sing apik kanggo pitakon sing efisien kaya iki yaiku Dhaptar Adjacency. Kanggo ngleksanakake ing ndhuwur panyimpenan kunci-nilai, perlu kanggo ngurutake tombol file lan folder kanthi cara sing padha diklompokaké adhedhasar anggota ing direktori induk. Kajaba iku, kanggo nampilake isi direktori ing wangun akrab kanggo pangguna Windows (folder pisanan, banjur file, loro diurutake miturut abjad), perlu kanggo nyakup kolom tambahan sing cocog ing tombol kasebut.

Gambar ing ngisor iki nuduhake carane, adhedhasar tugas ing tangan, perwakilan saka tombol ing wangun array byte bisa katon kaya. Byte kanthi pengenal direktori induk (abang) diselehake dhisik, banjur nganggo jinis (ijo) lan ing buntut kanthi jeneng (biru). cara sing dibutuhake. Tombol traversing kanthi urutan kanthi awalan abang sing padha menehi nilai sing digandhengake miturut urutan sing kudu ditampilake ing antarmuka panganggo (ing sisih tengen), tanpa mbutuhake pamrosesan pasca tambahan.

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOS

Serializing Keys lan Nilai

Akeh cara kanggo serializing obyek wis nemokke ing donya. Amarga kita ora duwe syarat liyane kajaba kacepetan, kita milih sing paling cepet kanggo awake dhewe - mbucal memori sing dikuwasani dening conto struktur basa C. Dadi, kunci unsur direktori bisa dimodelake karo struktur ing ngisor iki. NodeKey.

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

Kanggo nyimpen NodeKey ing panyimpenan dibutuhake ing obyek MDB_val posisi pointer data menyang alamat awal struktur, lan ngitung ukuran karo fungsi sizeof.

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

Ing bab pisanan ing kritéria pilihan database, aku disebutake minimalake alokasi dinamis ing operasi CRUD minangka faktor pilihan penting. Kode fungsi serialize nuduhake carane ing kasus LMDB padha bisa rampung nyingkiri nalika nglebokake cathetan anyar menyang database. Array byte sing mlebu saka server pisanan diowahi dadi struktur tumpukan, banjur dibuwang kanthi sepele menyang panyimpenan. Ngelingi sing ana uga ora alokasi dinamis nang LMDB, sampeyan bisa njaluk kahanan Fantastic dening standar iOS - nggunakake mung tumpukan memori kanggo bisa karo data sadawane kabeh path saka jaringan kanggo disk!

Tombol pesenan kanthi komparator binar

Hubungan urutan kunci kasebut ditemtokake dening fungsi khusus sing disebut komparator. Amarga mesin ora ngerti apa-apa babagan semantik bait sing ana, komparator standar ora duwe pilihan nanging ngatur tombol ing urutan leksikografis, nggunakake perbandingan bait-byte. Nggunakake kanggo ngatur struktur padha karo cukur nganggo kapak chopping. Nanging, ing kasus-kasus prasaja aku nemokake cara iki bisa ditampa. Alternatif kasebut diterangake ing ngisor iki, nanging ing kene aku bakal nyathet sawetara rakes sing kasebar ing dalan iki.

Wangsulan: Bab ingkang pisanan kanggo elinga iku perwakilan memori saka jinis data primitif. Mangkono, ing kabeh piranti Apple, variabel integer disimpen ing format kasebut Endian Cilik. Iki tegese byte sing paling ora penting bakal ana ing sisih kiwa, lan ora bisa ngurutake wilangan bulat nggunakake perbandingan bait-byte. Contone, nyoba nindakake iki kanthi sakumpulan nomer saka 0 nganti 511 bakal ngasilake asil ing ngisor iki.

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

Kanggo ngatasi masalah iki, integer kudu disimpen ing tombol ing format cocok kanggo comparator byte-byte. Fungsi saka kulawarga bakal mbantu sampeyan nindakake transformasi sing dibutuhake hton* (khususe htons kanggo nomer pindho bait saka conto).

Format kanggo makili string ing pemrograman, kaya sing sampeyan ngerteni, kabeh sejarah. Yen semantik senar, uga enkoding sing digunakake kanggo makili wong-wong mau ing memori, nuduhake yen ana luwih saka siji bait saben karakter, banjur luwih apik kanggo langsung ninggalake gagasan nggunakake komparator standar.

Bab kapindho sing kudu dieling-eling yaiku prinsip keselarasan kompiler lapangan struktur. Amarga saka wong-wong mau, bita karo nilai sampah bisa dibentuk ing memori antarane lapangan, sing, mesthi, ngilangi pangurutan byte-byte. Kanggo ngilangi sampah, sampeyan kudu ngumumake kolom kanthi urutan sing wis ditemtokake, ngelingi aturan keselarasan, utawa nggunakake atribut ing deklarasi struktur. packed.

Tombol pesenan karo komparator eksternal

Logika perbandingan kunci bisa uga rumit banget kanggo komparator binar. Salah sawijining sebab yaiku anané lapangan teknis ing struktur. Aku bakal nggambarake kedadeyan kasebut kanthi nggunakake conto kunci kanggo unsur direktori sing wis kita kenal.

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

Senadyan gamblang, ing akèh-akèhé kasus nganggo memori akeh banget. Buffer kanggo jeneng kasebut njupuk 256 bita, sanajan rata-rata jeneng file lan folder arang ngluwihi 20-30 karakter.

Salah sawijining teknik standar kanggo ngoptimalake ukuran rekaman yaiku "motong" menyang ukuran nyata. Intine yaiku yen isi kabeh kolom dawa variabel disimpen ing buffer ing mburi struktur, lan dawane disimpen ing variabel sing kapisah.​ Miturut pendekatan iki, kunci NodeKey diowahi kaya ing ngisor iki.

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

Salajengipun, nalika serializing, ukuran data ora ditemtokake sizeof kabeh struktur, lan ukuran kabeh kothak dawa tetep plus ukuran bagean bener digunakake saka buffer.

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

Minangka asil refactoring, kita nampa tabungan sing signifikan ing papan sing dikuwasani kunci. Nanging, amarga lapangan technical nameLength, komparator binar standar ora cocok maneh kanggo mbandhingake kunci. Yen kita ora ngganti karo kita dhewe, banjur dawa jeneng bakal dadi faktor prioritas luwih ing ngurutake saka jeneng dhewe.

LMDB ngidini saben database duwe fungsi comparison tombol dhewe. Iki ditindakake kanthi nggunakake fungsi kasebut mdb_set_compare strictly sadurunge mbukak. Kanggo alasan sing jelas, ora bisa diganti sajrone urip database. Comparator nampa rong tombol ing format binar minangka input, lan ing output ngasilake asil comparison: kurang saka (-1), luwih saka (1) utawa padha karo (0). Pseudocode kanggo NodeKey katon kaya ngono.

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

Anggere kabeh tombol ing database saka jinis padha, unconditionally cast perwakilan byte kanggo jinis struktur tombol aplikasi legal. Ana siji nuansa ing kene, nanging bakal dibahas ing ngisor iki ing bagean "Reads Reading".

Nilai Serializing

LMDB dianggo banget intensif karo tombol cathetan sing disimpen. Perbandhingan karo saben liyane dumadi ing kerangka operasi apa wae, lan kinerja kabeh solusi gumantung saka kacepetan komparator. Ing donya sing becik, komparator binar standar kudu cukup kanggo mbandhingake kunci, nanging yen sampeyan kudu nggunakake dhewe, banjur prosedur kanggo deserializing tombol kudu cepet sabisa.

Database ora utamané kasengsem ing bagean Nilai saka rekaman (nilai). Konversi saka perwakilan byte menyang obyek mung kedadeyan yen wis dibutuhake dening kode aplikasi, contone, kanggo nampilake ing layar. Wiwit kedadeyan kasebut arang banget, syarat kacepetan kanggo prosedur iki ora kritis, lan ing implementasine, kita luwih bebas fokus ing penak, contone, kanggo serialisasi metadata babagan file sing durung diunduh, kita nggunakake NSKeyedArchiver.

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

Nanging, ana wektu nalika kinerja isih penting. Contone, nalika nyimpen metainformation babagan struktur file awan pangguna, kita nggunakake memori sing padha mbucal obyek. Sorotan tugas ngasilake perwakilan serial yaiku kasunyatan manawa unsur-unsur direktori dimodelake kanthi hierarki kelas.​

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOS

Kanggo ngleksanakake ing basa C, lapangan tartamtu saka ahli waris diselehake ing struktur kapisah, lan sambungan karo basis wis ditemtokake liwat lapangan saka serikat pekerja. Konten nyata saka serikat kasebut ditemtokake liwat jinis atribut teknis.

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

Nambah lan nganyari cathetan

Tombol lan nilai serial bisa ditambahake menyang toko. Kanggo nindakake iki, gunakake fungsi kasebut mdb_put.

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

Ing tataran konfigurasi, panyimpenan bisa diijini utawa dilarang kanggo nyimpen sawetara rekaman kanthi tombol sing padha. Yen fraying mung bisa kedadeyan minangka akibat saka kesalahan ing kode, sampeyan bisa nglindhungi dhewe saka iku kanthi nemtokake gendera NOOVERWRITE.

Entri maca

Kanggo maca cathetan ing LMDB, gunakake fungsi kasebut mdb_get. Yen pasangan kunci-nilai diwakili dening struktur sing dibuwang sadurunge, mula prosedur iki katon kaya iki.

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

Listing sing ditampilake nuduhake carane serialisasi liwat struktur dump ngidini sampeyan nyisihake alokasi dinamis ora mung nalika nulis, nanging nalika maca data. Asale saka fungsi mdb_get pitunjuk katon persis ing alamat memori virtual ngendi database nyimpen perwakilan byte obyek. Nyatane, kita entuk jinis ORM sing nyedhiyakake kacepetan maca data sing dhuwur banget meh gratis. Senadyan kabeh kaendahan pendekatan kasebut, perlu kanggo ngelingi sawetara fitur sing ana gandhengane.

  1. Kanggo transaksi mung diwaca, pointer menyang struktur nilai dijamin tetep valid nganti transaksi ditutup. Kaya sing kacathet sadurunge, kaca B-wit ing ngendi obyek dumunung, amarga prinsip copy-on-write, tetep ora owah anggere dirujuk dening paling ora siji transaksi. Ing wektu sing padha, sanalika transaksi pungkasan sing digandhengake karo dheweke rampung, kaca kasebut bisa digunakake maneh kanggo data anyar. Yen obyek kudu urip ing transaksi sing nggawe, mula isih kudu disalin.
  2. Kanggo transaksi readwrite, pointer menyang struktur nilai sing diasilake mung sah nganti prosedur modifikasi pisanan (nulis utawa mbusak data).
  3. Senajan struktur NodeValue ora lengkap, nanging dipotong (ndeleng bagean "Tombol pesenan nggunakake komparator eksternal"), sampeyan bisa ngakses lapangan kanthi aman liwat penunjuk. Ingkang utama ora kanggo dereference!
  4. Ing kahanan apa wae, struktur kasebut ora bisa diowahi liwat penunjuk sing ditampa. Kabeh owah-owahan kudu digawe mung liwat cara mdb_put. Nanging, ora ketompo carane hard sampeyan pengin nindakake iki, iku ora bisa, wiwit wilayah memori ngendi struktur iki dumunung ing peta ing mode readonly.
  5. Remap file menyang papan alamat proses kanggo tujuan, contone, nambah ukuran panyimpenan maksimum nggunakake fungsi mdb_env_set_map_size rampung invalidates kabeh transaksi lan entitas sing gegandhengan ing umum lan penunjuk kanggo obyek tartamtu ing tartamtu.

Pungkasan, fitur liyane banget insidious sing mbukak inti ora pas karo paragraf liyane. Ing bab bab B-wit, Aku menehi diagram carane kaca sawijining disusun ing memori. Saka iki, alamat wiwitan buffer kanthi data serial bisa pancen sewenang-wenang. Amarga iki, pointer kanggo wong-wong mau ditampa ing struktur MDB_val lan suda kanggo pitunjuk kanggo struktur, iku dadi unaaligned ing kasus umum. Ing wektu sing padha, arsitektur sawetara chip (ing kasus iOS iki armv7) mbutuhake alamat data apa wae dadi pirang-pirang ukuran tembung mesin utawa, kanthi tembung liya, ukuran bit sistem ( kanggo armv7 iku 32 bit). Ing tembung liyane, operasi kaya *(int *foo)0x800002 ing wong-wong mau padha karo uwal lan ndadékaké kanggo eksekusi karo putusan EXC_ARM_DA_ALIGN. Ana rong cara kanggo nyingkiri nasib sing sedhih.

Kaping pisanan yaiku nyalin data awal menyang struktur sing jelas. Contone, ing komparator khusus iki bakal dibayangke kaya ing ngisor iki.

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

Cara alternatif yaiku menehi kabar luwih dhisik marang kompiler yen struktur nilai kunci bisa uga ora selaras karo atribut aligned(1). Ing ARM sampeyan bisa duwe efek sing padha entuk lan nggunakake atribut packed. Menimbang manawa uga mbantu ngoptimalake ruang sing dikuwasani dening struktur, metode iki luwih disenengi kanggo aku, sanajan приводит kanggo nambah biaya operasi akses data.

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

Pitakonan kisaran

Kanggo ngulang klompok rekaman, LMDB nyedhiyakake abstraksi kursor. Ayo goleki cara nggarap kanthi nggunakake conto tabel kanthi metadata awan pangguna sing wis kenal karo kita.

Minangka bagéan saka nampilake dhaptar file ing direktori, sampeyan kudu nemokake kabeh tombol sing ana gandhengane karo file lan folder anak. Ing bagean sadurunge kita ngurutake tombol NodeKey kuwi sing padha utamané dhawuh dening ID saka direktori tiyang sepah. Mangkono, kanthi teknis, tugas kanggo njupuk isi folder mudhun kanggo nempatake kursor ing wates ndhuwur klompok tombol kanthi ater-ater sing diwenehake lan banjur ngulang menyang wates ngisor.

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOS

Watesan ndhuwur bisa ditemokake langsung kanthi nggoleki urutan. Kanggo nindakake iki, kursor diselehake ing wiwitan kabeh dhaptar kunci ing basis data lan ditambahake nganti tombol karo pengenal direktori induk katon ing ngisor iki. Pendekatan iki nduweni 2 kelemahan sing jelas:

  1. Kompleksitas telusuran linear, sanajan, kaya sing dingerteni, ing wit-witan umume lan ing wit-B khusus, bisa ditindakake ing wektu logaritma.
  2. Tanpa guna, kabeh kaca sadurunge sing digoleki diangkat saka file menyang memori utama, sing larang banget.

Untunge, API LMDB nyedhiyakake cara sing efektif kanggo posisi kursor wiwitan. Kanggo nindakake iki, sampeyan kudu ngasilake kunci sing nilaine temenan kurang saka utawa padha karo tombol sing ana ing wates ndhuwur interval. Contone, ing hubungan kanggo dhaftar ing tokoh ndhuwur, kita bisa nggawe tombol kang lapangan parentId bakal padha karo 2, lan kabeh liyane kapenuhan nul. Tombol sing diisi sebagian diwenehake menyang input fungsi mdb_cursor_get nuduhake operasi 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);

Yen wates ndhuwur saka klompok tombol ditemokake, banjur kita terusake nganti kita ketemu utawa tombol ketemu liyane. parentId, utawa kuncine ora bakal entek babar pisan.​

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

Apa sing paling apik yaiku minangka bagean saka pengulangan nggunakake mdb_cursor_get, ora mung kunci, nanging uga regane. Yen, kanggo ngrampungake kahanan sampling, sampeyan kudu mriksa, antara liya, kolom saka bagean nilai rekaman, mula bisa diakses tanpa sadurunge nyeret tambahan.

4.3. Hubungan modeling antarane tabel

Saiki, kita wis bisa nimbang kabeh aspek ngrancang lan nggarap database tabel siji. Kita bisa ngomong yen tabel minangka kumpulan rekaman sing diurutake sing kasusun saka pasangan kunci-nilai sing padha. Yen sampeyan nampilake tombol minangka persegi panjang lan nilai sing digandhengake minangka parallelepiped, sampeyan bakal entuk diagram visual saka database.

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOS

Nanging, ing urip nyata iku arang bisa kanggo njaluk dening sethitik getihen. Asring ing basis data dibutuhake, pisanan, duwe sawetara tabel, lan kaping pindho, kanggo nggawe pilihan ing urutan sing beda karo kunci utama. Bagian pungkasan iki dikhususake kanggo masalah nggawe lan sesambungan.

Tabel indeks

Aplikasi maya duwe bagean "Galeri". Nampilake konten media saka kabeh awan, diurutake miturut tanggal. Kanggo ngleksanakake pilihan kasebut kanthi optimal, ing jejere tabel utama sampeyan kudu nggawe liyane kanthi jinis tombol anyar. Bakal ngemot kolom kanthi tanggal file digawe, sing bakal dadi kriteria pangurutan utama. Amarga tombol anyar ngrujuk data sing padha karo tombol ing tabel utama, diarani tombol indeks. Ing gambar ing ngisor iki padha disorot ing oranye.

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOS

Kanggo misahake tombol tabel sing beda-beda ing basis data sing padha, tableId lapangan teknis tambahan ditambahake menyang kabeh. Kanthi nggawe prioritas paling dhuwur kanggo ngurutake, kita bakal entuk panglompokan kunci dhisik miturut tabel, lan ing tabel - miturut aturan kita dhewe.​

Tombol indeks ngrujuk data sing padha karo kunci utama. Implementasi langsung saka properti iki kanthi digandhengake karo salinan bagean nilai kunci utama ora optimal saka sawetara sudut pandang:

  1. Ing babagan ruang sing dijupuk, metadata bisa dadi sugih.
  2. Saka sudut pandang kinerja, amarga nalika nganyari metadata simpul, sampeyan kudu nulis ulang nggunakake rong tombol.
  3. Saka sudut pandang dhukungan kode, yen kita lali nganyari data kanggo salah sawijining kunci, kita bakal entuk bug sing ora bisa dipahami babagan inkonsistensi data ing panyimpenan.

Sabanjure, kita bakal nimbang carane ngilangi kekurangan kasebut.

Ngatur hubungan antarane tabel

Pola kasebut cocog kanggo ngubungake tabel indeks karo tabel utama "kunci minangka nilai". Minangka jeneng kasebut, bagean nilai saka rekaman indeks minangka salinan saka nilai kunci utama. Pendekatan iki ngilangi kabeh cacat sing kasebut ing ndhuwur sing ana gandhengane karo nyimpen salinan bagean nilai saka rekaman utami. Biaya mung kanggo entuk nilai kanthi kunci indeks, sampeyan kudu nggawe 2 pitakon menyang database tinimbang siji. Secara skematis, skema database sing diasilake katon kaya iki.

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOS

Pola liyane kanggo ngatur hubungan antarane tabel yaiku "kunci redundant". Intine yaiku nambah atribut tambahan menyang kunci, sing ora dibutuhake kanggo ngurutake, nanging kanggo nggawe maneh tombol sing gegandhengan. Ing aplikasi Mail.ru Cloud ana conto nyata panggunaane, nanging supaya ora nyilem jero. konteks frameworks iOS tartamtu, Aku bakal menehi fiktif, nanging contone luwih cetha

Klien seluler awan duwe kaca sing nuduhake kabeh file lan folder sing dienggo bareng pangguna karo wong liya. Amarga ana sawetara file kasebut, lan ana macem-macem jinis informasi spesifik babagan publisitas sing ana gandhengane (sing diwenehake akses, hak apa, lan liya-liyane), mula ora rasional kanggo beban bagean regane. ngrekam ing meja utama karo. Nanging, yen sampeyan pengin nampilake file kasebut ing offline, sampeyan isih kudu nyimpen ing endi wae. Solusi alami yaiku nggawe tabel sing kapisah. Ing diagram ing ngisor iki, kunci kasebut diawali karo "P", lan "propname" placeholder bisa diganti karo nilai sing luwih spesifik "info umum".​

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOS

Kabeh metadata unik, kanggo nyimpen sing meja anyar digawe, diselehake ing bagean nilai saka rekaman. Ing wektu sing padha, sampeyan ora pengin duplikat data babagan file lan folder sing wis disimpen ing tabel utama. Nanging, data keluwih ditambahake menyang tombol "P" ing wangun kolom "node ID" lan "timestamp". Thanks kanggo wong-wong mau, sampeyan bisa nggawe kunci indeks, saka ngendi sampeyan bisa entuk kunci utama, saka ngendi, pungkasane, sampeyan bisa entuk metadata simpul.

Kesimpulan

Kita netepake asil implementasine LMDB kanthi positif. Sawise iku, jumlah aplikasi beku suda 30%.

Cahya lan mlarat saka basis data kunci-nilai LMDB ing aplikasi iOS

Asil saka karya rampung resonated ngluwihi tim iOS. Saiki, salah sawijining bagean "File" utama ing aplikasi Android uga wis ngalih nggunakake LMDB, lan bagean liyane ana ing dalan. Basa C, ing ngendi toko kunci-nilai diimplementasikake, minangka bantuan sing apik kanggo nggawe kerangka aplikasi ing saubengé lintas-platform ing C ++. Generator kode digunakake kanggo nyambungake perpustakaan C++ kanthi lancar karo kode platform ing Objective-C lan Kotlin Djinni saka Dropbox, nanging sing crita temen beda.

Source: www.habr.com

Add a comment