iOS тиркемелериндеги LMDB ачкыч-маанилер базасынын жаркырап жана жакырдыгы

iOS тиркемелериндеги LMDB ачкыч-маанилер базасынын жаркырап жана жакырдыгы

2019-жылдын күзүндө Mail.ru Cloud iOS командасында көптөн күткөн окуя болду. Колдонмонун абалын туруктуу сактоо үчүн негизги маалымат базасы мобилдик дүйнө үчүн абдан экзотикалык болуп калды Lightning Memory-карталанган маалымат базасы (LMDB). Кесилген ылдыйда биз сизге аны төрт бөлүктө кылдат карап чыгууну сунуштайбыз. Биринчиден, мындай тривиалдуу эмес жана татаал тандоонун себептери жөнүндө сүйлөшөлү. Андан кийин биз LMDB архитектурасынын өзөгүндө турган үч мамычаны карап чыгабыз: эс тутумга түшүрүлгөн файлдар, B+-дарагы, транзакцияны жана мультиверсияны ишке ашыруу үчүн көчүрүү-жазуу ыкмасы. Акыр-аягы, десерт үчүн - практикалык бөлүгү. Анда биз төмөнкү деңгээлдеги ачкыч-баалуу API'нин үстүнө бир нече таблицалар, анын ичинде индекси бар маалымат базасынын схемасын кантип иштеп чыгууну жана ишке ашырууну карап чыгабыз.

ыраазы

  1. Ишке ашыруу үчүн мотивация
  2. LMDB Positioning
  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 жана Habré боюнча макала. Учурдагы баяндын бир бөлүгү катары, мен маалымат базасын тандоого таасир эткен чечимдин аспектилерин баса белгилегим келет.

Системаны уюштуруунун актер модели көп агым анын экинчи маңызы болуп калат деп болжолдойт. Андагы моделдик объекттер агымдын чектерин кесип өткөндү жакшы көрүшөт. Жана алар муну кээде жана бул жерде эмес, дээрлик дайыма жана бардык жерде жасашат

iOS тиркемелериндеги LMDB ачкыч-маанилер базасынын жаркырап жана жакырдыгы

Берилген диаграммада маалымат базасы негизги компоненттердин бири болуп саналат. Анын негизги милдети макропаттернди ишке ашыруу болуп саналат Бөлүшүлгөн маалымат базасы. Эгерде ишкана дүйнөсүндө бул кызматтардын ортосунда маалыматтарды синхрондоштурууну уюштуруу үчүн колдонулса, анда актер архитектурасында - жиптер ортосундагы маалыматтар. Ошентип, бизге көп жиптүү чөйрөдө аны менен иштөөдө минималдуу кыйынчылыктарды жаратпай турган маалымат базасы керек болчу. Атап айтканда, бул андан алынган объекттер, жок эле дегенде, жип коопсуз болушу керек дегенди билдирет, жана идеалдуу толугу менен өзгөрүлбөс. Белгилүү болгондой, акыркы бир эле учурда бир нече жиптен эч кандай кулпуга кайрылбастан колдонулушу мүмкүн, бул аткарууга жакшы таасирин тийгизет.

iOS тиркемелериндеги LMDB ачкыч-маанилер базасынын жаркырап жана жакырдыгыМаалыматтар базасын тандоого таасир эткен экинчи маанилүү фактор биздин булут API болду. Ал git тарабынан кабыл алынган синхрондоштуруу ыкмасынан шыктанган. Ага окшоп, биз максат койгонбуз оффлайн-биринчи API, бул булут кардарлары үчүн ылайыктуураак көрүнөт. Алар булуттун толук абалын бир гана жолу чыгарып, андан кийин көпчүлүк учурларда синхрондоштуруу өзгөрүүлөрдү жайылтуу аркылуу ишке ашат деп болжолдонгон. Тилекке каршы, бул мүмкүнчүлүк дагы эле теориялык зонада гана, ал эми кардарлар тактар ​​менен иштөөнү практика жүзүндө үйрөнө элек. Мунун бир катар объективдүү себептери бар, аларды киргизүүнү кечеңдетпөө үчүн биз кашааларды калтырабыз. Эми, API "A" деп айтса, ал эми анын керектөөчүсү "B" деп айтпаганда, эмне болору жөнүндө сабактын көрсөтмөлүү корутундулары көбүрөөк кызыктырат.

