iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さ

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さ

2019 幎の秋、Mail.ru Cloud iOS チヌムで埅望のむベントが開催されたした。 アプリケヌションの状態を氞続的に保存するためのメむン デヌタベヌスは、モバむル䞖界にずっお非垞に珍しいものになっおいたす。 Lightning メモリマップド デヌタベヌス (LMDB)。 カットの䞋では、XNUMX 郚構成の詳现なレビュヌに泚目しおください。 たず、このような簡単ではなく難しい遞択をした理由に぀いお話したしょう。 次に、LMDB アヌキテクチャの䞭心ずなる XNUMX ぀のクゞラ、぀たりメモリ マップト ファむル、B + ツリヌ、トランザクションおよびマルチバヌゞョンを実装するためのコピヌ オン ラむト アプロヌチの怜蚎に移りたしょう。 最埌に、デザヌトの実践的な郚分です。 その䞭で、䜎レベルのキヌず倀の API の䞊に、むンデックス テヌブルを含む耇数のテヌブルを含むデヌタベヌス スキヌマを蚭蚈および実装する方法を芋おいきたす。

ペヌゞ内容

  1. 実装の動機
  2. LMDBの䜍眮決め
  3. XNUMX 頭のクゞラ LMDB
    3.1. クゞラその。 メモリマップされたファむル
    3.2. クゞラその。 B+ツリヌ
    3.3. クゞラその。 コピヌオンラむト
  4. Key-Value API に基づくデヌタ スキヌマの蚭蚈
    4.1. 基本的な抜象化
    4.2. テヌブルモデリング
    4.3. テヌブル間の関係のモデル化

1. 導入動機

2015 幎に幎に XNUMX 回、アプリケヌションのむンタヌフェヌスがどのくらいの頻床で遅延するかずいう指暙を削陀したした。 私たちはこれをただやっただけではありたせん。 ボタンが抌されない、リストがスクロヌルしないなど、アプリケヌションがナヌザヌのアクションに応答しなくなる堎合があるずいう事実に関する苊情が増えおいたす。 枬定の仕組みに぀いお 私は蚀いたした AvitoTech に掲茉されおいるので、ここでは番号の順序のみを瀺したす。

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さ

枬定結果は私たちにずっお冷氎シャワヌずなりたした。 フリヌズによっお匕き起こされる問題は、他の問題よりもはるかに倚いこずが刀明したした。 この事実に気づく前に、品質の䞻芁なテクニカル指暙がクラッシュフリヌだった堎合、焊点が圓おられた埌は、 ずれた フリヌズフリヌで。

構築した ダッシュボヌドがフリヌズする そしお過ごした 定量的 О 品質 原因を分析するず、アプリケヌションのメむンスレッドで実行される重いビゞネス ロゞックが䞻な敵であるこずが明らかになりたした。 この䞍名誉に察する自然な反応は、それを仕事の流れに抌し蟌みたいずいう燃えるような願望でした。 この問題を䜓系的に解決するために、私たちは軜量アクタヌに基づくマルチスレッド アヌキテクチャに頌りたした。 私は圌女の適応を iOS の䞖界に捧げたした XNUMX぀のスレッド 集合ツむッタヌや ハブレに関する蚘事。 今回の話の䞀環ずしお、デヌタベヌスの遞択に圱響を䞎えた決定の偎面を匷調したいず思いたす。

システム組織のアクタヌ モデルは、マルチスレッドがその XNUMX 番目の本質になるこずを前提ずしおいたす。 その䞭のモデルオブゞェクトはスレッドの境界を越えるこずを奜みたす。 そしお、圌らはこれを時々、いく぀かの堎所で行うのではなく、ほが垞に、どこでも行いたす。

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さ

デヌタベヌスは、提瀺された図の基瀎ずなるコンポヌネントの XNUMX ぀です。 その䞻なタスクはマクロ パタヌンを実装するこずです。 共有デヌタベヌス。 ゚ンタヌプラむズ環境ではサヌビス間のデヌタ同期を組織するために䜿甚され、アクタヌ アヌキテクチャの堎合はスレッド間のデヌタ同期に䜿甚されたす。 したがっお、マルチスレッド環境での䜜業が最小限の問題さえ匕き起こさないようなデヌタベヌスが必芁でした。 特に、これは、そこから掟生したオブゞェクトが少なくずもスレッドセヌフでなければならず、理想的にはたったく倉曎䞍可胜であるこずを意味したす。 ご存知のずおり、埌者はロックを䜿甚せずに耇数のスレッドから同時に䜿甚できるため、パフォヌマンスに有益な効果がありたす。

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さデヌタベヌスの遞択に圱響を䞎えた XNUMX 番目の重芁な芁玠は、クラりド API でした。 これは、同期に察する git アプロヌチからむンスピレヌションを埗たものです。 圌のように私たちが目指したのは オフラむンファヌストAPIこれはクラりド クラむアントに適しおいたす。 クラりドの完党な状態を䞀床だけ取り出すだけで、その埌はほずんどの堎合、ロヌリング倉曎を通じお同期が行われるず想定されおいたした。 残念なこずに、この可胜性はただ理論䞊の領域にすぎず、実際には、クラむアントはパッチの䜿い方を孊んでいたせん。 これには客芳的な理由が倚数ありたすが、導入を遅らせないために括匧曞きから倖したす。 ここで、API が「A」ず蚀い、その消費者が「B」ず蚀わなかった堎合に䜕が起こるかに぀いおのレッスンの有益な結果は、さらに興味深いものです。

したがっお、プル コマンドを実行するずきに、ロヌカル スナップショットにパッチを適甚するのではなく、その完党な状態をサヌバヌ党䜓の状態ず比范する git を想像するず、同期の仕組みに぀いおかなり正確に理解できるでしょう。クラりドクラむアントで発生したす。 その実装には、すべおのサヌバヌ ファむルずロヌカル ファむルに関するメタ情報を含む 500 ぀の DOM ツリヌをメモリに割り圓おる必芁があるこずは容易に掚枬できたす。 ナヌザヌが 1 䞇個のファむルをクラりドに保存しおいる堎合、それを同期するには、XNUMX 䞇ノヌドを持぀ XNUMX ぀のツリヌを再䜜成しお砎棄する必芁があるこずがわかりたした。 ただし、各ノヌドはサブオブゞェクトのグラフを含む集合䜓です。 この芳点から、プロファむリングの結果は予想通りでした。 マヌゞ アルゎリズムを考慮しなくおも、膚倧な数の小さなオブゞェクトを䜜成しお砎棄する手順自䜓にかなりの費甚がかかるこずが刀明したした。基本的な同期操䜜が倚数のオブゞェクトに含たれおいるずいう事実により、状況はさらに悪化しおいたす。ナヌザヌスクリプトの。 その結果、デヌタベヌスを遞択する際の XNUMX 番目の重芁な基準、぀たりオブゞェクトを動的に割り圓おずに CRUD 操䜜を実装できる機胜が修正されたした。

他の芁件はより䌝統的なもので、その完党なリストは次のずおりです。

  1. スレッドの安党性。
  2. マルチプロセッシング。 同じデヌタベヌス むンスタンスを䜿甚しお、スレッド間だけでなく、メむン アプリケヌションず iOS 拡匵機胜の間でも状態を同期したいずいう芁望によっお決たりたす。
  3. 保存された゚ンティティを倉曎䞍可胜なオブゞェクトずしお衚す機胜。
  4. CRUD 操䜜内での動的割り圓おの欠劂。
  5. 基本プロパティのトランザクションサポヌト ACIDキヌワヌド: 原子性、䞀貫性、分離性、信頌性。
  6. 最も人気のあるケヌスをスピヌドアップしたす。

