iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігі

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігі

2019 жылдың күзінде Mail.ru Cloud iOS командасында көптен күткен оқиға болды. Қолданба күйін тұрақты сақтауға арналған негізгі деректер базасы мобильді әлем үшін өте экзотикалық болды Lightning Memory-Mapped Database (LMDB). Кесудің астында біз сізге төрт бөліктен тұратын егжей-тегжейлі шолуды ұсынамыз. Алдымен, мұндай тривиальды емес және қиын таңдаудың себептері туралы сөйлесейік. Содан кейін біз LMDB архитектурасының негізіндегі үш тіректерді қарастыруға көшеміз: жадпен салыстырылған файлдар, B+-ағаш, транзакциялық және мультиверсияны жүзеге асыруға арналған көшіру-жазба әдісі. Соңында, десерт үшін - практикалық бөлік. Онда біз төменгі деңгейлі кілт-мән API-нің үстіне индексті қоса, бірнеше кестелері бар дерекқор схемасын құрастыру және енгізу жолын қарастырамыз.

Мазмұны

  1. Іске асыру үшін мотивация
  2. LMDB позициялауы
  3. LMDB үш тірегі
    3.1. №1 кит. Жадпен салыстырылған файлдар
    3.2. Кит №2. B+-ағаш
    3.3. Кит №3. Жазу бойынша көшіру
  4. API кілт-мәні үстіне деректер схемасын құрастыру
    4.1. Негізгі абстракциялар
    4.2. Кестені модельдеу
    4.3. Кестелер арасындағы байланыстарды модельдеу

1. Іске асыруға мотивация

2015 жылы бір жыл біз қолданбамыздың интерфейсінің қаншалықты жиі кешігетінін өлшеуге қиындық тудырдық. Біз мұны бір себеппен жасадық. Бізге кейде қолданба пайдаланушы әрекеттеріне жауап беруді тоқтататыны туралы жиі шағымдар алдық: түймелерді басу мүмкін емес, тізімдер айналдырмайды және т.б. Өлшемдер механикасы туралы деді AvitoTech бойынша, сондықтан мен мұнда тек сандар ретін беремін.

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігі

Өлшеу нәтижелері біз үшін суық душ болды. Басқаларға қарағанда, қатып қалудан туындайтын мәселелер көп екені белгілі болды. Егер осы фактіні түсінгенге дейін сапаның негізгі техникалық көрсеткіші апатсыз болса, фокустан кейін ауыстырылды мұздату тегін.

Салған мұздатулары бар бақылау тақтасы және жұмсағаннан кейін сандық и сапасы олардың себептерін талдау, басты жау айқын болды - қосымшаның негізгі ағынында орындалған ауыр іскерлік логика. Бұл масқараға табиғи реакция оны жұмыс ағындарына итермелеуге деген ыстық ықылас болды. Бұл мәселені жүйелі түрде шешу үшін біз жеңіл актерлерге негізделген көп ағынды архитектураға жүгіндік. Мен оны iOS әлеміне бейімдеуге арнадым екі жіп ұжымдық Twitter және Хабре туралы мақала. Ағымдағы әңгіменің бөлігі ретінде мен дерекқорды таңдауға әсер еткен шешімнің аспектілерін атап өткім келеді.

Жүйені ұйымдастырудың акторлық моделі көп ағынды оның екінші мәніне айналады деп болжайды. Ондағы үлгі нысандары ағынның шекараларын кесіп өтуді ұнатады. Және олар мұны кейде және мұнда және мұнда емес, үнемі және барлық жерде жасайды

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігі

Мәліметтер қоры ұсынылған диаграммадағы негізгі құрамдас бөліктердің бірі болып табылады. Оның негізгі міндеті - макроүлгіні жүзеге асыру Ортақ деректер қоры. Егер кәсіпорын әлемінде ол қызметтер арасында деректерді синхрондауды ұйымдастыру үшін пайдаланылса, актер архитектурасы жағдайында – ағындар арасындағы деректер. Осылайша, бізге көп ағынды ортада онымен жұмыс істеу кезінде ең аз қиындықтар тудырмайтын мәліметтер базасы қажет болды. Атап айтқанда, бұл одан алынған нысандар кем дегенде жіпке қауіпсіз және ең дұрысы толығымен өзгермейтін болуы керек дегенді білдіреді. Өздеріңіз білетіндей, соңғысы өнімділікке жақсы әсер ететін ешқандай құлыптауды қолданбай бірнеше жіптерден бір уақытта қолданылуы мүмкін.

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігіДерекқорды таңдауға әсер еткен екінші маңызды фактор біздің бұлттық API болды. Ол git қабылдаған синхрондау тәсілінен шабыттанды. Ол сияқты біз де мақсат еттік офлайн-бірінші API, бұл бұлттық клиенттер үшін қолайлырақ көрінеді. Олар бұлттың толық күйін тек бір рет шығарады деп болжанған, содан кейін синхрондау жағдайлардың басым көпшілігінде өзгерістерді енгізу арқылы жүзеге асады. Өкінішке орай, бұл мүмкіндік әлі тек теориялық аймақта, ал клиенттер іс жүзінде патчтармен жұмыс істеуді үйренген жоқ. Мұның бірқатар объективті себептері бар, олар енгізуді кейінге қалдырмау үшін біз жақшаларды қалдырамыз. Енді API «A» деп айтса, ал оның тұтынушысы «В» деп айтпаса, не болатыны туралы сабақтың нұсқаулық қорытындылары қызықтырақ.

Сонымен, егер сіз тарту пәрменін орындау кезінде жергілікті суретке патчтарды қолданудың орнына оның толық күйін толық сервер күйімен салыстыратын git-ті елестетсеңіз, онда сіз бұлтта синхрондау қалай жүретіні туралы нақты түсінікке ие боласыз. клиенттер. Оны жүзеге асыру үшін жадта барлық сервер және жергілікті файлдар туралы мета ақпараты бар екі DOM ағашын бөлу керек екенін болжау оңай. Егер пайдаланушы бұлтта 500 мың файлды сақтаса, оны синхрондау үшін 1 миллион түйіні бар екі ағашты қайта жасап, жою керек екен. Бірақ әрбір түйін ішкі нысандар графигін қамтитын жиынтық болып табылады. Осы тұрғыда профильдеу нәтижелері күтілді. Біріктіру алгоритмін есепке алмағанда да, үлкен көлемдегі шағын нысандарды құру және кейіннен жою процедурасының өзі айтарлықтай тиын тұратыны белгілі болды.Жағдайды негізгі синхрондау операциясының үлкен санға қосу фактісі қиындатады. пайдаланушы сценарийлерінің. Нәтижесінде біз дерекқорды таңдауда екінші маңызды критерийді бекітеміз - объектілерді динамикалық бөлусіз CRUD операцияларын жүзеге асыру мүмкіндігі.

Басқа талаптар дәстүрлі және олардың толық тізімі төмендегідей.

  1. Жіп қауіпсіздігі.
  2. Көп өңдеу. Күйді тек ағындар арасында ғана емес, сонымен қатар негізгі қолданба мен iOS кеңейтімдері арасында синхрондау үшін бірдей дерекқор данасын пайдалану ниетімен байланысты.
  3. Сақталған нысандарды өзгермейтін нысандар ретінде көрсету мүмкіндігі.​
  4. CRUD операцияларында динамикалық бөлулер жоқ.
  5. Негізгі сипаттар үшін транзакцияны қолдау ACID: атомдылық, консистенциясы, оқшаулануы және сенімділігі.
  6. Ең танымал жағдайларда жылдамдық.

Осы талаптар жиынтығымен SQLite жақсы таңдау болды және болып қала береді. Дегенмен, баламаларды зерттеу аясында мен бір кітапқа тап болдым «LevelDB-мен жұмысты бастау». Оның жетекшілігімен нақты бұлттық сценарийлердегі әртүрлі дерекқорлармен жұмыс жылдамдығын салыстыратын эталон жазылды. Нәтиже біздің күткенімізден де асып түсті. Ең танымал жағдайларда - барлық файлдардың сұрыпталған тізімінде курсорды алу және берілген каталог үшін барлық файлдардың сұрыпталған тізімін алу - LMDB SQLite қарағанда 10 есе жылдамырақ болды. Таңдау айқын болды.

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігі

2. LMDB позициялау

LMDB - бұл дерекқордың ең төменгі іргелі қабатын - сақтауды жүзеге асыратын өте кішкентай кітапхана (тек 10К жол).

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігі

Жоғарыда келтірілген диаграмма LMDB-ті жоғары деңгейлерді жүзеге асыратын SQLite-пен салыстыру, әдетте, негізгі деректері бар SQLite-тен дұрыс емес екенін көрсетеді. Бірдей сақтау қозғалтқыштарын тең бәсекелестер ретінде атаған дұрысырақ болар еді - BerkeleyDB, LevelDB, Sophia, RocksDB және т.б. Тіпті LMDB SQLite үшін сақтау механизмінің құрамдас бөлігі ретінде әрекет ететін әзірлемелер бар. Алғашқы мұндай эксперимент 2012 жылы болды өткізді LMDB арқылы Ховард Чу. нәтижелері қызықты болғаны сонша, оның бастамасын OSS әуесқойлары көтеріп, оның жалғасын адамда тапты. LumoSQL. 2020 жылдың қаңтарында бұл жобаның авторы Ден Ширер болды ұсынылды ол LinuxConfAu.