Ошентип, эгерде сиз гитти элестетсеңиз, ал тартуу буйругун аткарып жатканда, жергиликтүү сүрөткө патчтарды колдонуунун ордуна, анын толук абалын сервердин толук абалы менен салыштырса, анда булутта синхрондоштуруунун кандайча болору жөнүндө так түшүнүккө ээ болосуз. кардарлар. Аны ишке ашыруу үчүн эстутумда бардык сервер жана жергиликтүү файлдар жөнүндө мета-маалымат менен эки DOM дарагын бөлүштүрүү керек экенин болжолдоо оңой. Көрсө, эгер колдонуучу булутта 500 миң файлды сактаса, анда аны синхрондоштуруу үчүн 1 миллион түйүндүү эки даракты кайра жаратып, жок кылуу керек экен. Бирок ар бир түйүн субобъекттердин графигин камтыган агрегат болуп саналат. Бул жагынан алганда, профилдик натыйжалар күтүлгөн. Көрсө, бириктирүү алгоритмин эске албаганда да, эбегейсиз көп сандагы майда объекттерди түзүү жана андан ары жок кылуу процедурасынын өзү бир тыйынды талап кылат экен.Агдалды синхрондоштуруунун негизги операциясы көп санда камтылгандыгы менен курчутат. колдонуучу скрипттеринин. Натыйжада, биз маалымат базасын тандоодо экинчи маанилүү критерийди бекитебиз - объекттерди динамикалык бөлүштүрүүсүз CRUD операцияларын ишке ашыруу мүмкүнчүлүгү.

Башка талаптар салттуу болуп саналат жана алардын бүт тизмеси төмөнкүдөй.

  1. Жиптин коопсуздугу.
  2. Көп иштетүү. Бир эле маалымат базасынын инстанциясын жиптер арасында гана эмес, ошондой эле негизги тиркеме менен iOS кеңейтүүлөрүнүн ортосунда синхрондоштуруу үчүн колдонуу каалоосу менен шартталган.
  3. Сакталган объекттерди өзгөрүлбөгөн объекттер катары көрсөтүү мүмкүнчүлүгү
  4. CRUD операцияларында динамикалык бөлүштүрүү жок.
  5. Негизги касиеттерге транзакцияны колдоо КЫЧКЫЛ: атомдуулук, ырааттуулук, изоляция жана ишенимдүүлүк.
  6. Эң популярдуу учурларда ылдамдык.

Бул талаптардын топтому менен SQLite жакшы тандоо болгон жана кала берет. Бирок, альтернативаларды изилдөөнүн алкагында мен бир китепке туш болдум "LevelDB менен баштоо". Анын жетекчилиги астында реалдуу булут сценарийлериндеги ар кандай маалымат базалары менен иштөө ылдамдыгын салыштырган эталон жазылган. Натыйжа биздин күткөнүбүздөн ашып түштү. Эң популярдуу учурларда - бардык файлдардын иреттелген тизмесине курсорду алуу жана берилген каталог үчүн бардык файлдардын сорттолгон тизмесин алуу - LMDB SQLiteге караганда 10 эсе тезирээк болуп чыкты. Тандоо айкын болуп калды.

iOS тиркемелериндеги LMDB ачкыч-маанилер базасынын жаркырап жана жакырдыгы

2. LMDB Positioning

LMDB маалымат базаларынын эң төмөнкү фундаменталдык катмарын - сактоону ишке ашырган өтө кичинекей китепкана (болгону 10 миң сап).

iOS тиркемелериндеги LMDB ачкыч-маанилер базасынын жаркырап жана жакырдыгы