この䞀連の芁件を考慮するず、SQLite は今も昔も優れた遞択肢です。 しかし、代替案を怜蚎する䞭で、ある本に出䌚いたした。 「LevelDB 入門」。 圌女のリヌダヌシップの䞋、実際のクラりド シナリオでさたざたなデヌタベヌスの䜜業速床を比范するベンチマヌクが䜜成されたした。 結果は最も予想を䞊回りたした。 最も䞀般的なケヌスでは、すべおのファむルの゜ヌトされたリスト䞊でカヌ゜ルを取埗したり、指定されたディレクトリのすべおのファむルの゜ヌトされたリストを取埗したりする堎合、LMDB は SQLite より 10 倍高速であるこずが刀明したした。 遞択は明癜になりたした。

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さ

2. LMDBの䜍眮付け

LMDB は、デヌタベヌスの最も䜎い基本局であるストレヌゞを実装する、非垞に小さい (わずか 10K 行) ラむブラリです。

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さ

䞊の図は、LMDB ずさらに高いレベルを実装する SQLite を比范するこずは、通垞、SQLite ず Core Data よりも正確ではないこずを瀺しおいたす。 BerkeleyDB、LevelDB、Sophia、RocksDB など、同じストレヌゞ ゚ンゞンを同等の競合他瀟ずしお挙げる方が公平でしょう。LMDB が SQLite のストレヌゞ ゚ンゞン コンポヌネントずしお機胜する開発さえありたす。 2012幎に初めおそのような実隓が行われた 行っ 著者LMDB ハワヌド・チュヌ. 結果 非垞に興味深いものであるこずが刀明したため、圌のむニシアチブは OSS 愛奜家に取り䞊げられ、その継続が芋出されたした。 LumoSQL。 2020 幎 XNUMX 月のこのプロゞェクトの䜜成者は Den Shearer です 提瀺された LinuxConfAu でご芧ください。

LMDB の䞻な甚途は、アプリケヌション デヌタベヌスの゚ンゞンずしおです。 ラむブラリの倖芳は開発者のおかげです OpenLDAP圌らは、プロゞェクトの基盀ずしおの BerkeleyDB に匷い䞍満を抱いおいたした。 質玠な図曞通からの脱华 ツリヌ, ハワヌド・チュヌは、珟代で最も人気のある代替手段のXNUMX぀を䜜成するこずができたした。 圌は、このストヌリヌず LMDB の内郚構造に぀いお、非垞にクヌルなレポヌトを䜜成したした。 「ラむトニングメモリマップドデヌタベヌス」。 レオニヌド・ナリ゚フ別名 むレオ) Highload 2015 での Positive Technologies の講挔 「LMDB ゚ンゞンは特別なチャンピオンです」。 その䞭で圌は、ReOpenLDAP の実装ずいう同様のタスクの文脈で LMDB に぀いお語っおいたすが、LevelDB はすでに比范批刀を受けおいたす。 実装の結果、Positive Technologies は積極的に開発䞭のフォヌクを入手したした。 MDBX 非垞に優れた機胜、最適化、 バグの修正.

LMDB は、そのたたのストレヌゞずしおもよく䜿甚されたす。 たずえば、Mozilla Firefox ブラりザヌ 遞んだ 倚くのニヌズに察応しおおり、バヌゞョン 9 以降では Xcode 優先 むンデックスを保存するための SQLite。

この゚ンゞンはモバむル開発の䞖界でも人気を博したした。 䜿甚の痕跡が残る可胜性がありたす 芋぀ける Telegram の iOS クラむアント内。 LinkedIn はさらに䞀歩進んで、自瀟開発のデヌタ キャッシュ フレヌムワヌクである Rocket Data のデフォルト ストレヌゞずしお LMDB を遞択したした。 蚀った 2016幎の蚘事で。

LMDB は、Oracle の管理䞋ぞの移行埌、BerkeleyDB が残したニッチな分野で、日の圓たる堎所をめぐっお銖尟よく戊っおいたす。 このラむブラリは、同じ皮類のラむブラリず比范しおも、その速床ず信頌性で愛されおいたす。 ご存知のずおり、無料のランチはありたせん。LMDB ず SQLite のどちらを遞択するかに぀いおは、トレヌドオフに盎面する必芁があるこずを匷調したいず思いたす。 䞊の図は、高速化がどのように達成されるかを明確に瀺しおいたす。 たず、ディスク ストレヌゞ䞊の远加の抜象化レむダヌに察しお料金を支払う必芁はありたせん。 もちろん、優れたアヌキテクチャでは、䟝然ずしおそれらなしでは成り立ちたせん。必然的にアプリケヌション コヌドにそれらが衚瀺されたすが、はるかに薄くなりたす。 SQL 蚀語でのク゚リのサポヌトなど、特定のアプリケヌションに必芁のない機胜は備えおいたせん。 第 XNUMX に、アプリケヌションの操䜜ずディスク ストレヌゞぞのリク゚ストのマッピングを最適に実装できるようになりたす。 SQLiteの堎合 私の仕事で 平均的なアプリケヌションの平均的なニヌズから生じおいる堎合、アプリケヌション開発者は䞻な負荷シナリオをよく知っおいたす。 より生産的な゜リュヌションを実珟するには、最初の゜リュヌションの開発ずその埌のサポヌトの䞡方に高額な料金を支払う必芁がありたす。

3. XNUMX 頭のクゞラ LMDB

LMDB を俯瞰的に芋た埌は、さらに深く芋おいきたしょう。 次の XNUMX ぀のセクションでは、ストレヌゞ アヌキテクチャの基盀ずなる䞻な芁玠の分析に専念したす。

  1. ディスクを操䜜し、内郚デヌタ構造を同期するためのメカニズムずしおのメモリ マップ ファむル。
  2. 保存されたデヌタ構造の組織ずしおの B+ ツリヌ。
  3. ACID トランザクション プロパティずマルチバヌゞョン管理を提䟛するアプロヌチずしおのコピヌオンラむト。

3.1. クゞラその。 メモリマップされたファむル

メモリマップされたファむルは、リポゞトリの名前にも䜿甚されるほど重芁なアヌキテクチャ芁玠です。 保存された情報ぞのアクセスのキャッシュず同期の問題は、完党にオペレヌティング システムの刀断に委ねられたす。 LMDB 自䜓にはキャッシュが含たれたせん。 マップされたファむルからデヌタを盎接読み取るこずで、゚ンゞンの実装で倚くの郚分を省略できるため、これは䜜成者による意識的な決定です。 以䞋は、それらの䞀郚の完党なリストではありたせん。

  1. 耇数のプロセスからストレヌゞ内のデヌタを操䜜する堎合、そのデヌタの䞀貫性を維持するのはオペレヌティング システムの責任になりたす。 次のセクションでは、このメカニズムに぀いお画像を䜿甚しお詳现に説明したす。
  2. キャッシュが存圚しないため、LMDB は動的割り圓おに関連するオヌバヌヘッドから完党に解攟されたす。 実際のデヌタの読み取りずは、ポむンタを仮想メモリ内の正しいアドレスに蚭定するこずだけであり、それ以䞊のものではありたせん。 空想のように聞こえたすが、リポゞトリ ゜ヌスでは、すべおの calloc 呌び出しがリポゞトリ構成関数に集䞭しおいたす。
  3. キャッシュがないずいうこずは、キャッシュにアクセスするための同期に関連するロックがないこずも意味したす。 同時に任意の数のリヌダヌが存圚できたすが、デヌタにアクセスする途䞭で単䞀のミュヌテックスに遭遇するこずはありたせん。 このため、読み取り速床は CPU の数に関しお理想的な線圢スケヌラビリティを持ちたす。 LMDB では、倉曎操䜜のみが同期されたす。 䞀床に存圚できるラむタヌは XNUMX 人だけです。
  4. 最小限のキャッシュず同期ロゞックにより、マルチスレッド環境での䜜業に関連する非垞に耇雑なタむプの゚ラヌからコヌドを節玄できたす。 Usenix OSDI 2014 カンファレンスでは、XNUMX ぀の興味深いデヌタベヌス研究がありたした。 「すべおのファむル システムは同じように䜜られおいるわけではない: クラッシュ コンシステントなアプリケヌションの䜜成の耇雑さに぀いお」 О 嚯楜ず利益のためにデヌタベヌスを拷問する。 これらから、LMDB の前䟋のない信頌性ず、同じ SQLite でそれを䞊回るトランザクションの ACID プロパティのほが完璧な実装の䞡方に関する情報を埗るこずができたす。
  5. LMDB のミニマリズムにより、コヌドのマシン衚珟をプロセッサの L1 キャッシュに完党に配眮でき、結果ずしお速床特性が埗られたす。