LMDB негізінен қолданбалы дерекқорларға арналған қозғалтқыш ретінде пайдаланылады. Кітапхана өзінің пайда болуы үшін әзірлеушілерге қарыздар OpenLDAP, олар өз жобаларының негізі ретінде BerkeleyDB-ге қатты наразы болды. Қарапайым кітапханадан бастап btree, Ховард Чу біздің заманымыздың ең танымал баламаларының бірін жасай алды. Ол өзінің керемет баяндамасын осы оқиғаға, сондай-ақ LMDB ішкі құрылымына арнады. «Lightning Memory-карталанған деректер қоры». Сақтау орнын бағындырудың жақсы мысалын Леонид Юрьев (ака yleo) Positive Technologies компаниясының Highload 2015 баяндамасында «LMDB қозғалтқышы ерекше чемпион». Онда ол LMDB туралы ReOpenLDAP енгізудің ұқсас тапсырмасы контекстінде айтады және LevelDB салыстырмалы сынға ұшырады. Іске асыру нәтижесінде Positive Technologies тіпті белсенді дамып келе жатқан шанышқыға ие болды MDBX өте дәмді мүмкіндіктермен, оңтайландырулармен және қателерді түзету.

LMDB жиі сақтау орны ретінде пайдаланылады. Мысалы, Mozilla Firefox шолғышы таңдады ол бірқатар қажеттіліктер үшін және 9 нұсқасынан бастап, Xcode артықшылықты оның индекстерін сақтауға арналған SQLite.

Қозғалтқыш мобильді даму әлемінде де өз ізін қалдырды. Оны пайдаланудың іздері болуы мүмкін табу Telegram үшін iOS клиентінде. LinkedIn одан әрі ілгерілеп, LMDB-ті өзінің үйдегі деректерді кэштеу жүйесі Rocket Data үшін әдепкі сақтау орны ретінде таңдады. деді 2016 жылғы мақаласында.

LMDB Oracle бақылауына өткеннен кейін BerkeleyDB қалдырған тауашада күн сәулесіндегі орын үшін сәтті күресуде. Кітапхана өзінің құрдастарымен салыстырғанда жылдамдығы мен сенімділігі үшін жақсы көреді. Өздеріңіз білетіндей, тегін түскі ас жоқ, мен LMDB және SQLite арасында таңдау жасау кезінде кездесетін ымырасыздықты атап өткім келеді. Жоғарыдағы диаграмма жылдамдықты арттыруға қалай қол жеткізілетінін анық көрсетеді. Біріншіден, біз дискілік жадының үстіне абстракцияның қосымша қабаттары үшін төлемейміз. Жақсы сәулет әлі де оларсыз жасай алмайтыны анық және олар міндетті түрде қолданба кодында пайда болады, бірақ олар әлдеқайда нәзік болады. Оларда арнайы қолданба талап етпейтін мүмкіндіктер болмайды, мысалы, SQL тіліндегі сұрауларды қолдау. Екіншіден, дискілік жадқа сұраныстар бойынша қолданбалы операцияларды салыстыруды оңтайлы түрде жүзеге асыру мүмкін болады. Егер SQLite менің жұмысымда орташа қолданбаның орташа статистикалық қажеттіліктеріне негізделген болса, сіз қолданбаны әзірлеуші ​​ретінде негізгі жұмыс жүктемесі сценарийлерін жақсы білесіз. Неғұрлым өнімді шешім үшін бастапқы шешімді әзірлеу үшін де, оны кейінгі қолдау үшін де жоғары баға тегін төлеуге тура келеді.

3. LMDB үш тірегі

LMDB-ге құс көзімен қараған соң, тереңірек өтудің уақыты келді. Келесі үш бөлім сақтау архитектурасы негізделген негізгі тіректерді талдауға арналған:

  1. Жадпен салыстырылған файлдар дискімен жұмыс істеу және ішкі деректер құрылымдарын синхрондау механизмі ретінде.
  2. B+-ағаш сақталатын деректер құрылымының ұйымы ретінде.
  3. ACID транзакция қасиеттерін және мультиверсияны қамтамасыз ету тәсілі ретінде көшіру.

3.1. №1 кит. Жадпен салыстырылған файлдар

Жадпен салыстырылған файлдар маңызды архитектуралық элемент болып табылады, олар тіпті репозиторий атауында пайда болады. Сақталған ақпаратқа қол жеткізуді кэштеу және синхрондау мәселелері толығымен операциялық жүйеге жүктеледі. LMDB ішінде ешбір кэш жоқ. Бұл автордың саналы шешімі, өйткені салыстырылған файлдардан деректерді тікелей оқу қозғалтқышты іске асыруда көптеген бұрыштарды кесуге мүмкіндік береді. Төменде олардың кейбіреулерінің толық тізімі берілген.

  1. Бірнеше процестерден онымен жұмыс істеу кезінде жадтағы деректердің бірізділігін сақтау операциялық жүйенің міндетіне айналады. Келесі бөлімде бұл механика егжей-тегжейлі және суреттермен талқыланады.
  2. Кэштердің болмауы LMDB-ді динамикалық бөлумен байланысты үстеме шығындардан толығымен жояды. Тәжірибеде деректерді оқу виртуалды жадтағы дұрыс мекенжайға көрсеткіш орнатуды білдіреді және басқа ештеңе емес. Бұл ғылыми фантастика сияқты естіледі, бірақ сақтаудың бастапқы кодында calloc-қа барлық қоңыраулар сақтау конфигурациясы функциясында шоғырланған.
  3. Кэштердің жоқтығы сонымен қатар оларға қол жеткізуді синхрондаумен байланысты құлыптардың жоқтығын білдіреді. Оқырмандардың бір уақытта ерікті саны болуы мүмкін оқырмандар деректерге бару жолында бірде-бір мутексті кездестірмейді. Осыған байланысты оқу жылдамдығы процессорлар санына негізделген тамаша сызықтық масштабтауға ие. LMDB ішінде тек өзгерту әрекеттері үндестіріледі. Бір уақытта бір ғана жазушы болуы мүмкін.
  4. Кэштеу және үндестіру логикасының минимумы көп ағынды ортада жұмыс істеуге байланысты қателердің өте күрделі түрін жояды. Usenix OSDI 2014 конференциясында екі қызықты дерекқорды зерттеу болды: «Барлық файлдық жүйелер бірдей жасалмаған: апатқа ұшырайтын жүйелі қолданбаларды жасаудың күрделілігі туралы» и «Дерекқорларды ермек пен пайда үшін азаптау». Олардан сіз LMDB-нің бұрын-соңды болмаған сенімділігі туралы және SQLite-тен жоғары ACID транзакция қасиеттерінің мінсіз дерлік орындалуы туралы ақпаратты ала аласыз.
  5. LMDB минимализмі оның кодының машиналық көрінісін процессордың L1 кэшінде келесі жылдамдық сипаттамаларымен толығымен орналастыруға мүмкіндік береді.

Өкінішке орай, жад картасы бар iOS жүйесінде бәрі біз қалағандай бұлтсыз емес. Олармен байланысты кемшіліктер туралы көбірек саналы түрде айту үшін операциялық жүйелерде осы механизмді енгізудің жалпы принциптерін есте сақтау қажет.

Жадпен салыстырылған файлдар туралы жалпы ақпарат

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігіӘрбір іске қосылған қолданбамен операциялық жүйе процесс деп аталатын нысанды байланыстырады. Әрбір процеске ол жұмыс істеуге қажеттінің барлығын орналастыратын мекенжайлардың іргелес диапазоны бөлінеді. Ең төменгі мекенжайларда код және қатты кодталған деректер мен ресурстар бар бөлімдер бар. Содан кейін бізге үйме атауымен жақсы белгілі динамикалық мекенжай кеңістігінің өсіп келе жатқан блогы келеді. Онда бағдарламаның жұмыс істеуі кезінде пайда болатын нысандардың мекенжайлары бар. Жоғарғы жағында қолданбалар стекі пайдаланатын жад аймағы орналасқан. Ол өседі немесе қысқарады, басқаша айтқанда, оның көлемі де динамикалық сипатқа ие. Стек пен үйменің бір-біріне итеруіне және кедергі жасауына жол бермеу үшін олар мекенжай кеңістігінің әртүрлі ұштарында орналасқан.​ Жоғарғы және төменгі жағында екі динамикалық бөлімнің арасында тесік бар. Операциялық жүйе әртүрлі нысандарды процесспен байланыстыру үшін осы ортаңғы бөлімдегі мекенжайларды пайдаланады. Атап айтқанда, ол белгілі бір үздіксіз адрестер жиынын дискідегі файлмен байланыстыра алады. Мұндай файл жад картасы деп аталады.​

Процесске бөлінген мекенжай кеңістігі өте үлкен. Теориялық тұрғыдан адрестер саны жүйенің разрядтық сыйымдылығымен анықталатын көрсеткіш өлшемімен ғана шектеледі. Егер физикалық жад оны 1-ден 1-ге дейін салыстырса, онда бірінші процесс бүкіл ЖЖҚ-ны жұтып қояды және көп тапсырма туралы сөз болмас еді.