Жогорудагы диаграмма LMDBди SQLite менен салыштыруу көрсөтүп турат, ал дагы жогорку деңгээлдерди ишке ашырат, негизинен негизги маалыматтары бар SQLiteге караганда туура эмес. Бир эле сактагыч кыймылдаткычтарды бирдей атаандаштар катары келтирсек адилеттүү болмок - BerkeleyDB, LevelDB, Sophia, RocksDB, ж.б. Мындай биринчи эксперимент 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ди өзүнүн үйдө өстүрүлгөн маалыматтарды кэштөө алкагы үчүн демейки сактагыч катары тандады. айтып берди 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. Кэштердин жоктугу, ошондой эле алардын кирүү мүмкүнчүлүгүн синхрондоштуруу менен байланышкан кулпулардын жоктугун билдирет. Окурмандар, алардын ичинен бир эле учурда ыктыярдуу сандагы окурмандар болушу мүмкүн, маалыматтарга бара жаткан жолдо бир да мутекске жолукпайт. Ушундан улам, окуу ылдамдыгы CPU санына негизделген идеалдуу сызыктуу масштабга ээ. LMDBде өзгөртүү операциялары гана шайкештештирилет. Бир эле учурда бир гана жазуучу болушу мүмкүн.
  4. Минималдуу кэштөө жана синхрондоштуруу логикасы көп агымдуу чөйрөдө иштөө менен байланышкан каталардын өтө татаал түрүн жок кылат. Usenix OSDI 2014 конференциясында эки кызыктуу маалымат базасы изилдөөлөр болду: "Бардык файл тутумдары бирдей түзүлгөн эмес: Crash-ырааттуу тиркемелерди түзүүнүн татаалдыгы жөнүндө" и "Маалымат базаларын көңүл ачуу жана пайда үчүн кыйноо". Алардан сиз LMDBдин болуп көрбөгөндөй ишенимдүүлүгү жана SQLiteден жогору турган ACID транзакциясынын касиеттерин дээрлик кемчиликсиз ишке ашыруу жөнүндө маалымат ала аласыз.
  5. LMDBнин минимализми анын кодунун машиналык өкүлчүлүгүн процессордун L1 кэшинде кийинки ылдамдык мүнөздөмөлөрү менен толугу менен жайгаштырууга мүмкүндүк берет.

Тилекке каршы, iOS'то файлдарды эс тутумга түшүргөндө, баары биз каалагандай булутсуз эмес. Алар менен байланышкан кемчиликтер жөнүндө көбүрөөк аң-сезимдүү түрдө айтуу үчүн, бул механизмди операциялык системаларда ишке ашыруунун жалпы принциптерин эстен чыгарбоо керек.

Эстутумга түшүрүлгөн файлдар жөнүндө жалпы маалымат

iOS тиркемелериндеги LMDB ачкыч-маанилер базасынын жаркырап жана жакырдыгыИштеп жаткан ар бир тиркеме менен операциялык система процесс деп аталган объектти байланыштырат. Ар бир процесске даректердин чектеш диапазону бөлүнөт, анда ал иштөө үчүн керектүү нерселердин бардыгын жайгаштырат. Эң төмөнкү даректерде код жана катуу коддолгон маалыматтар жана ресурстар бар бөлүмдөр бар. Андан кийин бизге үймөк деген ат менен белгилүү болгон динамикалык дарек мейкиндигинин өсүп жаткан блогу келет. Ал программанын иштешинде пайда болгон объекттердин даректерин камтыйт. Жогору жагында тиркеме стеки колдонгон эс тутум аймагы жайгашкан. Ал же өсөт, же кыскарат, башкача айтканда, анын көлөмү да динамикалык мүнөзгө ээ. Стек жана үймөк бири-бирине түртүп, тоскоолдук кылбашы үчүн, алар дарек мейкиндигинин ар кайсы учунда жайгашкан.​ Үстүндө жана ылдый жагында эки динамикалык бөлүмдүн ортосунда тешик бар. Иштөө системасы процесс менен ар кандай объекттерди байланыштыруу үчүн бул ортоңку бөлүмдөгү даректерди колдонот. Атап айтканда, ал даректердин белгилүү бир үзгүлтүксүз топтомун дисктеги файл менен байланыштыра алат. Мындай файл эс тутум картасы деп аталат

Процесске бөлүнгөн дарек мейкиндиги абдан чоң. Теориялык жактан алганда, даректердин саны көрсөткүчтүн көлөмү менен гана чектелет, ал системанын бит сыйымдуулугу менен аныкталат. Эгерде физикалык эстутум аны 1ден 1ге салыштырса, анда эң биринчи процесс бүт оперативдик эстутумду жеп, көп тапшырма жөнүндө сөз болбойт.

Бирок, биздин тажрыйбабыздан биз заманбап операциялык системалар бир эле учурда каалагандай көптөгөн процесстерди аткара аларын билебиз. Бул алар кагаздагы процесстерге көп эстутумду гана бөлгөндүктөн мүмкүн, бирок чындыгында алар негизги физикалык эстутумга ушул жерде жана азыр талап кылынган бөлүгүн гана жүктөшөт. Демек, процесс менен байланышкан эс виртуалдык деп аталат.