残念ながら、iOS では、メモリ マップされたファむルは私たちが望むほどバラ色ではありたせん。 それらに関連する欠点に぀いおより意識的に語るには、このメカニズムをオペレヌティング システムに実装するための䞀般原則を思い出す必芁がありたす。

メモリマップトファむルに関する䞀般情報

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さオペレヌティング システムは、実行可胜なアプリケヌションごずに、プロセスず呌ばれる゚ンティティを関連付けたす。 各プロセスには連続したアドレス範囲が割り圓おられ、動䜜するために必芁なものすべおがそこに配眮されたす。 最䞋䜍のアドレスには、コヌドずハヌドコヌドされたデヌタずリ゜ヌスを含むセクションが含たれたす。 次に、ヒヌプずしおよく知られおいる、動的アドレス空間の䞊向きに成長するブロックが続きたす。 これには、プログラムの動䜜䞭に衚瀺される゚ンティティのアドレスが含たれたす。 䞀番䞊は、アプリケヌション スタックによっお䜿甚されるメモリ領域です。 倧きくなったり、小さくなったり、぀たりその倧きさもダむナミックな性質を持っおいたす。 スタックずヒヌプが互いに抌し合ったり干枉したりしないように、スタックずヒヌプはアドレス空間の異なる端で分離されおおり、䞊郚ず䞋郚の XNUMX ぀の動的セクションの間には穎がありたす。 この䞭間セクションのアドレスは、オペレヌティング システムによっおさたざたな゚ンティティのプロセスに関連付けられるために䜿甚されたす。 特に、特定の連続したアドレスのセットをディスク䞊のファむルにマッピングできたす。 このようなファむルはメモリマップト ファむルず呌ばれたす。

プロセスに割り圓おられるアドレス空間は膚倧です。 理論的には、アドレスの数はポむンタのサむズによっおのみ制限されたす。ポむンタのサむズはシステムのビット深床によっお決たりたす。 物理メモリが 1-in-1 で割り圓おられおいる堎合、最初のプロセスが RAM 党䜓を䜿い果たし、マルチタスクが発生するこずは疑いの䜙地がありたせん。

しかし、私たちは経隓から、最新のオペレヌティング システムでは同時に必芁な数のプロセスを実行できるこずを知っおいたす。 これは、玙䞊のプロセスのみに倚くのメモリを割り圓おるずいう事実によっお可胜になりたすが、実際には、珟時点で必芁な郚分のみがメむンの物理メモリにロヌドされたす。 したがっお、プロセスに関連付けられたメモリは仮想ず呌ばれたす。

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さ

オペレヌティング システムは、仮想メモリず物理メモリを特定のサむズのペヌゞに線成したす。 仮想メモリの特定のペヌゞが必芁になるずすぐに、オペレヌティング システムはそれを物理メモリにロヌドし、それらの察応関係を特別なテヌブルに曞き蟌みたす。 空きスロットがない堎合は、以前にロヌドされたペヌゞの 0 ぀がディスクにコピヌされ、芁求されたペヌゞがその代わりになりたす。 この手順に぀いおは埌で説明したすが、スワッピングず呌ばれたす。 以䞋の図は、説明されおいるプロセスを瀺しおいたす。 その䞊で、アドレス 4 のペヌゞ A がロヌドされ、アドレス 0 のメむン メモリ ペヌゞに配眮されたした。この事実は、セル番号 XNUMX の察応衚に反映されおいたす。

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さ

メモリマップされたファむルの堎合も、状況はたったく同じです。 論理的には、それらは仮想アドレス空間に継続的か぀完党に配眮されるず考えられたす。 ただし、物理メモリにはペヌゞごずに、オンデマンドでのみアクセスされたす。 このようなペヌゞの倉曎は、ディスク䞊のファむルず同期されたす。 したがっお、メモリ内のバむトを操䜜するだけで、ファむル I/O を実行できたす。すべおの倉曎は、オペレヌティング システム カヌネルによっお元のファむルに自動的に転送されたす。
​,war
以䞋の画像は、異なるプロセスからデヌタベヌスを操䜜するずきに LMDB がどのように状態を同期するかを瀺しおいたす。 異なるプロセスの仮想メモリを同じファむルにマッピングするこずにより、オペレヌティング システムは事実䞊、アドレス空間の特定のブロックを盞互に掚移的に同期するこずが矩務付けられたす。これが LMDB の目的です。
​,war

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さ

重芁な点は、LMDB が曞き蟌みシステム コヌル メカニズムを通じおデフォルトでデヌタ ファむルを倉曎し、ファむル自䜓が読み取り専甚モヌドで衚瀺されるこずです。 このアプロヌチには XNUMX ぀の重芁な意味がありたす。

最初の結果は、すべおのオペレヌティング システムに共通です。 その本質は、䞍正なコヌドによるデヌタベヌスぞの䞍泚意による損傷に察する保護を远加するこずです。 ご存知のずおり、プロセスの実行可胜呜什は、アドレス空間内のどこからでもデヌタに自由にアクセスできたす。 同時に、先ほど思い出したように、ファむルを読み取り/曞き蟌みモヌドで衚瀺するずいうこずは、どの呜什でもファむルをさらに倉曎できるこずを意味したす。 たずえば、存圚しないむンデックスにある配列芁玠を実際に䞊曞きしようずするなど、誀っおこれを行った堎合、この方法でこのアドレスにマップされおいるファむルを誀っお倉曎する可胜性があり、デヌタベヌスの砎損に぀ながる可胜性がありたす。 ファむルが読み取り専甚モヌドで衚瀺されおいる堎合、それに察応するアドレス空間を倉曎しようずするず、シグナルによるプログラムのクラッシュが発生したす。 SIGSEGV、ファむルはそのたた残りたす。

XNUMX 番目の結果は、すでに iOS に固有のものです。 䜜者も他の情報源もそれに぀いお明瀺的に蚀及しおいたせんが、それがなければ、LMDB はこのモバむル オペレヌティング システムでの実行には適しおいたせん。 次のセクションではその考察に専念したす。

iOS のメモリマップされたファむルの詳现

2018幎のWWDCでは玠晎らしい報告がありたした iOSメモリの詳现。 iOS では、物理メモリにあるすべおのペヌゞが、ダヌティ、圧瞮、クリヌンの 3 ぀のタむプのいずれかに属するこずがわかりたす。

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さ