Дегенмен, біздің тәжірибемізден біз қазіргі заманғы операциялық жүйелер бір уақытта қалағандай көптеген процестерді орындай алатынын білеміз. Бұл олардың қағаздағы процестерге тек көп жадты бөлуіне байланысты мүмкін, бірақ шын мәнінде олар негізгі физикалық жадқа осы жерде және қазір сұранысқа ие бөлікті ғана жүктейді. Сондықтан процесспен байланысты жады виртуалды деп аталады.

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігі

Операциялық жүйе виртуалды және физикалық жадты белгілі бір өлшемдегі беттерге ұйымдастырады. Виртуалды жадтың белгілі бір парағы сұранысқа ие болған кезде операциялық жүйе оны физикалық жадқа жүктейді және оларды арнайы кестеде сәйкестендіреді. Егер бос слоттар болмаса, бұрын жүктелген беттердің бірі дискіге көшіріледі, ал сұранысқа ие болады. Біз жақында қайтатын бұл процедура свопинг деп аталады. Төмендегі суретте сипатталған процесс көрсетілген. Онда 0 мекенжайы бар А беті жүктелді және 4 мекенжайы бар негізгі жад бетіне орналастырылды. Бұл факт 0 ұяшықтағы сәйкестік кестесінде көрсетілді.

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігі

Жадқа салынған файлдармен оқиға дәл солай. Логикалық түрде олар виртуалды мекенжай кеңістігінде үздіксіз және толығымен орналасқан. Дегенмен, олар физикалық жадты бет бойынша және тек сұрау бойынша енгізеді. Мұндай беттерді өзгерту дискідегі файлмен синхрондалады. Осылайша, жадтағы байттармен жұмыс істеу арқылы файл енгізу/шығаруын орындауға болады - барлық өзгерістер операциялық жүйе ядросы арқылы бастапқы файлға автоматты түрде тасымалданады.
مور
Төмендегі сурет әртүрлі процестердегі дерекқормен жұмыс істегенде LMDB күйін қалай үндестіретінін көрсетеді. Әртүрлі процестердің виртуалды жадын бір файлға салыстыру арқылы біз амалдық жүйені олардың адрестік кеңістіктерінің белгілі блоктарын LMDB көрінетін бір-бірімен транзитивтік түрде синхрондауға міндеттіміз.
مور

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігі

Маңызды нюанс мынада, LMDB әдепкі бойынша деректер файлын жазу жүйесін шақыру механизмі арқылы өзгертеді және файлдың өзін тек оқуға арналған режимде көрсетеді. Бұл тәсілдің екі маңызды салдары бар.

Бірінші нәтиже барлық операциялық жүйелерге тән. Оның мәні қате кодпен мәліметтер қорын байқаусызда зақымдаудан қорғауды қосу болып табылады. Өздеріңіз білетіндей, процестің орындалатын нұсқаулары оның мекенжай кеңістігінің кез келген жерінен деректерге еркін қол жеткізе алады. Сонымен қатар, біз жаңа ғана еске түсіргендей, файлды оқу-жазу режимінде көрсету кез келген нұсқау оны өзгерте алатынын білдіреді. Егер ол мұны қателесіп, мысалы, жоқ индексте массив элементін қайта жазуға тырысса, онда ол осы мекенжаймен салыстырылған файлды кездейсоқ өзгерте алады, бұл дерекқордың бүлінуіне әкеледі. Егер файл тек оқуға арналған режимде көрсетілсе, онда сәйкес мекенжай кеңістігін өзгерту әрекеті бағдарламаның сигналмен төтенше тоқтатылуына әкеледі. SIGSEGV, және файл өзгеріссіз қалады.

Екінші нәтиже iOS-қа тән. Автор да, басқа көздер де бұл туралы нақты айтпайды, бірақ онсыз LMDB бұл мобильді операциялық жүйеде жұмыс істеуге жарамсыз еді. Келесі бөлім оны қарастыруға арналған.

IOS жүйесіндегі жадпен салыстырылған файлдардың ерекшеліктері

2018 жылы WWDC-те тамаша есеп болды «iOS жадына терең бойлау». Ол iOS жүйесінде физикалық жадта орналасқан барлық беттер 3 түрдің бірі екенін айтады: лас, қысылған және таза.

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігі

Таза жад - бұл физикалық жадтан ауыртпалықсыз түсіруге болатын беттер жиынтығы. Олардағы деректерді қажетінше бастапқы көздерден қайта жүктеуге болады. Тек оқуға арналған жадпен салыстырылған файлдар осы санатқа жатады. iOS файлмен салыстырылған беттерді кез келген уақытта жадтан босатудан қорықпайды, өйткені олардың дискідегі файлмен синхрондалуына кепілдік беріледі.
مور
Барлық өзгертілген беттер бастапқыда қай жерде орналасқанына қарамастан, лас жадта қалады. Атап айтқанда, олармен байланысты виртуалды жадқа жазу арқылы өзгертілген жадпен салыстырылған файлдар осылай жіктеледі. LMDB жалаушамен ашылады MDB_WRITEMAP, оған өзгертулер енгізгеннен кейін сіз мұны жеке тексере аласыз.​

Қолданба тым көп физикалық жадты ала бастағанда, iOS оны лас бетті қысуға ұшыратады. Лас және қысылған беттер алатын жалпы жад қолданбаның жад ізі деп аталатын жерді құрайды. Белгілі бір шекті мәнге жеткенде, OOM өлтіруші жүйесінің демоны процестен кейін келеді және оны мәжбүрлеп тоқтатады. Бұл iOS-тың жұмыс үстелі операциялық жүйелерімен салыстырғандағы ерекшелігі. Керісінше, беттерді физикалық жадтан дискіге ауыстыру арқылы жад ізін азайту iOS жүйесінде қарастырылмаған.Себептерді тек болжауға болады. Мүмкін беттерді дискіге және артқа қарқынды жылжыту процедурасы мобильді құрылғылар үшін тым көп энергияны қажет етеді немесе iOS SSD дискілеріндегі ұяшықтарды қайта жазу ресурсын үнемдейді немесе дизайнерлер жүйенің жалпы өнімділігіне қанағаттанбаған болуы мүмкін. үнемі ауыстырылады. Қалай болғанда да, бұл факт факт болып қала береді.

Жоғарыда айтылған жақсы жаңалық, LMDB әдепкі бойынша файлдарды жаңарту үшін mmap механизмін пайдаланбайды. Бұл көрсетілген деректердің iOS жүйесі бойынша таза жад ретінде жіктелетінін және жад ізіне ықпал етпейтінін білдіреді. Мұны VM Tracker деп аталатын Xcode құралы арқылы тексеруге болады. Төмендегі скриншот жұмыс кезінде Cloud қолданбасының iOS виртуалды жадының күйін көрсетеді. Бастапқыда онда 2 LMDB данасы инициализацияланды. Біріншісіне өз файлын 1ГиБ виртуалды жадта көрсетуге рұқсат етілді, екіншісіне - 512МБ. Екі қойма да резиденттік жадтың белгілі бір көлемін алатынына қарамастан, олардың ешқайсысы лас өлшемге ықпал етпейді.

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігі

Ал енді жағымсыз жаңалықтардың уақыты келді. 64-биттік жұмыс үстелі операциялық жүйелеріндегі своп механизмінің арқасында әрбір процесс өзінің ықтимал свопына арналған қатты дискідегі бос орын мүмкіндік беретіндей виртуалды мекенжай кеңістігін алады. IOS жүйесінде свопты қысумен ауыстыру теориялық максимумды түбегейлі азайтады. Енді барлық тірі процестер негізгі (оқу жедел жады) жадыға сәйкес келуі керек, ал сәйкес келмейтіндердің барлығын тоқтатуға мәжбүрлеу керек. Бұл жоғарыда айтылғандай айтылған есеп, және ресми құжаттама. Нәтижесінде, iOS mmap арқылы бөлуге болатын жад көлемін айтарлықтай шектейді. Мұнда осында Осы жүйелік қоңырауды пайдаланып әртүрлі құрылғыларда бөлуге болатын жад көлемінің эмпирикалық шектеулерін қарауға болады. Смартфондардың ең заманауи үлгілерінде iOS 2 гигабайтқа жомарт болды, ал iPad-тың жоғарғы нұсқаларында - 4. Іс жүзінде, әрине, ең төменгі қолдау көрсетілетін құрылғы үлгілеріне назар аудару керек, мұнда бәрі өте қайғылы. Одан да сорақысы, VM Tracker бағдарламасындағы қолданбаның жады күйіне қарап, LMDB жад картасына ұқсайтын жалғыз нұсқадан алыс екенін көресіз. Жақсы бөліктерді жүйелік бөлгіштер, ресурс файлдары, кескін жақтаулары және басқа да кішігірім жыртқыштар жейді.

Бұлттағы эксперименттердің нәтижелеріне сүйене отырып, біз LMDB бөлген жады үшін келесі компромисстік мәндерге келдік: 384 биттік құрылғылар үшін 32 мегабайт және 768 биттік құрылғылар үшін 64. Бұл көлем таусылғаннан кейін кез келген өзгерту әрекеттері кодпен аяқтала бастайды MDB_MAP_FULL. Мониторинг барысында біз мұндай қателерді байқаймыз, бірақ олар өте аз, бұл кезеңде оларды елемеуге болады.