iOS тиркемелериндеги LMDB ачкыч-маанилер базасынын жаркырап жана жакырдыгы

Операциялык система виртуалдык жана физикалык эстутумду белгилүү өлчөмдөгү баракчаларга уюштурат. Виртуалдык эс тутумдун белгилүү бир барагы суроо-талапка ээ болоору менен операциялык система аны физикалык эс тутумга жүктөйт жана аларды атайын таблицага дал келтирет. Эгерде бош орундар жок болсо, анда мурда жүктөлгөн барактардын бири дискке көчүрүлүп, анын ордун талап кылынган барак ээлейт. Бир аздан кийин кайта турган бул процедура алмашуу деп аталат. Төмөндөгү сүрөт сүрөттөлгөн процессти көрсөтөт. Анда 0 дареги бар А барагы жүктөлүп, 4 дареги бар эстутумдун негизги бетине жайгаштырылды. Бул факт 0-уячадагы корреспонденциялар таблицасында чагылдырылган.

iOS тиркемелериндеги LMDB ачкыч-маанилер базасынын жаркырап жана жакырдыгы

Окуя эстутумга түшүрүлгөн файлдар менен дал ошондой. Логикалык жактан алар тынымсыз жана толугу менен виртуалдык дарек мейкиндигинде жайгашкан имиш. Бирок, алар физикалык эстутум баракчасына барактан жана өтүнүч боюнча гана кирет. Мындай барактарды өзгөртүү дисктеги файл менен синхрондолот. Ушундай жол менен, сиз жөн гана эс тутумдагы байттар менен иштөө менен файлды I/O аткара аласыз - бардык өзгөртүүлөр операциялык системанын өзөгү тарабынан автоматтык түрдө баштапкы файлга өткөрүлүп берилет.

Төмөнкү сүрөттө LMDB ар кандай процесстердин маалымат базасы менен иштөөдө анын абалын кантип синхрондоштурууну көрсөтөт. Ар түрдүү процесстердин виртуалдык эс тутумун бир файлга түшүрүү менен, биз иш жүзүндө операциялык системаны LMDB карап турган жерде алардын дарек мейкиндиктеринин айрым блокторун бири-бири менен өтмө синхрондоштурууга милдеттендиребиз.

iOS тиркемелериндеги LMDB ачкыч-маанилер базасынын жаркырап жана жакырдыгы

Маанилүү нюанс, LMDB демейки боюнча, жазуу тутумунун чакыруу механизми аркылуу маалымат файлын өзгөртөт жана файлдын өзүн окуу үчүн гана режиминде көрсөтөт. Бул ыкманын эки маанилүү натыйжасы бар.

Биринчи натыйжа бардык операциялык системалар үчүн жалпы болуп саналат. Анын маңызы туура эмес код менен маалымат базасына атайылап зыян келтирүүдөн коргоону кошуу болуп саналат. Белгилүү болгондой, процесстин аткарылуучу инструкциялары анын дарек мейкиндигинин каалаган жеринен маалыматтарга кирүү үчүн акысыз. Ошол эле учурда, биз жаңы эле эстегендей, файлды окуу-жазуу режиминде көрсөтүү каалаган инструкция аны өзгөртө алат дегенди билдирет. Эгерде ал муну жаңылыштык менен жасаса, мисалы, жок индексте массивдин элементин чындыгында үстүнө жазууга аракет кылса, анда ал кокусунан бул дарекке коюлган файлды өзгөртүшү мүмкүн, бул маалымат базасынын бузулушуна алып келет. Эгерде файл окуу үчүн гана режимде көрсөтүлсө, анда тиешелүү дарек мейкиндигин өзгөртүү аракети программаны сигнал менен чукул токтотууга алып келет. SIGSEGV, жана файл бүтүн бойдон калат.

Экинчи натыйжа iOS үчүн мурунтан эле белгилүү. Бул тууралуу автор да, башка булактар ​​да ачык айтпайт, бирок ансыз LMDB бул мобилдик операциялык тутумда иштөөгө жарабайт. Кийинки бөлүм аны карап чыгууга арналган.