クリヌン メモリは、物理メモリから安党にスワップアりトできるペヌゞのコレクションです。 それらに含たれるデヌタは、必芁に応じお元の゜ヌスから再ロヌドできたす。 読み取り専甚のメモリ マップ ファむルがこのカテゎリに分類されたす。 iOS は、ファむルにマップされたペヌゞをディスク䞊のファむルず同期しおいるこずが保蚌されおいるため、い぀でもメモリからアンロヌドするこずを躊躇したせん。
​,war
倉曎されたすべおのペヌゞは、元の堎所に関係なく、ダヌティ メモリに入りたす。 特に、関連付けられた仮想メモリぞの曞き蟌みによっお倉曎されたメモリ マップト ファむルも、この方法で分類されたす。 フラグを䜿甚しお LMDB を開く MDB_WRITEMAP倉曎を加えた埌は、自分の目で確認できたす。

アプリケヌションが物理メモリを過剰に占有し始めるずすぐに、iOS はダヌティ ペヌゞを圧瞮したす。 ダヌティ ペヌゞや圧瞮ペヌゞによっお占有されおいるメモリの集合が、いわゆるアプリケヌションのメモリ フットプリントです。 特定のしきい倀に達するず、OOM キラヌ システム デヌモンがプロセスの埌に来お匷制的に終了したす。 これは、デスクトップ オペレヌティング システムず比范した iOS の特殊性です。 察照的に、物理メモリからディスクにペヌゞをスワップしおメモリ䜿甚量を削枛する機胜は iOS には提䟛されおおらず、その理由に぀いおは掚枬するしかありたせん。 おそらく、ペヌゞをディスクに集䞭的に移動したり、元に戻したりする手順は、モバむル デバむスにずっお゚ネルギヌ消費が倚すぎるか、iOS が SSD ディスク䞊のセルを曞き換えるリ゜ヌスを節玄しおいるか、あるいは、すべおが揃っおいるシステムの党䜓的なパフォヌマンスに蚭蚈者が満足しおいなかった可胜性がありたす。垞に亀換されおいたす。 いずれにせよ、事実は倉わりたせん。

良いニュヌスは、すでに前述したように、LMDB はデフォルトではファむルの曎新に mmap メカニズムを䜿甚しないこずです。 したがっお、レンダリングされたデヌタは iOS によっおクリヌン メモリずしお分類され、メモリ フットプリントには圱響したせん。 これは、VM Tracker ず呌ばれる Xcode ツヌルを䜿甚しお確認できたす。 以䞋のスクリヌンショットは、動䜜䞭の iOS クラりド アプリケヌションの仮想メモリの状態を瀺しおいたす。 開始時には、2 ぀の LMDB むンスタンスが初期化されたした。 1 ぀目はファむルを 512 GiB の仮想メモリにマップするこずができ、XNUMX ぀目は XNUMXMiB でした。 どちらのストレヌゞも䞀定量の垞駐メモリを占有したすが、どちらもダヌティ サむズには圱響したせん。

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さ

さお、悪いニュヌスの時間です。 64 ビット デスクトップ オペレヌティング システムのスワップ メカニズムのおかげで、各プロセスは、ハヌド ディスク䞊の空き領域がスワップの可胜性を蚱容するのず同じだけ倚くの仮想アドレス領域を占有するこずができたす。 iOS でスワップを圧瞮に眮き換えるず、理論䞊の最倧倀が倧幅に枛少したす。 珟圚、すべおの生きおいるプロセスはメむン (RAM 読み取り) メモリに収たる必芁があり、収たらないプロセスはすべお匷制終了の察象ずなりたす。 䞊蚘のように蚘茉されおいたすが、 レポヌト、および 公匏ドキュメント。 結果ずしお、iOS は mmap 経由で割り圓お可胜なメモリの量を厳しく制限したす。 ここ ここで このシステム コヌルを䜿甚しお、さたざたなデバむスに割り圓おられるメモリ量の経隓的な制限を確認できたす。 スマヌトフォンの最新モデルでは、iOS の容量が 2 ギガバむト増加し、iPad の最䞊䜍バヌゞョンでは 4 ギガバむト増加しおいたす。もちろん、実際には、サポヌトされおいる最も若いデバむス モデルに泚目する必芁がありたすが、そこではすべおが非垞に残念です。 さらに悪いこずに、VM Tracker でアプリケヌションのメモリ状態を確認するず、メモリ マップド メモリを芁求しおいるのは LMDB だけではないこずがわかりたす。 適切なチャンクは、システム アロケヌタヌ、リ゜ヌス ファむル、むメヌゞ フレヌムワヌク、その他の小さな捕食者によっお食い荒らされたす。

クラりドでの実隓の結果、LMDB によっお割り圓おられるメモリの劥協倀ずしお、384 ビット デバむスの堎合は 32 メガバむト、768 ビット デバむスの堎合は 64 メガバむトが芋぀かりたした。 このボリュヌムが䜿い果たされるず、コヌドによる倉曎操䜜が完了し始めたす。 MDB_MAP_FULL。 モニタリングではこのような゚ラヌが芳察されたすが、この段階では無芖できるほど小さいものです。

ストレヌゞによる過剰なメモリ消費の明らかではない理由ずしお、トランザクションの存続期間が長いこずが考えられたす。 これら XNUMX ぀の珟象がどのように関連しおいるかを理解するには、残りの XNUMX 頭の LMDB クゞラを考慮するこずが圹立ちたす。

3.2. クゞラその。 B+ツリヌ

キヌ/倀ストア䞊でテヌブルを゚ミュレヌトするには、その API に次の操䜜が存圚する必芁がありたす。

  1. 新しい芁玠を挿入したす。
  2. 指定されたキヌを持぀芁玠を怜玢したす。
  3. 芁玠を削陀したす。
  4. ゜ヌト順にキヌ間隔を繰り返したす。

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さXNUMX ぀の操䜜すべおを簡単に実装できる最も単玔なデヌタ構造は、二分探玢ツリヌです。 その各ノヌドは、子キヌのサブセット党䜓を XNUMX ぀のサブツリヌに分割するキヌです。 巊偎は芪より小さいもの、右偎は芪より倧きいものです。 順序付けられたキヌのセットの取埗は、叀兞的なツリヌ走査の XNUMX ぀を通じお実珟されたす。

バむナリ ツリヌには、ディスク デヌタ構造ずしおの効果を劚げる XNUMX ぀の根本的な欠点がありたす。 たず、そのバランスの床合いが予枬できない。 さたざたな枝の高さが倧きく異なる可胜性のあるツリヌを取埗するこずにはかなりのリスクがあり、これにより、予想されるものず比范しお怜玢のアルゎリズムの耇雑さが倧幅に悪化したす。 第二に、ノヌド間のクロスリンクが豊富であるため、バむナリ ツリヌのメモリ内での局所性が倱われ、(ノヌド間のリンクずいう点で) 近いノヌドが仮想メモリ内のたったく異なるペヌゞに配眮される可胜性がありたす。 その結果、ツリヌ内のいく぀かの隣接ノヌドを単玔に走査するだけでも、同等の数のペヌゞにアクセスする必芁がある堎合がありたす。 プロセッサ キャッシュ内でペヌゞを垞に回転させるのはコストがかからないため、メモリ内デヌタ構造ずしおのバむナリ ツリヌの有効性に぀いお話す堎合でも、これは問題になりたす。 ノヌド関連のペヌゞをディスクから頻繁に呌び出すず、状況は非垞に悪くなりたす。 嘆かわしい。

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さB ツリヌはバむナリ ツリヌの進化版であり、前の段萜で特定した問題を解決したす。 第䞀に、それらは自己バランスをずりたす。 第 2 に、各ノヌドは子キヌのセットを XNUMX ぀ではなく、M 個の順序付きサブセットに分割したす。M ずいう数は、数癟たたは数千のオヌダヌず非垞に倧きくなる可胜性がありたす。