Жадтың жадты шамадан тыс тұтынуының айқын емес себебі ұзақ мерзімді транзакциялар болуы мүмкін. Бұл екі құбылыстың қалай байланысты екенін түсіну үшін бізге LMDB қалған екі тірегін қарастыру көмектеседі.

3.2. Кит №2. B+-ағаш

Кілттер-мәнді жадының жоғарғы жағындағы кестелерді эмуляциялау үшін оның API ішінде келесі әрекеттер болуы керек:

  1. Жаңа элементті енгізу.
  2. Берілген кілтпен элементті іздеңіз.
  3. Элементті жою.
  4. Пернелердің аралықтары бойынша сұрыпталған ретімен қайталаңыз.

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігіБарлық төрт операцияны оңай жүзеге асыра алатын ең қарапайым деректер құрылымы екілік іздеу ағашы болып табылады. Оның әрбір түйіні еншілес кілттердің барлық ішкі жиынын екі ішкі ағашқа бөлетін кілтті білдіреді. Сол жақта ата-анасынан кішірек, ал оң жақта үлкенірек болады. Кілттердің реттелген жинағын алуға классикалық ағаш өтулерінің бірі арқылы қол жеткізіледі.​

Екілік ағаштардың дискіге негізделген деректер құрылымы ретінде тиімді болуына кедергі болатын екі негізгі кемшілігі бар. Біріншіден, олардың тепе-теңдік дәрежесін болжау мүмкін емес. Әртүрлі бұтақтардың биіктігі айтарлықтай ерекшеленетін ағаштарды алудың айтарлықтай қаупі бар, бұл күтілетінмен салыстырғанда іздеудің алгоритмдік күрделілігін айтарлықтай нашарлатады. Екіншіден, түйіндер арасындағы кросс-байланыстардың көптігі екілік ағаштарды жадтағы локализациядан айырады.Тұйық түйіндер (олардың арасындағы байланыстар бойынша) виртуалды жадтың мүлдем басқа беттерінде орналасуы мүмкін. Нәтижесінде, ағаштағы бірнеше көршілес түйіндердің қарапайым өтуінің өзі салыстырмалы беттер санын аралауды қажет етуі мүмкін. Бұл тіпті жадтағы деректер құрылымы ретінде екілік ағаштардың тиімділігі туралы айтқан кезде де мәселе, өйткені процессор кэшіндегі беттерді үнемі айналдыру арзан рахат емес. Дискіден түйіндермен байланысты беттерді жиі шығарып алу туралы сөз болғанда, жағдай толығымен болады аянышты.

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігіВ-ағаштар бинарлы ағаштардың эволюциясы бола отырып, алдыңғы абзацта анықталған мәселелерді шешеді. Біріншіден, олар өзін-өзі теңестіреді. Екіншіден, олардың әрбір түйіні еншілес кілттер жинағын 2-ге емес, M реттелген ішкі жиындарға бөледі, ал M саны бірнеше жүздеген, тіпті мыңдаған ретпен айтарлықтай үлкен болуы мүмкін.

Осылайша:

  1. Әрбір түйінде тапсырыс берілген кілттердің көп саны бар және ағаштар өте қысқа.
  2. Ағаш жадта орналасу локализациясының қасиетіне ие болады, өйткені мәні жақын кілттер табиғи түрде бір немесе көрші түйіндерде бір-біріне жақын орналасқан.
  3. Іздестіру операциясы кезінде ағаштан түсу кезінде транзиттік түйіндердің саны азаяды.
  4. Ауқым сұраулары кезінде оқылатын мақсатты түйіндердің саны азаяды, өйткені олардың әрқайсысында реттелген кілттердің көп саны бар.

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігі

LMDB деректерді сақтау үшін B+ ағашы деп аталатын B-ағашының нұсқасын пайдаланады. Жоғарыдағы диаграмма ондағы түйіндердің үш түрін көрсетеді:

  1. Жоғарғы жағында тамыр орналасқан. Ол қойма ішіндегі дерекқор тұжырымдамасынан басқа ештеңені жүзеге асырмайды. Бір LMDB данасында салыстырылған виртуалды мекенжай кеңістігін ортақ пайдаланатын бірнеше дерекқорды жасауға болады. Олардың әрқайсысы өз тамырынан басталады.
  2. Ең төменгі деңгейде жапырақтары бар. Олар және тек оларда дерекқорда сақталған кілт-мән жұптары бар. Айтпақшы, бұл В+-ағаштардың ерекшелігі. Егер кәдімгі B-ағаш мән бөліктерін барлық деңгейлердің түйіндерінде сақтаса, онда B+ вариациясы тек ең төменгі нұсқада болады. Бұл фактіні анықтағаннан кейін біз LMDB-де қолданылатын ағаштың ішкі түрін жай B-ағаш деп атаймыз.
  3. Түбір мен жапырақтардың арасында навигациялық (тармақтық) түйіндері бар 0 немесе одан да көп техникалық деңгейлер болады. Олардың міндеті - кілттердің сұрыпталған жиынтығын жапырақтар арасында бөлу.

Физикалық тұрғыдан түйіндер – алдын ала белгіленген ұзындықтағы жады блоктары. Олардың өлшемі операциялық жүйедегі жад беттерінің өлшеміне еселенген, біз оны жоғарыда қарастырдық. Түйіннің құрылымы төменде көрсетілген. Тақырыпта мета ақпарат бар, олардың ішіндегі ең айқыны, мысалы, бақылау сомасы. Содан кейін деректері бар ұяшықтар орналасқан ығысулар туралы ақпарат келеді. Деректер не кілттер болуы мүмкін, егер біз шарлау түйіндері туралы айтатын болсақ, немесе жапырақтар жағдайында тұтас кілт-мән жұптары болуы мүмкін.​ Жұмыстағы беттердің құрылымы туралы қосымша ақпаратты оқуға болады. «Жоғары өнімділігі бар негізгі құнды дүкендерді бағалау».

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігі

Бет түйіндерінің ішкі мазмұнын қарастыра отырып, біз одан әрі LMDB B ағашын келесі пішінде жеңілдетілген түрде көрсетеміз.

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігі

Түйіндері бар беттер дискіде ретімен орналасады. Жоғары нөмірленген беттер файлдың соңына қарай орналасады. Мета-парақ деп аталатын бет барлық ағаштардың тамырларын табуға болатын ығысулар туралы ақпаратты қамтиды. Файлды ашқан кезде LMDB жарамды мета-парақты іздеу үшін файл бетін соңынан басына дейін сканерлейді және ол арқылы бар дерекқорларды табады.

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігі

Енді деректерді ұйымдастырудың логикалық және физикалық құрылымы туралы түсінікке ие бола отырып, біз LMDB үшінші тірегін қарастыруға көшеміз. Оның көмегімен барлық сақтау түрлендірулері транзакциялық түрде және бір-бірінен оқшауланып, дерекқорға тұтастай мультиверсия қасиетін береді.

3.3. Кит №3. Жазу бойынша көшіру

Кейбір B-ағаш операциялары оның түйіндеріне бірқатар өзгерістер енгізуді қамтиды. Бір мысал - ең жоғары сыйымдылыққа жеткен түйінге жаңа кілт қосу. Бұл жағдайда, біріншіден, түйінді екіге бөлу, екіншіден, оның ата-анасындағы жаңа түйінделген еншілес түйінге сілтеме қосу қажет. Бұл процедура өте қауіпті болуы мүмкін. Егер қандай да бір себептермен (апаттық, электр қуатының үзілуі және т.б.) сериядағы өзгерістердің бір бөлігі ғана орын алса, онда ағаш сәйкес емес күйде қалады.

Дерекқорды қатеге төзімді етудің дәстүрлі шешімдерінің бірі B-ағашының жанына қосымша дискідегі деректер құрылымын қосу болып табылады - алдын ала жазу журналы (WAL) деп те белгілі транзакциялар журналы. Бұл файл, оның соңында жоспарланған операция B-ағашының өзін өзгерту алдында қатаң түрде жазылады. Осылайша, өзін-өзі диагностикалау кезінде деректердің бүлінуі анықталса, дерекқор өзін ретке келтіру үшін журналға жүгінеді.

LMDB қатеге төзімділік механизмі ретінде көшіру бойынша жазу деп аталатын басқа әдісті таңдады. Оның мәні бар беттегі деректерді жаңартудың орнына алдымен оны толығымен көшіреді және көшірмедегі барлық өзгертулерді жасайды.

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігі

Әрі қарай, жаңартылған деректер қолжетімді болуы үшін оның негізгі түйінінде ағымдағы болған түйінге сілтемені өзгерту қажет. Ол үшін де өзгерту қажет болғандықтан, ол да алдын ала көшіріледі. Процесс түбірге дейін рекурсивті түрде жалғасады. Өзгертетін соңғы нәрсе - мета беттегі деректер.​

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігі

Жаңарту процедурасы кезінде кенеттен процесс бұзылса, онда жаңа мета бет жасалмайды немесе ол дискіге толығымен жазылмайды және оның бақылау сомасы дұрыс емес болады. Осы екі жағдайдың кез келгенінде жаңа беттерге қол жетімсіз болады, бірақ ескі беттерге әсер етпейді. Бұл деректер сәйкестігін сақтау үшін LMDB журналына алдын ала жазу қажеттілігін жояды. Де-факто, жоғарыда сипатталған дискідегі деректерді сақтау құрылымы бір уақытта өз қызметін алады. Ашық транзакция журналының болмауы деректерді оқудың жоғары жылдамдығын қамтамасыз ететін LMDB мүмкіндіктерінің бірі болып табылады.

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігі

Тек қосымша B-ағаш деп аталатын нәтиже дизайны транзакцияны оқшаулауды және көп нұсқаны қамтамасыз етеді. LMDB ішінде әрбір ашық транзакция ағымдағы сәйкес ағаш түбірімен байланыстырылады. Транзакция аяқталмайынша, онымен байланыстырылған ағаштың беттері ешқашан өзгертілмейді немесе деректердің жаңа нұсқалары үшін қайта пайдаланылмайды.Осылайша, сіз сол уақытта өзекті деректер жиынтығымен қалағаныңызша жұмыс істей аласыз. жад қазіргі уақытта белсенді түрде жаңартылса да, транзакция ашылды. Бұл LMDB-ті сүйіктіміз үшін тамаша деректер көзі ететін мультиверсияның мәні UICollectionView. Транзакцияны ашқаннан кейін, ештеңе қалмаудан қорқып, ағымдағы деректерді кейбір жад құрылымына асығыс шығару арқылы қолданбаның жад ізін арттырудың қажеті жоқ. Бұл мүмкіндік LMDB-ді бірдей SQLite-тен ерекшелендіреді, ол мұндай толық оқшауланумен мақтана алмайды. Соңғысында екі транзакцияны ашып, олардың біреуіндегі белгілі бір жазбаны жойғаннан кейін, қалған екіншісінде бірдей жазбаны алу мүмкін болмайды.

Монетаның екінші жағы - виртуалды жадты тұтынудың айтарлықтай жоғары болуы. Слайд дерекқордың әртүрлі нұсқаларын қарайтын 3 ашық оқу транзакциясымен бір уақытта өзгертілсе, дерекқор құрылымы қандай болатынын көрсетеді. LMDB ағымдағы транзакциялармен байланысты түбірлерден қол жеткізуге болатын түйіндерді қайта пайдалана алмайтындықтан, дүкенде жадтағы тағы бір төртінші түбірді бөлуден және оның астындағы өзгертілген беттерді қайтадан клондаудан басқа амалы жоқ.

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігі

Бұл жерде жадпен салыстырылған файлдар бөлімін еске түсіру пайдалы болар еді. Виртуалды жадты қосымша тұтыну бізді алаңдатпауы керек сияқты, өйткені бұл қолданбаның жад ізіне ықпал етпейді. Дегенмен, сонымен бірге iOS-тың оны бөлуде өте сараң екендігі атап өтілді, және біз серверде немесе жұмыс үстелінде сияқты LMDB аймағын 1 терабайтпен қамтамасыз ете алмаймыз және бұл мүмкіндік туралы мүлде ойламаймыз. Мүмкін болса, транзакциялардың қызмет ету мерзімін мүмкіндігінше қысқа етуге тырысу керек.

4. API кілт-мәнінің жоғарғы жағындағы деректер схемасын құрастыру

LMDB ұсынған негізгі абстракцияларды қарастыру арқылы API талдауымызды бастайық: орта және дерекқорлар, кілттер мен мәндер, транзакциялар және курсорлар.

Код тізімдері туралы ескертпе

Жалпыға қолжетімді LMDB API интерфейсіндегі барлық функциялар өз жұмысының нәтижесін қате коды түрінде қайтарады, бірақ кейінгі барлық тізімдерде қысқаша болу үшін оны тексеру жүргізілмейді.​ Іс жүзінде біз репозиториймен әрекеттесу үшін тіпті өз функциямызды қолдандық. шанышқы C++ орауыштары lmdbxx, онда қателер C++ ерекше жағдайлары ретінде жүзеге асырылады.

LMDB жүйесін iOS немесе macOS жүйесіне арналған жобаға қосудың ең жылдам жолы ретінде мен CocoaPod ұсынамын. POSLMDB.

4.1. Негізгі абстракциялар

Қоршаған орта

құрылым MDB_env LMDB ішкі күйінің репозиторийі болып табылады. Префиксті функциялар тобы mdb_env оның кейбір қасиеттерін конфигурациялауға мүмкіндік береді. Ең қарапайым жағдайда қозғалтқышты инициализациялау келесідей көрінеді.

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 қосымшасында біз тек екі параметрдің әдепкі мәндерін өзгерттік.

Біріншісі - сақтау файлы салыстырылатын виртуалды мекенжай кеңістігінің өлшемі. Өкінішке орай, тіпті бір құрылғыда нақты мән іске қосудан іске қосуға дейін айтарлықтай өзгеруі мүмкін. iOS-тың бұл мүмкіндігін ескеру үшін сақтаудың максималды көлемі динамикалық түрде таңдалады. Белгілі бір мәннен бастап, ол функцияға дейін рет-ретімен екі есе азаяды mdb_env_open басқа нәтижені қайтармайды ENOMEM. Теорияда керісінше жол бар - алдымен қозғалтқышқа ең аз жадты бөліңіз, содан кейін қателер алынған кезде, MDB_MAP_FULL, көбейтіңіз. Дегенмен, бұл әлдеқайда тікенді. Себебі, функцияның көмегімен жадты қайта бөлу процедурасы (remap). mdb_env_set_map_size қозғалтқыштан бұрын алынған барлық нысандарды (курсорлар, транзакциялар, кілттер және мәндер) жарамсыз етеді. Оқиғалардың бұл айналымын кодексте ескеру оның айтарлықтай күрделенуіне әкеледі. Дегенмен, виртуалды жад сіз үшін өте маңызды болса, бұл алда тұрған шанышқыны мұқият қарауға себеп болуы мүмкін. MDBX, мұнда жарияланған мүмкіндіктер арасында «автоматты түрде дерекқор өлшемін реттеу» бар.

Әдепкі мәні бізге сәйкес келмейтін екінші параметр жіп қауіпсіздігін қамтамасыз ету механикасын реттейді. Өкінішке орай, кем дегенде iOS 10-да ағынды жергілікті сақтауды қолдауда проблемалар бар. Осы себепті жоғарыдағы мысалда репозиторий жалаушамен ашылады MDB_NOTLS. Бұған қоса, бұл да қажет болды шанышқы C++ қаптамасы lmdbxxосы атрибутпен және ондағы айнымалыларды қиып алу.

Деректер базасы

Дерекқор - бұл жоғарыда талқылаған жеке B-ағаш данасы. Оның ашылуы транзакцияның ішінде орын алады, бұл бастапқыда сәл оғаш көрінуі мүмкін.

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

Шынында да, LMDB ішіндегі транзакция нақты дерекқор нысаны емес, сақтау нысаны болып табылады. Бұл концепция әртүрлі деректер қорларында орналасқан объектілерге атомдық операцияларды орындауға мүмкіндік береді. Теориялық тұрғыдан бұл әртүрлі деректер базасы түрінде кестелерді модельдеу мүмкіндігін ашады, бірақ бір уақытта мен төменде егжей-тегжейлі сипатталған басқа жолды ұстандым.

Кілттер мен мәндер

құрылым MDB_val кілт пен құндылық ұғымын модельдейді. Репозиторийде олардың семантикасы туралы түсінік жоқ. Ол үшін басқа нәрсе - бұл берілген өлшемдегі байт массиві. Ең үлкен кілт өлшемі - 512 байт.

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

Салыстырушының көмегімен дүкен кілттерді өсу ретімен сұрыптайды. Егер сіз оны өзіңіздікімен ауыстырмасаңыз, оларды лексикографиялық ретпен байт-байт бойынша сұрыптайтын әдепкі пайдаланылады.

Мәмілелер

Мәміле құрылымы егжей-тегжейлі сипатталған алдыңғы тарау, сондықтан мен олардың негізгі қасиеттерін қысқаша қайталаймын:

  1. Барлық негізгі қасиеттерді қолдайды ACID: атомдылық, консистенциясы, оқшаулануы және сенімділігі. Мен MDBX жүйесінде түзетілген macOS және iOS жүйелерінің ұзақ мерзімділігі бойынша қате бар екенін атап өтпеймін. Толығырақ олардан оқи аласыз README.
  2. Көп ағынды тарату тәсілі «бір жазушы/бірнеше оқырман» схемасымен сипатталған. Жазушылар бірін-бірі блоктайды, бірақ оқырмандарды блоктамайды. Оқырмандар жазушыларға да, бір-біріне де бөгет жасамайды.
  3. Кірістірілген транзакцияларды қолдау.
  4. Көп нұсқаны қолдау.

LMDB мультиверсиясы соншалықты жақсы, мен оны іс жүзінде көрсеткім келеді. Төмендегі кодтан әрбір транзакцияның барлық кейінгі өзгерістерден толығымен оқшауланған, ашылған кездегі ағымдағы дерекқор нұсқасымен жұмыс істейтінін көруге болады. Сақтауды инициализациялау және оған сынақ жазбасын қосу қызықты ештеңені білдірмейді, сондықтан бұл рәсімдер спойлер астында қалды.