iOS тутумунда эстутумга түшүрүлгөн файлдардын өзгөчөлүктөрү

2018-жылы WWDCде эң сонун отчет болгон "iOS Memory Deep Dive". Бул 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'та свопту кысуу менен алмаштыруу теориялык максимумду түп-тамырынан азайтат. Эми бардык тирүү процесстер негизги (окуу RAM) эс тутумуна туура келиши керек, ал эми туура келбегендердин бардыгын токтотууга мажбур болушу керек. Бул жогоруда айтылгандай айтылган билдирүүжана расмий документтер. Натыйжада, 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+ вариациясы эң төмөнкүдө гана болот. Бул фактыны аныктагандан кийин, биз мындан ары LMDBде колдонулган дарактын түрүн жөн гана B-дарагы деп атайбыз.
  3. Тамыр менен жалбырактардын ортосунда навигациялык (тармактык) түйүндөрү бар 0 же андан көп техникалык деңгээлдер бар. Алардын милдети жалбырактардын ортосундагы баскычтардын сорттолгон топтомун бөлүштүрүү болуп саналат.

Физикалык жактан алганда, түйүндөр - алдын ала белгиленген узундуктагы эс тутум блоктору. Алардын көлөмү биз жогоруда талкууланган операциялык тутумдагы эстутум барактарынын өлчөмүнөн эсе көп. Түйүн структурасы төмөндө көрсөтүлгөн. Баш маалымат мета маалыматты камтыйт, алардын эң айкыны, мисалы, текшерүү суммасы. Андан кийин маалыматтары бар уячалар жайгашкан офсеттер жөнүндө маалымат келет. Маалыматтар же багыттоо түйүндөрү жөнүндө сөз болуп жаткан болсо, ачкычтар, же жалбырактардын учурда бүтүндөй ачкыч-маани түгөйлөрү болушу мүмкүн. "Жогорку натыйжалуу негизги баалуу дүкөндөрдү баалоо".

iOS тиркемелериндеги LMDB ачкыч-маанилер базасынын жаркырап жана жакырдыгы

Барак түйүндөрүнүн ички мазмунун карап чыгып, биз андан ары LMDB B дарагын төмөнкү формада жөнөкөйлөтүлгөн түрдө көрсөтөбүз.

iOS тиркемелериндеги LMDB ачкыч-маанилер базасынын жаркырап жана жакырдыгы

Түйүндөрү бар барактар ​​дискте ырааттуу жайгашат. Жогорку номерленген барактар ​​файлдын аягында жайгашкан. Мета-баракча бардык дарактардын тамырларын табууга мүмкүн болгон офсеттер жөнүндө маалыматты камтыйт. Файлды ачып жатканда, LMDB жарактуу мета баракты издөө үчүн файлды барактан барактан сканерлейт жана ал аркылуу учурдагы маалымат базаларын табат.

iOS тиркемелериндеги LMDB ачкыч-маанилер базасынын жаркырап жана жакырдыгы

Эми, маалыматтарды уюштуруунун логикалык жана физикалык түзүмү жөнүндө түшүнүккө ээ болуу менен, биз LMDB үчүнчү мамысын карап чыга алабыз. Анын жардамы менен бардык сактоо модификациялары транзакциялуу түрдө жана бири-биринен обочолонуп, маалымат базасына бүтүндөй мультиверсиялык касиетти берет.

3.3. Кит №3. Жаз боюнча көчүрмө