それによっお

  1. 各ノヌドにはすでに順序付けられた倚数のキヌがあり、ツリヌは非垞に䜎くなりたす。
  2. 倀が近いキヌは自然に XNUMX ぀たたは隣接するノヌド䞊で互いに隣り合っお配眮されるため、ツリヌはメモリ内で局所性の特性を獲埗したす。
  3. 怜玢操䜜䞭にツリヌを䞋降するずきに通過ノヌドの数を枛らしたす。
  4. 各タヌゲット ノヌドにはすでに倧量の順序付けされたキヌが含たれおいるため、範囲ク゚リで読み取られるタヌゲット ノヌドの数が枛りたす。

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さ

LMDB は、B+ ツリヌず呌ばれる B ツリヌのバリアントを䜿甚しおデヌタを保存したす。 䞊の図は、それに含たれる XNUMX 皮類のノヌドを瀺しおいたす。

  1. 䞀番䞊にあるのが根です。 これは、リポゞトリ内のデヌタベヌスの抂念を具䜓化したものにすぎたせん。 単䞀の LMDB むンスタンス内に、マップされた仮想アドレス空間を共有する耇数のデヌタベヌスを䜜成できたす。 それぞれは独自のルヌトから始たりたす。
  2. 最䞋䜍レベルは葉 (リヌフ) です。 デヌタベヌスに栌玍されおいるキヌず倀のペアを含むのは、それらだけです。 ちなみに、これがB+-treeの特城です。 通垞の B ツリヌがすべおのレベルのノヌドに倀郚分を栌玍する堎合、B+ 倉動は最䞋䜍のノヌドにのみ存圚したす。 この事実を修正したので、以䞋では、LMDB で䜿甚されるツリヌのサブタむプを単に B ツリヌず呌びたす。
  3. ルヌトずリヌフの間には、ナビゲヌション (分岐) ノヌドを持぀ 0 個以䞊の技術レベルがありたす。 圌らのタスクは、゜ヌトされたキヌのセットをリヌフ間で分割するこずです。

物理的には、ノヌドは所定の長さのメモリのブロックです。 それらのサむズは、䞊で説明したオペレヌティング システムのメモリ ペヌゞのサむズの倍数です。 ノヌド構造を以䞋に瀺したす。 ヘッダヌにはメタ情報が含たれおおり、その䞭で最も明癜なものは、たずえばチェックサムです。 次に、デヌタを含むセルが䜍眮するオフセットに関する情報が続きたす。 デヌタの圹割は、ナビゲヌション ノヌドに぀いお話しおいる堎合はキヌ、たたはリヌフの堎合はキヌず倀のペア党䜓のいずれかになりたす。ペヌゞの構造に぀いおは、䜜品で詳しく読むこずができたす。 「高性胜 Key-Value ストアの評䟡」.

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さ

ペヌゞ ノヌドの内郚コンテンツを扱ったので、次の圢匏で LMDB B ツリヌをさらに簡略化しお衚したす。

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さ

ノヌドを含むペヌゞはディスク䞊に順番に配眮されたす。 番号が倧きいペヌゞほど、ファむルの終わりに向かっお配眮されたす。 いわゆるメタ ペヌゞ (メタ ペヌゞ) には、すべおのツリヌのルヌトを芋぀けるために䜿甚できるオフセットに関する情報が含たれおいたす。 ファむルが開かれるず、LMDB は有効なメタ ペヌゞを探しおファむルをペヌゞごずに最埌から最初たでスキャンし、それを通じお既存のデヌタベヌスを芋぀けたす。

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さ

デヌタ構成の論理的および物理的構造を理解したので、LMDB の XNUMX 番目のクゞラの怜蚎に進むこずができたす。 これにより、すべおのストレヌゞ倉曎がトランザクション的に盞互に分離しお実行され、デヌタベヌス党䜓にマルチバヌゞョン特性も䞎えられたす。

3.3. クゞラその。 コピヌオンラむト

䞀郚の B ツリヌ操䜜には、そのノヌドに察する䞀連の倉曎党䜓が含たれたす。 䞀䟋ずしお、すでに最倧容量に達しおいるノヌドに新しいキヌを远加するこずが挙げられたす。 この堎合、たずノヌドを XNUMX ぀に分割し、次にその芪ノヌドに新しくスピンオフされた子ノヌドぞのリンクを远加する必芁がありたす。 この手順は朜圚的に非垞に危険です。 䜕らかの理由 (クラッシュ、停電など) でシリヌズからの倉曎の䞀郚のみが発生した堎合、ツリヌは䞍敎合な状態のたたになりたす。

デヌタベヌスをフォヌルト トレラントにするための埓来の解決策の XNUMX ぀は、远加のディスク ベヌスのデヌタ構造であるトランザクション ログ (WAL) を B ツリヌの隣に远加するこずです。 これは、B ツリヌ自䜓が倉曎される盎前に、その末尟に意図した操䜜が蚘述されるファむルです。 したがっお、自己蚺断䞭にデヌタ砎損が怜出された堎合、デヌタベヌスはログを参照しお自身をクリヌンアップしたす。

LMDB は、フォヌルト トレランス メカニズムずしお、コピヌ オン ラむトず呌ばれる別の方法を遞択したした。 その本質は、既存のペヌゞのデヌタを曎新するのではなく、たずペヌゞ党䜓をコピヌし、そのコピヌ内にすでに含たれおいるすべおの倉曎を行うこずです。

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さ

たた、曎新されたデヌタを利甚できるようにするためには、芪ノヌドにおいお最新になったノヌドぞのリンクを、そのノヌドに関連しお倉曎する必芁がある。 このためにも修正が必芁なので、これも事前にコピヌされたす。 このプロセスはルヌトに至るたで再垰的に続行されたす。 メタ ペヌゞ䞊のデヌタは最埌に倉曎されたす。

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さ

曎新手順䞭にプロセスが突然クラッシュした堎合、新しいメタ ペヌゞが䜜成されないか、最埌たでディスクに曞き蟌たれず、そのチェックサムが䞍正確になりたす。 これら XNUMX ぀のケヌスのいずれの堎合も、新しいペヌゞにはアクセスできなくなりたすが、叀いペヌゞは圱響を受けたせん。 これにより、LMDB がデヌタの䞀貫性を維持するためにログを先行曞き蟌む必芁がなくなりたす。 実際には、䞊で説明したディスク䞊のデヌタ ストレヌゞの構造が同時にその機胜を果たしたす。 明瀺的なトランザクション ログがないこずは、高いデヌタ読み取り速床を提䟛する LMDB の機胜の XNUMX ぀です。

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さ

远加専甚 B ツリヌず呌ばれる結果ずしお埗られる構造は、トランザクションの分離ずマルチバヌゞョン管理を自然に提䟛したす。 LMDB では、開いおいる各トランザクションには、それに関連付けられた最新のツリヌ ルヌトがありたす。 トランザクションが完了しない限り、トランザクションに関連付けられたツリヌのペヌゞが倉曎されたり、新しいバヌゞョンのデヌタに再利甚されたりするこずはありたせん。したがっお、トランザクション時に関連しおいたデヌタ セットを正確に奜きなだけ操䜜できたす。この時点でストレヌゞがアクティブに曎新され続けおいる堎合でも、トランザクションがオヌプンされた時点で。 これがマルチバヌゞョン管理の本質であり、LMDB を私たちの愛する人にずっお理想的なデヌタ゜ヌスにしたす。 UICollectionView。 トランザクションを開いた埌は、䜕も残らないこずを恐れお、アプリケヌションのメモリ䜿甚量を増やしたり、珟圚のデヌタをメモリ内の構造に急いで送り蟌んだりする必芁はありたせん。 この機胜は、LMDB を、そのような完党な分離を誇るこずができない同じ SQLite ず区別したす。 埌者で XNUMX ぀のトランザクションを開き、そのうちの XNUMX ぀で特定のレコヌドを削陀するず、XNUMX 番目に残ったトランザクションでは同じレコヌドを取埗できなくなりたす。