Сынақ жазбасын қосу

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

Мен сізге SQLite бағдарламасымен бірдей трюкті қолданып көруді және не болатынын көруді ұсынамын.

Мультиверсия iOS әзірлеушісінің өміріне өте жақсы жеңілдіктер әкеледі. Бұл сипатты пайдалану арқылы пайдаланушы тәжірибесінің ойларына негізделген экран пішіндері үшін деректер көзінің жаңарту жылдамдығын оңай және табиғи түрде реттей аласыз. Мысалы, Mail.ru Cloud қолданбасының жүйелік медиа галереясынан мазмұнды автоматты түрде жүктеу сияқты мүмкіндігін алайық. Жақсы қосылыммен клиент серверге секундына бірнеше фотосурет қоса алады. Әр жүктеп алғаннан кейін жаңартсаңыз UICollectionView пайдаланушының бұлтындағы мультимедиа мазмұнымен сіз осы процесс барысында 60 кадр/с және бірқалыпты айналдыруды ұмыта аласыз. Экранды жиі жаңартуға жол бермеу үшін негізгі деректердің өзгеру жылдамдығын қандай да бір жолмен шектеу керек UICollectionViewDataSource.

Егер дерекқор мультиверсияны қолдамаса және тек ағымдағы күймен жұмыс істеуге мүмкіндік берсе, онда деректердің уақыт бойынша тұрақты суретін жасау үшін оны кейбір жадтағы деректер құрылымына немесе уақытша кестеге көшіру керек. Бұл тәсілдердің кез келгені өте қымбат. Жадта сақтау жағдайында біз жадта салынған объектілерді сақтаудан туындайтын шығындарды да, артық ORM түрлендірулерімен байланысты уақыт бойынша да аламыз. Уақытша үстелге келетін болсақ, бұл одан да қымбат рахат, тек тривиальды емес жағдайларда ғана мағынасы бар.

LMDB мультиверсиялық шешімі тұрақты деректер көзін сақтау мәселесін өте талғампаз түрде шешеді. Тек транзакцияны ашу жеткілікті - біз оны аяқтамайынша, деректер жиынтығының түзетілуіне кепілдік беріледі. Жаңарту жылдамдығының логикасы енді толықтай презентация қабатының қолында, маңызды ресурстарға қосымша шығынсыз.

Курсорлар

Меңзерлер B-ағашты өту арқылы кілт-мән жұптары бойынша реттелген қайталау механизмін қамтамасыз етеді. Оларсыз біз қазір жүгінетін деректер қорындағы кестелерді тиімді модельдеу мүмкін емес еді.

4.2. Кестені модельдеу

Кілттерді ретке келтіру қасиеті негізгі абстракциялардың үстіндегі кесте сияқты жоғары деңгейлі абстракцияны құруға мүмкіндік береді. Бұл процесті пайдаланушының барлық файлдары мен қалталары туралы ақпаратты кэштейтін бұлттық клиенттің негізгі кестесінің мысалын пайдаланып қарастырайық.

Кесте схемасы

Қалта ағашы бар кесте құрылымын бейімдеуге болатын жалпы сценарийлердің бірі берілген каталогта орналасқан барлық элементтерді таңдау болып табылады.Осы түрдегі тиімді сұраулар үшін деректерді ұйымдастырудың жақсы үлгісі болып табылады. Жақындық тізімі. Оны кілттер-мәнді жадының үстіне енгізу үшін файлдар мен қалталардың кілттерін ата-аналық каталогтағы мүшелігіне қарай топтастырылатындай етіп сұрыптау қажет. Сонымен қатар, каталогтың мазмұнын Windows пайдаланушысына таныс пішінде көрсету үшін (алдымен қалталар, содан кейін екеуі де алфавит бойынша сұрыпталған файлдар) кілтке сәйкес қосымша өрістерді қосу қажет.

Төмендегі суретте тапсырманың негізінде байт массиві түріндегі пернелердің көрінісі қалай көрінетіні көрсетілген. Бастапқы каталогтың идентификаторы (қызыл) бар байттар алдымен, содан кейін түрімен (жасыл) және атымен (көк) құйрықта орналасады.Әдепкі LMDB компараторы бойынша лексикографиялық тәртіпте сұрыпталады, олар тізімде реттелген. талап етілетін тәсіл. Бірдей қызыл префиксі бар пернелерді дәйекті түрде жылжыту бізге олардың байланысты мәндерін пайдаланушы интерфейсінде (оң жақта) кез келген қосымша өңдеуді қажет етпестен көрсету тәртібімен береді.

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігі

Кілттер мен мәндерді сериялау

Әлемде объектілерді сериялаудың көптеген әдістері ойлап табылған. Бізде жылдамдықтан басқа талап болмағандықтан, біз өзіміз үшін ең жылдамын таңдадық – Си тіл құрылымының данасы алып жатқан жадтың дампін.Осылайша, каталог элементінің кілтін келесі құрылыммен модельдеуге болады. NodeKey.

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

Сақтау NodeKey объектіде қажет сақтауда MDB_val деректер көрсеткішін құрылымның басының мекенжайына орналастырыңыз және олардың өлшемін функциямен есептеңіз sizeof.

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

Дерекқорды таңдау критерийлері туралы бірінші тарауда мен маңызды таңдау факторы ретінде CRUD операцияларындағы динамикалық бөлулерді азайту туралы айттым. Функция коды serialize дерекқорға жаңа жазбаларды енгізу кезінде LMDB жағдайында оларды толығымен болдырмауға болатынын көрсетеді. Серверден кіріс байт массиві алдымен стек құрылымдарына түрленеді, содан кейін олар жадқа тривиальды түрде төгіледі. LMDB ішінде динамикалық бөлулер де жоқ екенін ескере отырып, iOS стандарттары бойынша фантастикалық жағдайға қол жеткізуге болады - желіден дискіге дейінгі бүкіл жол бойындағы деректермен жұмыс істеу үшін тек стек жадын пайдаланыңыз!

Екілік компаратормен кілттерге тапсырыс беру

Кілттік реттік қатынас компаратор деп аталатын арнайы функция арқылы анықталады. Қозғалтқыш құрамындағы байттардың семантикасы туралы ештеңе білмейтіндіктен, әдепкі компаратордың байт-байт салыстыруын қолданып, пернелерді лексикографиялық тәртіпте орналастырудан басқа таңдауы жоқ. Оны құрылымдарды ұйымдастыру үшін пайдалану балтамен қырынуға ұқсайды. Дегенмен, қарапайым жағдайларда мен бұл әдісті қолайлы деп санаймын. Балама төменде сипатталған, бірақ бұл жерде мен осы жол бойында шашыраңқы бірнеше тырмаларды атап өтемін.

Есте сақтау керек бірінші нәрсе - қарапайым деректер түрлерінің жады көрінісі. Осылайша, барлық Apple құрылғыларында бүтін айнымалылар пішімде сақталады Кішкентай Эндиан. Бұл ең аз маңызды байт сол жақта болады және байт-байт салыстыру арқылы бүтін сандарды сұрыптау мүмкін болмайтынын білдіреді. Мысалы, мұны 0-ден 511-ге дейінгі сандар жиынымен орындау келесі нәтижені береді.

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

Бұл мәселені шешу үшін бүтін сандар байт-байт компараторы үшін қолайлы форматта кілтте сақталуы керек. Отбасының функциялары қажетті түрлендіруді жүзеге асыруға көмектеседі hton* (сондай-ақ htons мысалдағы екібайтты сандар үшін).

Бағдарламалауда жолдарды көрсету пішімі, өзіңіз білетіндей, біртұтас тарихы. Егер жолдардың семантикасы, сондай-ақ оларды жадта көрсету үшін қолданылатын кодтау әр таңбаға бір байттан көп болуы мүмкін екенін көрсетсе, әдепкі компараторды пайдалану идеясын дереу тастаған дұрыс.

Есте сақтау керек екінші нәрсе теңестіру принциптері құрылым өрісінің компиляторы. Олардың арқасында өрістер арасында жадта қоқыс мәндері бар байттар қалыптасуы мүмкін, бұл, әрине, байтты сұрыптауды бұзады. Қоқысты жою үшін теңестіру ережелерін есте сақтай отырып, өрістерді қатаң анықталған тәртіпте жариялау керек немесе құрылым декларациясында төлсипатты пайдалану керек. packed.

Сыртқы компараторы бар кілттерге тапсырыс беру

Негізгі салыстыру логикасы екілік компаратор үшін тым күрделі болуы мүмкін. Көптеген себептердің бірі - құрылымдарда техникалық өрістердің болуы. Мен олардың пайда болуын бізге бұрыннан таныс каталог элементінің кілтінің мысалы арқылы суреттеймін.

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

Қарапайымдылығына қарамастан, көп жағдайда ол тым көп жадты жұмсайды. Атауға арналған буфер 256 байт алады, дегенмен файлдар мен қалталардың атаулары орташа есеппен 20-30 таңбадан асады.