Кээ бир В дарагынын операциялары анын түйүндөрүнө бир катар өзгөртүүлөрдү киргизүүнү камтыйт. Бир эле мисал, максималдуу кубаттуулугуна жеткен түйүнгө жаңы ачкыч кошуу. Бул учурда, биринчиден, түйүндү экиге бөлүү керек, экинчиден, анын ата-энесинде жаңы бүчүрлүү бала түйүнгө шилтеме кошуу керек. Бул жол-жобосу өтө коркунучтуу болуп саналат. Эгерде кандайдыр бир себептерден улам (кырсык, электр жарыгынын үзгүлтүккө учурашы, ж.

Берилиштер базасынын каталарына чыдамдуу кылуунун салттуу чечимдеринин бири B-дарагынын жанына дисктеги кошумча маалымат структурасын кошуу болуп саналат - транзакциялар журналы, ошондой эле алдын ала жазуу журналы (WAL) катары белгилүү. Бул файл, анын аягында пландалган операция B-дарагынын өзүн өзгөртүүдөн мурун так жазылган. Ошентип, өзүн-өзү диагностикалоо учурунда маалыматтардын бузулушу аныкталса, маалымат базасы өзүн иретке келтирүү үчүн журналга кайрылат.

LMDB катага толеранттуулук механизми катары башка ыкманы тандап алган, аны жазууга көчүрүү деп атаган. Анын маңызы бар беттеги маалыматтарды жаңыртуунун ордуна, алгач аны толугу менен көчүрүп, көчүрмөдө бардык өзгөртүүлөрдү киргизет.

iOS тиркемелериндеги LMDB ачкыч-маанилер базасынын жаркырап жана жакырдыгы

Андан кийин, жаңыланган маалыматтар жеткиликтүү болушу үчүн, анын негизги түйүнүндө учурдагы болуп калган түйүнгө шилтемени өзгөртүү керек. Бул үчүн да өзгөртүү керек болгондуктан, ал да алдын ала көчүрүлөт. Процесс рекурсивдүү түрдө тамырга чейин уланат. Өзгөртүү үчүн акыркы нерсе - мета баракчадагы маалыматтар.​

iOS тиркемелериндеги LMDB ачкыч-маанилер базасынын жаркырап жана жакырдыгы

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

iOS тиркемелериндеги LMDB ачкыч-маанилер базасынын жаркырап жана жакырдыгы

Тиркеме үчүн гана В-дарагы деп аталган натыйжадагы дизайн, албетте, транзакцияларды изоляциялоону жана көп версияны камсыз кылат. LMDBде ар бир ачык транзакция учурда тиешелүү дарак тамыры менен байланышкан. Транзакция аяктаганга чейин, ага байланыштуу дарактын барактары эч качан өзгөртүлбөйт же маалыматтардын жаңы версиялары үчүн кайра колдонулбайт.Ошентип, сиз ошол убакта тиешелүү болгон маалыматтардын так топтому менен каалаганча иштей аласыз. транзакция ачылды, ал тургай, учурда сактагыч жигердүү жаңыртылат. Бул LMDB биздин сүйүктүүбүз үчүн идеалдуу маалымат булагы кылган мультиверсиянын маңызы UICollectionView. Транзакцияны ачкандан кийин, эч нерсеси жок калып калуудан коркуп, учурдагы маалыматтарды шашылыш түрдө кээ бир эс тутумдагы түзүмгө чыгаруу менен колдонмонун эс тутумун көбөйтүүнүн кереги жок. Бул өзгөчөлүк LMDBди ошол эле SQLiteден айырмалап турат, ал мындай толук изоляция менен мактана албайт. Акыркысында эки транзакцияны ачып, алардын бириндеги белгилүү бир жазууну жок кылгандан кийин, калган экинчисинин ичинде ошол эле жазууну алуу мүмкүн болбой калат.

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

iOS тиркемелериндеги LMDB ачкыч-маанилер базасынын жаркырап жана жакырдыгы

Бул жерде эстутумга түшүрүлгөн файлдар боюнча бөлүмдү эстеп алуу пайдалуу болмок. Виртуалдык эстутумдун кошумча керектөөлөрү бизди көп тынчсыздандырбашы керек окшойт, анткени ал тиркеменин эс тутумунун изи үчүн салым кошпойт. Бирок, ошол эле учурда, iOS аны бөлүштүрүүдө өтө сараң экени жана биз серверде же иш тактасындай, LMDB аймагын 1 терабайт менен камсыз кыла албайбыз жана бул функция жөнүндө такыр ойлонбойбуз деп белгиленди. Мүмкүн болсо, транзакциялардын мөөнөтүн мүмкүн болушунча кыска кылууга аракет кылышыңыз керек.

4. Ачкыч-маани API үстүнө берилиш схемасын долбоорлоо

Келгиле, API анализибизди LMDB тарабынан берилген негизги абстракцияларды карап баштайлы: чөйрө жана маалымат базалары, ачкычтар жана баалуулуктар, транзакциялар жана курсорлор.

Код тизмеси жөнүндө эскертүү

Жалпыга ачык 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. Бардык негизги касиеттерин колдойт КЫЧКЫЛ: атомдуулук, ырааттуулук, изоляция жана ишенимдүүлүк. MDBXде оңдолгон macOS жана iOS'тун туруктуулугуна байланыштуу ката бар экенин белгилебей коё албайм. Кененирээк алардан окуй аласыз README.
  2. Көп агымга болгон мамиле "бир жазуучу / бир нече окурман" схемасы менен сүрөттөлөт. Жазуучулар бири-бирине тоскоол болот, бирок окурмандарды тоспойт. Окурмандар жазуучуларды же бири-бирин тоспойт.
  3. Уюшкан транзакцияларды колдоо.
  4. Multiversion колдоо.

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 менен бир эле амалды колдонуп көрүп, эмне болорун көрүүнү сунуштайм.

Multiversion iOS иштеп чыгуучунун жашоосуна абдан жакшы мүмкүнчүлүктөрдү алып келет. Бул касиетти колдонуу менен, колдонуучу тажрыйбасынын ойлоруна таянып, экран формалары үчүн маалымат булагын жаңыртуу ылдамдыгын оңой жана табигый түрдө тууралай аласыз. Мисалы, системанын медиа галереясынан мазмунду автожүктөө сыяктуу Mail.ru Cloud тиркемесинин өзгөчөлүгүн алалы. Жакшы байланыш менен кардар серверге секундасына бир нече сүрөттөрдү кошо алат. Ар бир жүктөөдөн кийин жаңыртсаңыз UICollectionView колдонуучунун булутундагы медиа-контент менен сиз бул процессте 60 кадр/сек жана жылмакай сыдырууну унута аласыз. Экранды тез-тез жаңыртууларды болтурбоо үчүн, сиз кандайдыр бир жол менен базадагы маалыматтардын өзгөрүү ылдамдыгын чектөөңүз керек UICollectionViewDataSource.

Эгерде маалымат базасы мультиверсияны колдоого албаса жана учурдагы абал менен гана иштөөгө мүмкүндүк берсе, анда маалыматтардын убакытка туруктуу сүрөтүн түзүү үчүн, аны кандайдыр бир эс тутумдагы маалымат структурасына же убактылуу таблицага көчүрүү керек. Бул ыкмалардын кайсынысы болбосун абдан кымбат. Эстутумда сакталган учурда, биз эстутумда да, курулган объекттерди сактоо менен шартталган жана ашыкча ORM трансформациялары менен байланышкан чыгымдарды алабыз. Убактылуу дасторконго келсек, бул анча-мынча эмес учурларда гана мааниге ээ болгон андан да кымбат ырахат.

LMDBдин мультиверсиялык чечими туруктуу маалымат булагын сактоо маселесин абдан көрктүү түрдө чечет. Болгону транзакцияны ачуу жана voila жетиштүү - биз аны аягына чыгармайынча, маалыматтар топтому бекитилет. Анын жаңыртуу ылдамдыгынын логикасы азыр толугу менен презентация катмарынын колунда, олуттуу ресурстарды талап кылбайт.

Курсорлор

Курсорлор 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" баскычына "түйүн ID" жана "убакыт белгиси" талаалары түрүндө кошулат. Алардын жардамы менен сиз индекс ачкычын түзө аласыз, андан сиз баштапкы ачкычты ала аласыз, андан акыры түйүн метаберилиштерин ала аласыз.

Корутунду

Биз LMDB ишке ашыруунун натыйжаларына оң баа беребиз. Андан кийин, тиркемелерди катып калуулардын саны 30% га кыскарган.

iOS тиркемелериндеги LMDB ачкыч-маанилер базасынын жаркырап жана жакырдыгы

Аткарылган иштин натыйжалары iOS командасынан тышкары резонанс жаратты. Учурда Android тиркемесинин негизги "Файлдар" бөлүмдөрүнүн бири да LMDB колдонууга өттү, ал эми башка бөлүктөрү жолдо. Негизги баалуулуктар сактагычы ишке ашырылган Си тили C++ тилинде анын айланасында кросс-платформанын айланасында колдонмо негизин түзүүгө жакшы жардам берген. Алынган C++ китепканасын Objective-C жана Kotlin тилдериндеги платформа коду менен үзгүлтүксүз туташтыруу үчүн код генератору колдонулган. Джинни Dropbox'тан, бирок бул таптакыр башка окуя.

Source: www.habr.com

Комментарий кошуу