コむンの裏返しずしお、仮想メモリの消費量が倧幅に増加する可胜性がありたす。 このスラむドは、デヌタベヌスの異なるバヌゞョンを参照する 3 ぀のオヌプン読み取りトランザクションを同時に倉曎した堎合に、デヌタベヌス構造がどのようになるかを瀺しおいたす。 LMDB は実際のトランザクションに関連付けられたルヌトから到達可胜なノヌドを再利甚できないため、ストレヌゞにはメモリ内に別の XNUMX 番目のルヌトを割り圓お、その䞋に倉曎されたペヌゞをもう䞀床耇補する以倖に遞択肢はありたせん。

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さ

ここで、メモリ マップト ファむルに関するセクションを思い出すのは䞍必芁ではありたせん。 仮想メモリの远加消費は、アプリケヌションのメモリ フットプリントに圱響しないため、あたり気にする必芁はないようです。 しかし同時に、iOS はその割り圓おを非垞にケチであり、マスタヌの肩からサヌバヌたたはデスクトップ䞊に 1 テラバむトの LMDB 領域を提䟛し、この機胜に぀いおたったく考慮するこずはできたせん。 可胜であれば、トランザクションの存続期間をできるだけ短くするように努めおください。

4. Key-Value 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 クラりド アプリケヌションでは、XNUMX ぀のパラメヌタヌのみのデフォルト倀を倉曎したした。

XNUMX ぀目は、ストレヌゞ ファむルがマップされる仮想アドレス空間のサむズです。 残念ながら、同じデバむスであっおも、特定の倀は実行ごずに倧きく異なる可胜性がありたす。 iOS のこの機胜を考慮しお、ストレヌゞの最倧量を動的に遞択したす。 特定の倀から開始しお、関数が達成されるたで連続的に半分になりたす。 mdb_env_open 以倖の結果は返されたせん ENOMEM。 理論的には、逆の方法がありたす。たず゚ンゞンに最小限のメモリを割り圓お、次に゚ラヌを受信したずきに、 MDB_MAP_FULL、増やしおください。 しかし、それははるかに厄介です。 その理由は、関数を䜿甚しおメモリを再マッピングする手順が mdb_env_set_map_size 以前に゚ンゞンから受け取ったすべおの゚ンティティ (カヌ゜ル、トランザクション、キヌ、倀) を無効にしたす。 コヌド内でこのような出来事の倉化を考慮するず、コヌドが倧幅に耇雑になりたす。 それでも、仮想メモリがあなたにずっお非垞に倧切である堎合、これははるか先の分岐点に泚目する理由になるかもしれたせん。 MDBX, 宣蚀された機胜の䞭には、「自動オンザフラむデヌタベヌスサむズ調敎」がありたす。

10 番目のパラメヌタは、デフォルト倀が適切ではありたせんでしたが、スレッドの安党性を確保するメカニズムを制埡したす。 残念ながら、少なくずも iOS XNUMX では、スレッド ロヌカル ストレヌゞのサポヌトに問題がありたす。 このため、䞊蚘の䟋では、リポゞトリは次のフラグで開かれたす。 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キヌワヌド: 原子性、䞀貫性、分離性、信頌性。 macOS ず iOS での耐久性の点で、MDBX で修正されたバグがあるこずに泚意せずにはいられたせん。 詳しくは、圌らの蚘事をご芧ください。 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 クラりド アプリケヌションの機胜を考えおみたしょう。 接続が良奜であれば、クラむアントは XNUMX 秒あたり数枚の写真をサヌバヌに远加できたす。 ダりンロヌドするたびに曎新する堎合 UICollectionView ナヌザヌのクラりド内のメディア コンテンツでは、このプロセス䞭に 60 fps やスムヌズなスクロヌルのこずを忘れるこずができたす。 頻繁な画面曎新を防ぐには、ベヌス内のデヌタ倉曎速床を䜕らかの方法で制限する必芁がありたす。 UICollectionViewDataSource.

デヌタベヌスがマルチバヌゞョンをサポヌトしおおらず、珟圚の状態でのみ䜜業できる堎合、時間安定したデヌタ スナップショットを䜜成するには、それをメモリ内のデヌタ構造たたは䞀時テヌブルにコピヌする必芁がありたす。 これらのアプロヌチはいずれも非垞に高䟡です。 むンメモリ ストレヌゞの堎合、構築されたオブゞェクトの保存によっお発生するメモリ コストず、冗長な ORM 倉換に関連する時間コストの䞡方が発生したす。 䞀時テヌブルに関しおは、これはさらに高䟡な楜しみであり、重芁な堎合にのみ意味がありたす。

LMDB のマルチバヌゞョン化は、安定したデヌタ ゜ヌスを維持するずいう問題を非垞に゚レガントな方法で解決したす。 トランザクションを開くだけで十分です。トランザクションが完了するたで、デヌタ セットは修正されるこずが保蚌されたす。 曎新レヌトのロゞックは完党にプレれンテヌション局の手に委ねられおおり、倧量のリ゜ヌスのオヌバヌヘッドは発生したせん。

カヌ゜ル

カヌ゜ルは、B ツリヌを走査するこずにより、キヌず倀のペアを芏則的に反埩するためのメカニズムを提䟛したす。 これらがなければ、デヌタベヌス内のテヌブルを効果的にモデル化するこずは䞍可胜であり、ここで泚目したす。

4.2. テヌブルモデリング

キヌ順序付けプロパティを䜿甚するず、基本的な抜象化の䞊にテヌブルなどのトップレベルの抜象化を構築できたす。 このプロセスを、ナヌザヌのすべおのファむルずフォルダヌに関する情報がキャッシュされおいるクラりド クラむアントのメむン テヌブルの䟋で考えおみたしょう。

テヌブルスキヌマ

フォルダヌ ツリヌを含むテヌブルの構造を明確にする必芁がある䞀般的なシナリオの XNUMX ぀は、特定のディレクトリ内にあるすべおの芁玠を遞択するこずです。この皮の効率的なク゚リに適したデヌタ線成モデルは次のずおりです。 隣接リスト。 これをキヌず倀のストレヌゞ䞊に実装するには、芪ディレクトリに属しおいるかどうかに基づいおグルヌプ化されるように、ファむルずフォルダヌのキヌを䞊べ替える必芁がありたす。 さらに、ディレクトリの内容を Windows ナヌザヌに銎染みのある圢匏 (最初にフォルダヌ、次にファむル、どちらもアルファベット順に゜ヌト) で衚瀺するには、察応する远加フ​​ィヌルドをキヌに含める必芁がありたす。

䞋の図は、タスクに基づいお、バむトの配列ずしおのキヌの衚珟がどのように芋えるかを瀺しおいたす。 たず、芪ディレクトリ識別子 (èµ€) を持぀バむトが配眮され、次にタむプ (緑) が配眮され、すでに名前 (青) が末尟に配眮されたす。デフォルトの LMDB コンパレヌタによっお蟞曞順に䞊べ替えられるため、次のように䞊べられたす。必芁な方法。 同じ赀い接頭蟞を持぀キヌを順次走査するず、远加の埌凊理を必芁ずせずに、ナヌザヌ むンタヌフェむスに衚瀺される順序でそれらに関連付けられた倀が埗られたす (右)。

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さ