Жазбаның өлшемін оңтайландырудың стандартты әдістерінің бірі оны нақты өлшемге дейін «қию» болып табылады. Оның мәні мынада: барлық айнымалы ұзындықтағы өрістердің мазмұны құрылымның соңында буферде сақталады, ал олардың ұзындықтары бөлек айнымалыларда сақталады.​ Бұл тәсілге сәйкес кілт NodeKey келесідей түрлендіріледі.

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

Әрі қарай, сериялау кезінде деректер өлшемі көрсетілмейді sizeof бүкіл құрылым және барлық өрістердің өлшемі бекітілген ұзындық пен буфердің нақты пайдаланылған бөлігінің өлшемі болып табылады.

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

Рефакторинг нәтижесінде біз кілттер алатын кеңістікті айтарлықтай үнемдедік. Дегенмен, техникалық салаға байланысты nameLength, әдепкі екілік компаратор енді кілттерді салыстыру үшін жарамсыз. Егер біз оны өзімізбен алмастырмасақ, онда атау ұзындығы атаудың өзінен гөрі сұрыптауда басымдылық факторы болады.

LMDB әрбір дерекқордың өзінің кілтті салыстыру функциясына ие болуына мүмкіндік береді. Бұл функция арқылы орындалады mdb_set_compare ашар алдында қатаң түрде. Белгілі себептерге байланысты оны дерекқордың қызмет ету мерзімі ішінде өзгерту мүмкін емес. Салыстырушы кіріс ретінде екілік пішімдегі екі кілтті алады, ал шығысында салыстыру нәтижесін қайтарады: (-1) кіші, (1) үлкен немесе (0) тең. үшін псевдокод NodeKey солай көрінеді.

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

Дерекқордағы барлық кілттер бір типте болғанша, олардың байт көрінісін бағдарлама кілті құрылымының түріне сөзсіз трансляциялау заңды болып табылады. Мұнда бір нюанс бар, бірақ ол төменде «Оқу жазбалары» бөлімінде талқыланады.

Мәндерді сериялау

LMDB сақталған жазбалардың кілттерімен өте қарқынды жұмыс істейді. Оларды бір-бірімен салыстыру кез келген қолданбалы операция шеңберінде жүреді және бүкіл шешімнің өнімділігі компаратордың жылдамдығына байланысты. Идеалды әлемде кілттерді салыстыру үшін әдепкі екілік компаратор жеткілікті болуы керек, бірақ егер сіз өзіңізді пайдалануыңыз керек болса, ондағы кілттерді сериядан шығару процедурасы мүмкіндігінше жылдам болуы керек.

Мәліметтер қоры жазбаның (мәннің) мәндік бөлігіне ерекше қызығушылық танытпайды. Оны байт көрінісінен объектіге түрлендіру оны қолданбалы код талап еткенде ғана жүзеге асады, мысалы, оны экранда көрсету. Бұл салыстырмалы түрде сирек болатындықтан, бұл процедураның жылдамдық талаптары соншалықты маңызды емес және оны жүзеге асыруда біз ыңғайлылыққа назар аударуға әлдеқайда еркін боламыз.Мысалы, әлі жүктелмеген файлдар туралы метадеректерді сериялау үшін біз пайдаланамыз NSKeyedArchiver.

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

Дегенмен, өнімділік әлі де маңызды болатын кездер болады. Мысалы, пайдаланушы бұлтының файлдық құрылымы туралы метаақпаратты сақтау кезінде біз объектілердің бірдей жад демпін пайдаланамыз. Олардың серияланған көрінісін жасау тапсырмасының ерекшелігі каталог элементтерінің сыныптар иерархиясы арқылы модельдену фактісі болып табылады.

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігі

Оны Си тілінде жүзеге асыру үшін мұрагерлердің нақты өрістері бөлек құрылымдарға орналастырылады, ал олардың базалық өріспен байланысы типтік одақ өрісі арқылы көрсетіледі. Бірлестіктің нақты мазмұны техникалық атрибут түрі арқылы көрсетіледі.

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

Жазбаларды қосу және жаңарту

Сериялық кілт пен мәнді дүкенге қосуға болады. Мұны істеу үшін функцияны пайдаланыңыз mdb_put.

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

Конфигурациялау сатысында сақтау бір кілтпен бірнеше жазбаларды сақтауға рұқсат етілуі немесе тыйым салынуы мүмкін.Егер кілттердің қайталануына тыйым салынса, онда жазбаны енгізу кезінде бар жазбаны жаңартуға рұқсат етілгенін немесе жоқтығын анықтауға болады. Егер тозу кодтағы қатенің нәтижесінде ғана орын алса, жалаушаны көрсету арқылы өзіңізді одан қорғай аласыз. NOOVERWRITE.

Жазбаларды оқу

LMDB ішіндегі жазбаларды оқу үшін функцияны пайдаланыңыз mdb_get. Егер кілт-мән жұбы бұрын демпингтік құрылымдармен ұсынылса, онда бұл процедура келесідей көрінеді.

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

Ұсынылған листинг құрылымды дамп арқылы сериялау тек жазу кезінде ғана емес, деректерді оқу кезінде де динамикалық бөлулерден құтылуға мүмкіндік беретінін көрсетеді. Функциядан алынған mdb_get көрсеткіш дерекқор нысанның байт көрінісін сақтайтын виртуалды жад мекенжайына дәл қарайды. Шын мәнінде, біз өте жоғары деректерді оқу жылдамдығын қамтамасыз ететін ORM түрін тегін дерлік аламыз. Тәсілдің барлық сұлулығына қарамастан, онымен байланысты бірнеше ерекшеліктерді есте сақтау қажет.

  1. Тек оқуға арналған транзакция үшін мән құрылымының көрсеткіші транзакция жабылғанша ғана жарамды болып қалуына кепілдік беріледі. Жоғарыда айтылғандай, нысан орналасқан В-ағаш беттері, жазуға көшіру принципінің арқасында, кем дегенде бір транзакцияға сілтеме жасалғанша өзгеріссіз қалады. Сонымен қатар, олармен байланысты соңғы транзакция аяқталғаннан кейін беттерді жаңа деректер үшін қайта пайдалануға болады. Объектілердің оларды жасаған транзакциядан аман қалуы қажет болса, оларды әлі де көшіру керек.
  2. Қайта жазу транзакциясы үшін нәтижелік мән құрылымына көрсеткіш тек бірінші өзгерту процедурасына дейін жарамды болады (деректерді жазу немесе жою).
  3. Құрылым болса да NodeValue толық емес, бірақ кесілген («Сыртқы компаратор арқылы кілттерге тапсырыс беру» тармақшасын қараңыз), оның өрістеріне көрсеткіш арқылы қауіпсіз қол жеткізуге болады. Ең бастысы, оны назардан тыс қалдырмаңыз!
  4. Ешбір жағдайда құрылымды қабылданған көрсеткіш арқылы өзгертуге болмайды. Барлық өзгертулер тек әдіс арқылы жасалуы керек mdb_put. Дегенмен, мұны қаншалықты орындағыңыз келсе де, бұл мүмкін емес, өйткені бұл құрылым орналасқан жад аймағы тек оқуға арналған режимде салыстырылады.
  5. Функцияны пайдаланып, мысалы, ең үлкен сақтау көлемін ұлғайту мақсатында файлды процестің мекенжай кеңістігіне қайта салыңыз mdb_env_set_map_size барлық транзакцияларды және тұтастай байланысты субъектілерді толығымен жарамсыз етеді және атап айтқанда белгілі бір объектілерге нұсқайды.

Ақырында, тағы бір ерекшелік соншалық, оның мәнін ашу басқа абзацқа сыймайды. В-ағаш туралы тарауда мен оның беттерінің жадта қалай орналасатыны туралы диаграмма бердім. Осыдан серияланған деректері бар буфер басының адресі абсолютті ерікті болуы мүмкін екендігі шығады. Осыған байланысты құрылымда оларға көрсеткіш алынды MDB_val және құрылымға көрсеткішке дейін қысқартылған, ол жалпы жағдайда теңестірілмеген болып шығады. Сонымен қатар, кейбір чиптердің архитектурасы (iOS жағдайында бұл armv7) кез келген деректердің мекенжайы машина сөзінің өлшеміне немесе басқаша айтқанда, жүйенің бит өлшеміне еселік болуын талап етеді ( armv7 үшін бұл 32 бит). Басқаша айтқанда, операция сияқты *(int *foo)0x800002 олар бойынша қашып кетуге тең және үкіммен орындауға әкеледі EXC_ARM_DA_ALIGN. Мұндай қайғылы тағдырдан құтылудың екі жолы бар.

Біріншісі деректерді анық тураланған құрылымға алдын ала көшіруге дейін төмендейді. Мысалы, теңшелетін компараторда бұл келесідей көрсетіледі.

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

Балама әдіс - компиляторға кілт-мән құрылымдарының төлсипат бойынша тураланбауы мүмкін екендігі туралы алдын ала хабарлау. aligned(1). ARM-де сіз бірдей әсер ете аласыз жету және пакеттелген төлсипатты пайдалану. Бұл құрылым алып жатқан кеңістікті оңтайландыруға көмектесетінін ескере отырып, бұл әдіс маған жақсырақ болып көрінеді, дегенмен приводит деректерге қол жеткізу операцияларының құнының артуына.

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

Ауқымды сұраулар

Жазбалар тобын қайталау үшін LMDB курсор абстракциясын береді. Бізге бұрыннан таныс пайдаланушы бұлтының метадеректері бар кестенің мысалын пайдаланып, онымен қалай жұмыс істеу керектігін қарастырайық.

Каталогтағы файлдар тізімін көрсету бөлігі ретінде оның еншілес файлдары мен қалталары байланыстырылған барлық кілттерді табу қажет. Алдыңғы бөлімдерде біз кілттерді сұрыптадық NodeKey олар ең алдымен басты каталогтың идентификаторы бойынша реттелген. Осылайша, техникалық тұрғыдан қалтаның мазмұнын шығарып алу міндеті курсорды берілген префиксі бар пернелер тобының жоғарғы шекарасына қоюға, содан кейін төменгі шекараға қайталануға келеді.

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігі

Жоғарғы шекараны дәйекті іздеу арқылы тікелей табуға болады. Ол үшін курсор дерекқордағы барлық кілттер тізімінің басына қойылады және оның астында негізгі каталогтың идентификаторы бар кілт пайда болғанша одан әрі ұлғайтылады. Бұл тәсілдің 2 айқын кемшілігі бар:

  1. Сызықтық іздеудің күрделілігі, белгілі болғандай, жалпы ағаштарда және әсіресе В-ағашында оны логарифмдік уақытта жүзеге асыруға болады.
  2. Бекер, ізделетін беттердің алдындағы барлық беттер файлдан негізгі жадқа көтеріледі, бұл өте қымбат.

Бақытымызға орай, LMDB API курсорды бастапқы орналастырудың тиімді әдісін қамтамасыз етеді.Ол үшін мәні интервалдың жоғарғы шекарасында орналасқан кілттен аз немесе оған тең болатын кілтті жасау керек. Мысалы, жоғарыдағы суреттегі тізімге қатысты біз өрістің кілтін жасай аламыз parentId 2-ге тең болады, ал қалғандарының барлығы нөлмен толтырылады. Мұндай жартылай толтырылған кілт функция кірісіне беріледі mdb_cursor_get операцияны көрсетеді 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);

Егер пернелер тобының жоғарғы шекарасы табылса, біз оны кездестірмейінше немесе кілт басқасымен кездескенше қайталаймыз. parentId, немесе кілттер мүлдем бітпейді

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

Бір қызығы, mdb_cursor_get көмегімен итерацияның бөлігі ретінде біз тек кілтті ғана емес, мәнді де аламыз. Егер іріктеу шарттарын орындау үшін, басқалармен қатар, жазбаның мән бөлігінен өрістерді тексеру қажет болса, онда олар қосымша қимылдарсыз қол жетімді.

4.3. Кестелер арасындағы байланыстарды модельдеу

Осы уақытқа дейін біз бір кестелік деректер қорын жобалау мен жұмыс істеудің барлық аспектілерін қарастыра алдық. Кестені кілт-мән жұптарының бірдей типінен тұратын сұрыпталған жазбалар жиыны деп айта аламыз. Егер кілтті тіктөртбұрыш ретінде және байланысты мәнді параллелепипед ретінде көрсетсеңіз, дерекқордың көрнекі диаграммасын аласыз.

مور

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігі

Дегенмен, шынайы өмірде соншалықты аз қантөгіспен өмір сүру сирек. Көбінесе деректер қорында біріншіден, бірнеше кестелер болуы керек, екіншіден, оларда бастапқы кілттен өзгеше тәртіпте таңдаулар жасау қажет. Бұл соңғы бөлім оларды құру және өзара байланыстыру мәселелеріне арналған.

Индекс кестелері

Бұлтты қолданбада «Галерея» бөлімі бар. Ол күні бойынша сұрыпталған бүкіл бұлттағы медиа мазмұнын көрсетеді. Мұндай таңдауды оңтайлы жүзеге асыру үшін негізгі кестенің жанында кілттердің жаңа түрімен басқасын жасау керек. Оларда файлдың жасалған күні бар өріс болады, ол негізгі сұрыптау шарты ретінде әрекет етеді. Жаңа кілттер негізгі кестедегі кілттермен бірдей деректерге сілтеме жасайтындықтан, олар индекстік кілттер деп аталады. Төмендегі суретте олар қызғылт сары түспен бөлінген.

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігі

Бір дерекқорда әртүрлі кестелердің кілттерін бір-бірінен бөлу үшін олардың барлығына қосымша техникалық өріс tableId қосылды. Оны сұрыптаудың ең жоғары басымдылығына айналдыра отырып, біз кілттерді алдымен кестелер бойынша, ал кестелер ішінде - өз ережелерімізге сәйкес топтауға қол жеткіземіз.

Индекс кілті бастапқы кілт сияқты бірдей деректерге сілтеме жасайды. Бұл сипатты онымен бастапқы кілттің мән бөлігінің көшірмесін байланыстыру арқылы тікелей жүзеге асыру бірнеше көзқарас бойынша оңтайлы емес:

  1. Алынған кеңістік тұрғысынан метадеректер айтарлықтай бай болуы мүмкін.
  2. Өнімділік тұрғысынан түйіннің метадеректерін жаңартқан кезде оны екі кілт арқылы қайта жазуға тура келеді.
  3. Кодты қолдау тұрғысынан, егер біз кілттердің біреуі үшін деректерді жаңартуды ұмытып қалсақ, біз жадтағы деректер сәйкессіздігінің қиын қатесін аламыз.

Әрі қарай, біз бұл кемшіліктерді қалай жоюға болатынын қарастырамыз.

Кестелер арасындағы байланысты ұйымдастыру

Үлгі индекстік кестені негізгі кестемен байланыстыру үшін өте қолайлы «мән ретінде кілт». Оның аты айтып тұрғандай, индекс жазбасының мән бөлігі бастапқы кілт мәнінің көшірмесі болып табылады. Бұл тәсіл бастапқы жазбаның мән бөлігінің көшірмесін сақтауға байланысты жоғарыда аталған барлық кемшіліктерді жояды. Жалғыз құн - индекс кілті бойынша мәнді алу үшін дерекқорға бір сұраудың орнына 2 сұрау жасау керек. Схемалық түрде алынған дерекқор схемасы келесідей көрінеді.

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігі

Кестелер арасындағы қатынастарды ұйымдастырудың тағы бір үлгісі «артық кілт». Оның мәні кілтке қосымша атрибуттарды қосу болып табылады, олар сұрыптау үшін емес, байланыстырылған кілтті қайта жасау үшін қажет.. Mail.ru Cloud қосымшасында оны пайдаланудың нақты мысалдары бар, дегенмен терең сүңгуге жол бермеу үшін нақты iOS құрылымдарының контекстінде мен жалған, бірақ айқынырақ мысал келтіремін.​

Бұлтты мобильді клиенттерде пайдаланушы басқа адамдармен бөліскен барлық файлдар мен қалталарды көрсететін бет бар. Мұндай файлдар салыстырмалы түрде аз болғандықтан және олармен байланысты жариялылық туралы нақты ақпараттың әртүрлі түрлері көп болғандықтан (кімге рұқсат берілген, қандай құқықтармен және т. онымен негізгі кестеге жазыңыз. Дегенмен, мұндай файлдарды офлайн режимінде көрсеткіңіз келсе, оны әлі де бір жерде сақтау керек. Табиғи шешім - ол үшін бөлек кесте жасау. Төмендегі диаграммада оның кілтіне «P» префиксі қойылған және «пропname» толтырғышын «жалпыға ортақ ақпарат» нақты мәніне ауыстыруға болады.

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігі

Жаңа кесте жасалған сақтау үшін барлық бірегей метадеректер жазбаның мән бөлігінде орналастырылады. Сонымен қатар, сіз негізгі кестеде бұрыннан сақталған файлдар мен қалталар туралы деректерді қайталағыңыз келмейді. Оның орнына артық деректер «P» пернесіне «түйін идентификаторы» және «уақыт белгісі» өрістері түрінде қосылады. Олардың арқасында сіз индекстік кілтті құра аласыз, одан бастапқы кілтті алуға болады, одан түйіннің метадеректерін алуға болады.

Қорытынды

Біз LMDB енгізу нәтижелерін оң бағалаймыз. Одан кейін қолданбаларды қатыру саны 30%-ға азайды.

iOS қолданбаларындағы LMDB кілт-мән дерекқорының жарқырауы мен кедейлігі

Жасалған жұмыстардың нәтижелері iOS командасынан тыс резонанс тудырды. Қазіргі уақытта Android қосымшасындағы негізгі «Файлдар» бөлімдерінің бірі де LMDB пайдалануға ауысты, ал басқа бөліктер жолда. Кілттер қоймасы жүзеге асырылатын Си тілі C++ тілінде кросс-платформаның айналасында қолданбалы жүйені құруға жақсы көмек болды. Алынған C++ кітапханасын Objective-C және Kotlin платформалық кодымен үздіксіз қосу үшін код генераторы пайдаланылды. Джинни Dropbox-тан, бірақ бұл мүлдем басқа оқиға.

Ақпарат көзі: www.habr.com

пікір қалдыру