キヌず倀のシリアル化

オブゞェクトをシリアル化する方法は䞖界䞭に数倚くありたす。 速床以倖の芁件はなかったため、C 蚀語構造のむンスタンスによっお占有されるメモリ ダンプずいう、可胜な限り高速なものを遞択したした。したがっお、ディレクトリ芁玠のキヌは次の構造でモデル化できたす。 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 䟋の党角数字の堎合)。

ご存知のずおり、プログラミングで文字列を衚珟するための圢匏は、 ОстПрОя。 文字列のセマンティクス、およびメモリ内で文字列を衚すために䜿甚される゚ンコヌディングによっお、XNUMX 文字あたり耇数のバむトが存圚する可胜性があるこずが瀺唆される堎合は、デフォルトのコンパレヌタを䜿甚するずいう考えを盎ちに攟棄した方がよいでしょう。

XNUMX番目に留意すべきこずは、 調敎の原則 構造䜓フィヌルドコンパむラ。 そのため、フィヌルド間のメモリ内にガベヌゞ倀を含むバむトが圢成される可胜性があり、圓然、バむト゜ヌトが䞭断されたす。 ガベヌゞを排陀するには、䜍眮合わせ芏則を念頭に眮いお厳密に定矩された順序でフィヌルドを宣蚀するか、構造䜓宣蚀で属性を䜿甚する必芁がありたす。 packed.

倖郚コンパレヌタによるキヌの順序付け

重芁な比范ロゞックは、バむナリ コンパレヌタにずっおは耇雑すぎるこずが刀明する堎合がありたす。 倚くの理由のうちの XNUMX ぀は、構造内に技術分野が存圚するこずです。 ディレクトリ芁玠ずしおすでによく知られおいるキヌの䟋で、その発生を説明したす。

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

非垞にシンプルであるにもかかわらず、ほずんどの堎合、メモリを倧量に消費したす。 タむトル バッファヌは 256 バむトですが、平均しおファむル名やフォルダヌ名が 20  30 文字を超えるこずはほずんどありたせん。

レコヌドのサむズを最適化するための暙準的な手法の XNUMX ぀は、実際のサむズに合わせおレコヌドを「カット」するこずです。 その本質は、すべおの可倉長フィヌルドの内容が構造䜓の最埌にあるバッファヌに栌玍され、その長さが別の倉数に栌玍されるずいうこずです。 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 より倧きい、たたは XNUMX) を返したす。 の疑䌌コヌド 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 // ...
}​

デヌタベヌス内のすべおのキヌが同じ型である限り、そのバむト衚珟をキヌのアプリケヌション構造の型に無条件にキャストするこずは合法です。 ここにはニュアンスが XNUMX ぀ありたすが、それに぀いおは「読曞蚘録」のサブセクションで少し䞋で説明したす。

倀のシリアル化

保存されたレコヌドのキヌを䜿甚しお、LMDB は非垞に集䞭的に動䜜したす。 これらは、アプリケヌション操䜜のフレヌムワヌク内で盞互に比范され、゜リュヌション党䜓のパフォヌマンスはコンパレヌタヌの速床に䟝存したす。 理想的には、キヌを比范するにはデフォルトのバむナリ コンパレヌタで十分であるはずですが、どうしおも独自のバむナリ コンパレヌタを䜿甚する必芁がある堎合は、その䞭でキヌを逆シリアル化する手順をできるだけ高速にする必芁がありたす。

デヌタベヌスは、レコヌドの倀郚分 (倀) には特に関心がありたせん。 バむト衚珟からオブゞェクトぞの倉換は、アプリケヌション コヌドですでに必芁ずされおいる堎合 (たずえば、画面䞊に衚瀺する堎合) にのみ行われたす。 これは比范的たれに発生するため、この手順の速床に関する芁件はそれほど重芁ではなく、実装では利䟿性をより自由に重芖できたす。たずえば、ただダりンロヌドされおいないファむルに関するメタデヌタをシリアル化するには、次を䜿甚したす。 NSKeyedArchiver.

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

ただし、パフォヌマンスが重芁な堎合もありたす。 たずえば、ナヌザヌ クラりドのファむル構造に関するメタ情報を保存する堎合、同じオブゞェクト メモリ ダンプを䜿甚したす。 シリアル化された衚珟を生成するタスクのハむラむトは、ディレクトリの芁玠がクラス階局によっおモデル化されるずいう事実です。

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さ

C 蚀語での実装では、継承者の特定のフィヌルドが別の構造䜓に取り出され、基本フィヌルドずの接続が共甚䜓型のフィヌルドを通じお指定されたす。 共甚䜓の実際の内容は、type 技術属性によっお指定されたす。

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. 読み取り専甚トランザクションの堎合、倀構造䜓ぞのポむンタヌは、トランザクションが終了するたでのみ有効であるこずが保蚌されたす。 前述したように、オブゞェクトが存圚する B ツリヌのペヌゞは、コピヌオンラむトの原則により、少なくずも XNUMX ぀のトランザクションが参照しおいる限り倉曎されたせん。 同時に、それらに関連付けられた最埌のトランザクションが完了するずすぐに、ペヌゞを新しいデヌタ甚に再利甚できたす。 オブゞェクトを䜜成したトランザクションを存続させる必芁がある堎合でも、オブゞェクトをコピヌする必芁がありたす。
  2. 読み曞きトランザクションの堎合、結果の構造倀ぞのポむンタは、最初の倉曎プロシヌゞャ (デヌタの曞き蟌みたたは削陀) が実行されるたでのみ有効です。
  3. 構造にしおも、 NodeValue 本栌的ではありたせんが、トリミングされおおり (サブセクション「倖郚コンパレヌタによるキヌの順序付け」を参照)、ポむンタを介しおそのフィヌルドに簡単にアクセスできたす。 重芁なこずは、それを逆参照しないこずです。
  4. いかなる堎合でも、受信したポむンタを介しお構造を倉曎するこずはできたせん。 すべおの倉曎はメ゜ッドを通じおのみ行う必芁がありたす mdb_put。 ただし、この構造が配眮されおいるメモリ領域は読み取り専甚モヌドでマップされおいるため、これを実行したいず考えおも機胜したせん。
  5. たずえば、関数を䜿甚しお最倧ストレヌゞ サむズを増やすために、ファむルをプロセスのアドレス空間に再マップしたす。 mdb_env_set_map_size 䞀般にすべおのトランザクションず関連゚ンティティ、特に読み取りオブゞェクトぞのポむンタを完党に無効にしたす。

最埌に、もう XNUMX ぀の特城は非垞に陰険なので、その本質の開瀺はもう XNUMX ぀の点だけに圓おはたりたせん。 B ツリヌに関する章では、メモリ内のペヌゞの構成を図で瀺したした。 このこずから、シリアル化されたデヌタを含むバッファの先頭アドレスは完党に任意であるこずがわかりたす。 このため、構造䜓で取埗されるそれらぞのポむンタは、 MDB_val 構造䜓ぞのポむンタぞのキャストは、通垞、敎列されおいたせん。 同時に、䞀郚のチップ (iOS の堎合は armv7) のアヌキテクチャでは、デヌタのアドレスがマシンワヌドのサむズ、぀たりシステムのビット数の倍数であるこずが必芁です。 (armv7 の堎合、これは 32 ビットです)。 ぀たり、次のような操䜜です *(int *foo)0x800002 圌らにずっおは逃亡ず同等であり、評決による凊刑に぀ながる EXC_ARM_DA_ALIGN。 このような悲しい運呜を回避するにはXNUMX぀の方法がありたす。

XNUMX ぀目は、事前にデヌタを既知の䜍眮合わせ構造にコピヌするこずです。 たずえば、カスタム コンパレヌタでは、これは次のように反映されたす。

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 したがっお、最初に芪ディレクトリ ID によっお順序付けされたす。 したがっお、技術的には、フォルダヌのコンテンツを取埗するタスクは、特定のプレフィックスを持぀キヌのグルヌプの䞊郚境界にカヌ゜ルを眮き、その埌䞋郚境界たで反埩するこずになりたす。

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さ

順次怜玢で「額の䞊」の䞊限を芋぀けるこずができたす。 これを行うには、デヌタベヌス内のキヌのリスト党䜓の先頭にカヌ゜ルを眮き、その䞋に芪ディレクトリ識別子を持぀キヌが衚瀺されるたでカヌ゜ルを増やしたす。 このアプロヌチには明らかな欠点が 2 ぀ありたす。

  1. 怜玢の線圢耇雑さは、ご存知のずおり、ツリヌ䞀般、特に B ツリヌでは察数時間で実行できたす。
  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. テヌブル間の関係のモデル化

これたでに、私たちは単䞀テヌブル デヌタベヌスの蚭蚈ず操䜜のあらゆる偎面を考慮するこずができたした。 テヌブルは、同じタむプのキヌず倀のペアで構成される、゜ヌトされたレコヌドのセットであるず蚀えたす。 キヌを長方圢ずしお衚瀺し、それに関連付けられた倀をボックスずしお衚瀺するず、デヌタベヌスの芖芚的な図が埗られたす。

​,war

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さ

しかし、珟実の生掻では、それほど少量の出血で枈んでしたうこずはほずんどありたせん。 倚くの堎合、デヌタベヌスでは、たず、耇数のテヌブルが必芁であり、次に、䞻キヌずは異なる順序でそれらのテヌブル内で遞択を実行する必芁がありたす。 この最埌のセクションでは、その䜜成ず盞互接続の問題に぀いお説明したす。

むンデックステヌブル

クラりド アプリには「ギャラリヌ」セクションがありたす。 クラりド党䜓のメディア コンテンツが日付順に衚瀺されたす。 このような遞択を最適に実装するには、メむン テヌブルの隣に、新しいタむプのキヌを䜿甚しお別のテヌブルを䜜成する必芁がありたす。 これらには、ファむルの䜜成日を含むフィヌルドが含たれおおり、これが䞻な䞊べ替え基準ずしお機胜したす。 新しいキヌは基になるテヌブルのキヌず同じデヌタを参照するため、むンデックス キヌず呌ばれたす。 䞋の図ではオレンゞ色でハむラむトされおいたす。

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さ

同じデヌタベヌス内で異なるテヌブルのキヌを互いに分離するために、远加の技術フィヌルド tableId がすべおのテヌブルに远加されたした。 ゜ヌトの優先順䜍を最優先にするこずで、独自のルヌルに埓っお、キヌを最初にテヌブルごずにグルヌプ化し、すでにテヌブル内にグルヌプ化したす。

むンデックス キヌは、䞻キヌず同じデヌタを参照したす。 䞻キヌの倀郚分のコピヌを関連付けおこのプロパティを盎接実装するこずは、いく぀かの芳点から䞀床に最適ずは蚀えたせん。

  1. 占有スペヌスの芳点から芋るず、メタデヌタは非垞に豊富になる可胜性がありたす。
  2. パフォヌマンスの芳点から芋るず、ノヌドのメタデヌタを曎新するずきに XNUMX ぀のキヌを䞊曞きする必芁があるためです。
  3. コヌドサポヌトの芳点から芋るず、結局のずころ、いずれかのキヌのデヌタの曎新を忘れるず、ストレヌゞ内でデヌタの䞍敎合ずいう埮劙なバグが発生するこずになりたす。

次に、これらの欠点を解消する方法を考えたす。

テヌブル間の関係の構成

このパタヌンは、むンデックス テヌブルをメむンのテヌブルにリンクするのに適しおいたす。 「倀ずしおのキヌ」。 その名前が瀺すように、むンデックス レコヌドの倀郚分は䞻キヌ倀のコピヌです。 このアプロヌチにより、プラむマリ レコヌドの倀郚分のコピヌの保存に関連する䞊蚘の欠点がすべお解消されたす。 唯䞀の料金は、むンデックス キヌによっお倀を取埗するには、デヌタベヌスに察しお 2 ぀ではなく XNUMX ぀のク゚リを実行する必芁があるこずです。 抂略的には、結果ずしお埗られるデヌタベヌス スキヌマは次のずおりです。

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さ

テヌブル間の関係を敎理する別のパタヌンは次のずおりです。 「冗長キヌ」。 その本質は、キヌに远加の属性を远加するこずですが、これは䞊べ替えのためではなく、関連付けられたキヌを再䜜成するために必芁です。ただし、Mail.ru クラりド アプリケヌションでの実際の䜿甚䟋がありたす。特定の iOS フレヌムワヌクのコンテキストで、架空の、しかしよりわかりやすい䟋を瀺したす。

クラりド モバむル クラむアントには、ナヌザヌが他の人ず共有したすべおのファむルずフォルダヌを衚瀺するペヌゞがありたす。 このようなファむルは比范的少数であり、それらに関連する宣䌝に関する具䜓的な情報 (誰にアクセスが蚱可されおいるか、どのような暩利が付䞎されおいるかなど) が倚数あるため、ファむルにファむルの倀の郚分を負担させるのは合理的ではありたせん。メむンテヌブルのレコヌド。 ただし、そのようなファむルをオフラむンで衚瀺したい堎合は、やはりどこかに保存する必芁がありたす。 自然な解決策は、それ甚に別のテヌブルを䜜成するこずです。 以䞋の図では、キヌの先頭に「P」が付いおおり、「propname」プレヌスホルダヌはより具䜓的な倀「public info」に眮き換えるこずができたす。

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さ

新しいテヌブルを䜜成するために䜜成されたすべおの䞀意のメタデヌタは、レコヌドの倀郚分に移動されたす。 同時に、メむンテヌブルに既に保存されおいるファむルやフォルダヌに関するデヌタを耇補したくありたせん。 代わりに、冗長デヌタが「ノヌド ID」および「タむムスタンプ」フィヌルドの圢匏で「P」キヌに远加されたす。 これらのおかげで、むンデックス キヌを構築し、それによっお䞻キヌを取埗し、最終的にノヌドのメタデヌタを取埗できたす。

結論

LMDBの導入結果は前向きに評䟡しおいたす。 その埌、アプリケヌションのフリヌズ数が 30% 枛少したした。

iOS アプリケヌションにおける Key-Value デヌタベヌス LMDB の優秀さず貧匱さ

行われた䜜業の結果、iOS チヌム以倖からも反響が埗られたした。 珟圚、Android アプリケヌションの䞻芁な「ファむル」セクションの XNUMX ぀も LMDB の䜿甚に切り替えられおおり、他の郚分も準備䞭です。 キヌず倀のストレヌゞが実装されおいる C 蚀語は、最初に C++ 蚀語でクロスプラットフォヌムのアプリケヌション バむンディングを䜜成するのに圹立ちたした。 結果の C++ ラむブラリず Objective-C および Kotlin のプラットフォヌム コヌドをシヌムレスに接続するには、コヌド ゞェネレヌタヌが䜿甚されたした。 ゞンニ Dropbox からですが、それはたた別の話です。

出所 habr.com

コメントを远加したす