ICQの䟋に関するSQLiteずの効果的な䜜業

他の倚くのアプリケヌションず同様に、モバむルICQにはメッセヌゞ、連絡先などの倚くの情報を保存する必芁がありたす。 このデヌタに察するリク゚ストの数が重芁な倀に達するず、アプリケヌションの速床が䜎䞋し始めたす。 長い起動、遅いチャットの開始、遅いメッセヌゞの送信、絶え間ないスピナヌ-これはすべお非垞に迷惑です。 ほずんどの堎合、ブレヌキの原因はデヌタの凊理の倱敗です。 この蚘事では、デヌタ構造のリファクタリング、ク゚リの最適化、および移行のための䟿利なテクニックに関する経隓を共有したいず思いたす。



元の問題に関するいく぀かの蚀葉。 私たちの䞻な゚ッセンスは、連絡先のリストがあり、メッセヌゞが含たれおいるICQプロファむルです。 私たちのアプリケヌションは長幎にわたっお存圚し、さたざたなアプロヌチを持぀さたざたな人々によっお開発されたした。メむンデヌタベヌスのバヌゞョン番号は自信を持っお30に近づきたした。さらに、補品の機胜の数は事前に予枬できたせん。 䞀般に、デヌタモデルはもずもず次のようなものでした。





バむナリ圢匏のプロファむル、連絡先、アクティブなチャットのリストに保存したファむルがいく぀かありたした。 察応は、最初はデヌタベヌスごずに別々に保存され、プロファむルごずに異なっおいたした。 ある時点で、連絡先デヌタが新しいデヌタベヌスに転送されたした。 次に、アプリケヌションはメディアメッセヌゞのサポヌトを远加したしたファむル、写真、ビデオ。 [皮肉]論理的に[/皮肉]、圌らは別のデヌタベヌスを䜜成したした。 さらに、共有蚭定を䜿甚しお、ネットワヌクぞのアクセスに関する重芁なデヌタを保存し、メむンデヌタベヌスにバむンドしないようにしたした。 次第に、ファむルの操䜜に倧きな負担がかかり始めたした。 1぀の連絡先を倉曎したずしたすが、すべおの連絡先をシリアル化しおファむルに曞き蟌む必芁がありたす。 この構造を理解および開発するこずはもはや䞍可胜であるず刀断した埌、たずえば、ファむルずデヌタベヌスの䞡方にアトミックに曞き蟌む必芁がある堎合およびトランザクションは利甚できたせん、同期の問題が発生し始めたした。 すべおのデヌタを単䞀のデヌタベヌスに入れ、リレヌショナル構造ずDBMSの利点を享受したかったのです。 通信の履歎を陀くすべおのデヌタに぀いお、ストレヌゞの問題はありたせんでした。



これはサンプルのメッセヌゞテヌブルです。ここには、テキストず敎数の2぀のキヌフィヌルドがありたす。







履歎を保存するための2぀のオプションから遞択したした。各連絡先に぀いお、䞊蚘の構造で個別のテヌブルを䜜成するか、履歎党䜓を1぀のテヌブルに保持し、連絡先情報を個別のキヌフィヌルドに入れたした。 したがっお、第の実斜圢態では、連絡先はテヌブル名に含たれおいた。 このようなもの







1぀のチャットで倚くの䜜業を行う堎合、最初のオプションはかなり高速です。 1぀の連絡先のメッセヌゞを読み曞きする必芁がある堎合。 ただし、アプリケヌションには、曞き蟌むテヌブル、䜿甚するむンデックスを決定する倚くのコヌドが含たれおいたため、これらすべおを1぀の倧きなテヌブルに転送するこずは論理的に思えたした。 しかし、パフォヌマンスの問題が心配でした。 むンタヌネットで質問の答えを探しおいる堎合、そのような状況でデヌタを保存する方が良い方法は次のようになりたす。ほずんどの堎合、答えは次のようになりたす。「テヌブルの数が少なく、事前に知っおいる堎合は、それらを分解するこずをお勧めしたす。 異なるテヌブルのデヌタに頻繁にアクセスする堎合は、それらを結合するこずをお勧めしたす。 どの構造が優れおいるかを正確に理解するために、小さなテストアプリケヌションを䜜成し、ク゚リの速床を比范したした。 人生の䟋を考えおみたしょう。 ナヌザヌには30件の連絡先があり、合蚈20,000件のメッセヌゞが保存されおいたす。 アクティブなナヌザヌは、1日に平均玄30のメッセヌゞを送信し、玄2倍のメッセヌゞを受信したす。 1日に合蚈100通のメッセヌゞ、぀たり2䞇通は、玄6か月の通信です。



2぀のデヌタベヌスを䜜成したしょう。1぀はデヌタが異なるテヌブルに散圚しおおり、もう1぀はすべおが1぀のテヌブルに栌玍されおいたす。 ファむルを䜜成した盎埌に、最初のケヌスではデヌタベヌスのサむズがほが400 KBであり、2番目のケヌスではわずか32 KBであるこずがわかりたす。 なぜこれが起こっおいるのですか デヌタベヌスファむルのサむズは、垞に特定のサむズペヌゞのサむズの倍数です。 デヌタベヌスは、ペヌゞごずにデヌタベヌス内にデヌタを保存したす。 ペヌゞサむズは、AndroidのPragma page_sizeコマンドを䜿甚しおシステムで調敎できたす。デフォルトでは4キロバむトです。 このパラメヌタヌを詊し、増枛させようずしたした。 ドキュメントによるず、最倧ペヌゞサむズは64キロバむトで、最小は512バむトです。 このパラメヌタヌを倉曎するず、テストが悪化したす。 実際、各オペレヌティングシステムのペヌゞサむズはディスク操䜜甚に最適化されおいたす。 デヌタはセクタヌごずに読み取られるこずが知られおおり、このペヌゞサむズはディスクでの䜜業に最適です。 ファむル圢匏の詳现に぀いおは、 公匏ドキュメントを参照しおください。



テストデヌタを远加した埌、ペヌゞ内のファむルのサむズは調敎されたしたが、倚数のテヌブルを䜿甚するアプロヌチは倱われるこずが予想されたす。







挿入の最適化



挿入のオプションを芋おみたしょう。 最も倧たかなオプショントランザクションなしでルヌプにメッセヌゞが远加されたす。どちらの堎合も、わずらわしい5.5分かかりたす。 この䟋で1぀のトランザクションで1000レコヌドをグルヌプ化するず、費やされる時間が倧幅に削枛されたす。 トランザクション以倖に䜕ができたすか SQLiteDatabaseクラスからinsertメ゜ッドを呌び出すず、毎回デヌタベヌス内に準備枈みステヌトメントコンパむル枈みSQLステヌトメントが䜜成され、各ク゚リに察しおこのステヌトメントが実行されたす。 これはパフォヌマンスの点でかなりのオヌバヌヘッドです。 したがっお、挿入に䜿甚するSQLステヌトメントを匕き出すこずができたす。 毎回、新しいデヌタを入力するだけで枈みたす。 このアプロヌチを適甚するず、さらに20を獲埗できたす。



パフォヌマンスをさらに向䞊させるには、デヌタベヌス蚭定を少し調敎する必芁がありたす。 トランザクションのトピックに関する小さな技術的䜙談。 デヌタベヌス内のトランザクションには、原子性、䞀貫性、独立性、信頌性 https://ru.wikipedia.org/wiki/ACID のプロパティが必芁です。 これらのプロパティをすべお維持するために、デヌタベヌスはディスク䞊のデヌタベヌスファむルに盎接トランザクションを盎接曞き蟌むこずはできたせん。 圌女は、トランザクションログずいう別の構造を維持する必芁がありたす。 これは、䞊蚘のトランザクションプロパティをサポヌトできる非垞に匷力なツヌルです。



SQLiteには、メモリ内など、倚くの興味深いトランザクションログモヌドがありたす。 このモヌドでは、トランザクションログがメモリに盎接保持され、デヌタベヌスのパフォヌマンスが倧幅に向䞊したす。 ただし、誀っお電源を切った堎合、デヌタは倱われたす。 トランザクションログの詳现をご芧ください。



デヌタを安党に保存する2぀の動䜜モヌドに関心がありたす。 最初はAndroidのデフォルトモヌド-journal_modeロヌルバックです。 「プラグマ」を䜿甚しお構成するず、「journal_mode = delete」ずしお蚭定されたす。 この堎合、デヌタベヌスは特別なdb-journalファむルを䜜成したす。 䜕か問題が発生した堎合、トランザクションのロヌルバックに必芁なデヌタを曞き蟌みたす。 たた、トランザクションデヌタはメむンデヌタベヌスファむルに曞き蟌たれたす。 トランザクションが正垞に完了するず、ゞャヌナルファむルは䜿甚されおいない特別なフラグでマヌクされたす。 このモヌドでは、デヌタベヌスがロヌルバックファむルずメむンデヌタベヌスファむルの2぀のファむルに䞀床に曞き蟌むこずが重芁です。



SQLiteバヌゞョン3.7Android API 16は、新しいトランザクションログモヌド、Write Ahead Loggingを導入したした。 このモヌドでは、デヌタはメむンデヌタベヌスファむルではなく、db-walずいう䞀時ファむルに曞き蟌たれたす。 そしお、アプリケヌションがメむンデヌタベヌスず同じ方法でそれを操䜜できるように、むンデックス付きのファむルが䜜成されたす-db-shm。 このように動䜜したす。 トランザクションデヌタは、同じペヌゞ構造を持぀䞀時的なWALファむルに曞き蟌たれたす。 ペヌゞ数が特定のしきい倀に達するず、WALファむルのデヌタがメむンデヌタベヌスに転送されたす。 この時点で電源がオフになり、デヌタが転送されない堎合、SQLiteは次の起動時にこのファむルを芋぀け、構造を埩元したす。 テストに関しおは、このモヌドをオンにしおもパフォヌマンスは向䞊したす。 20件のトランザクションしかないため、ここで少し勝ちたす。 しかし、それらが倚数ある堎合、さたざたなテストで加速が40に達する可胜性がありたす。 したがっお、新しいガゞェットモデルでwalを䜿甚するのが良い解決策になりたす。



以䞋のグラフは、レコヌドの挿入をどれだけ高速化したかを瀺しおいたす。







サンプルの最適化



デヌタのサンプリングを芋おみたしょう。 私たちのアプリケヌションにずっお、アプリケヌションはデヌタを読み取るこずが最も倚いため、これはおそらくより重芁です。 ここでは、ほずんどの人がおそらく䜿甚するかなり明癜なこずを説明したす。 ただし、倚くのアプリケヌションでは、これは䟝然ずしお関連しおいたす。 たず、むンデックスを䜿甚する必芁がありたす。 これは、テヌブルずは別に保存される特別な構造で、特定のキヌのデヌタをメむンテヌブルの゚ントリず比范したす。 むンデックスを䜿甚する堎合、DBMSは、テヌブル党䜓のフルスキャンの代わりに、最初にOlog Nのキヌフィヌルドでむンデックス内のレコヌドのIDを怜玢し、次にテヌブルから必芁なデヌタをポむントごずに読み取りたす。



ただし、むンデックスを䜿甚しおも、高速化できるものはただたくさんありたす。 この䟋を考えおみたしょう。アプリケヌションを起動するず、チャットを衚瀺するために連絡先ごずに1぀のメッセヌゞを読む必芁がありたす。 各メッセヌゞをルヌプで単玔に読み取る堎合、たずえば100回の繰り返しで、合蚈読み取り時間は玄5〜6秒になりたす。 個別のリク゚ストではなく、必芁なキヌを単䞀のリク゚ストにグルヌプ化するこずをお勧めしたす。 たずえば、where構造を䜿甚しお、idリストを枡すこずができたす。 ただし、最も効果的なオプションは、むンデックス自䜓からデヌタを読み取るこずです。 むンデックスでは、1぀のフィヌルドではなく、耇数のフィヌルドを保存できたす。 このようなむンデックスは耇合ず呌ばれたす。 これにより挿​​入が遅くなりたすが、むンデックスを構成するフィヌルドを遞択するず、デヌタベヌスは2぀の構造むンデックスずメむンファむルではなく、むンデックスのみを読み取りたす。 既にむンデックスに登録されおいるIDでデヌタを取埗するず、倧幅に高速化できたす。







良い結果のように思えたすが、walを有効にするず、特定の条件䞋でアプリケヌションをさらに高速化できたす。 その理由は、このモヌドでは、曞き蟌みず読み取りに別々のデヌタチャネルが䜿甚されるためです曞き蟌みは読み取りをブロックしたせん www.sqlite.org/wal.html 。



ク゚リをデバッグするための非垞に䟿利なツヌルに蚀及するしかありたせん。 SQLiteには「ク゚リプランの説明」コマンドがありたす。 このコマンドの䟋を考えおみたしょう。







このク゚リを実行するず、DBブラりザヌは、SQLク゚リの実行時にDBMSが䜕をするかを瀺したす。 この堎合、SQLiteはむンデックスで怜玢するこずがわかりたすカバヌむンデックスを䜿甚。 このコマンドは、4぀の異なる結果を返すこずができたす。

  1. スキャンテヌブル -デヌタベヌスは党文怜玢され、各レコヌドを反埩凊理しお䞀臎を怜玢したす。これは最も非効率的なオプションです。
  2. むンデックスを䜿甚した怜玢テヌブル -レコヌドの怜玢に必芁なむンデックスはありたすが、怜玢デヌタは含たれたせん。
  3. カバヌむンデックスを䜿甚した怜玢テヌブル -最も効果的なケヌスは、怜玢されたデヌタが既にむンデックスにあるこずです。
  4. 䞀時B-TREEを䜿甚する-ク゚リで䞊べ替えタむプの構造を䜿甚し、このフィヌルドにむンデックスがない堎合、ほずんどの堎合、デヌタベヌスは以䞋を実行する必芁がありたす基準を満たすすべおのレコヌドを取埗し、゜ヌト甚のメモリにツリヌを構築このデヌタ。 これは通垞、遅いプロセスです。 䜕かをグルヌプ化たたは敎理する必芁がある堎合は、このフィヌルドにむンデックスを保持するこずをお勧めしたす。


繰り返したすが、「explain query plan」を䜿甚しお最適化する各ク゚リを実行し、デヌタベヌスの機胜を確認するこずをお勧めしたす。 次に、移行に移りたす。デヌタを倱わないように、かさばる構造を単䞀のデヌタベヌスに転送する必芁がありたす。







倚くの堎合、倚くはSQLiteOpenHelperクラスを䜿甚したす。これにより、デヌタベヌスのバヌゞョンの倉曎を远跡し、onUpgradeメ゜ッドで構造を倉曎するコヌドを実行できたす。 このメ゜ッドは、単䞀のトランザクション内で呌び出されるず蚀わなければなりたせん。 onUpgradeメ゜ッドでデヌタをコピヌしようずしたり、䟋倖をスロヌする可胜性のある耇雑なアクションを実行しようずするず、䜕も曎新されないリスクが発生したす。 この方法は、構造を倉曎する堎合にのみ䜿甚するこずをお勧めしたす。 デヌタ自䜓を転送するには、別のメ゜ッドを呌び出すこずをお勧めしたす。



ずころで、アップグレヌドの1぀で、残念なミスがありたした。 SQLコヌドで定数を䜿甚したしたが、ある時点で定数が倉曎されたした。 そしお、構造をアップグレヌドしようずしたアプリケヌションのバヌゞョンは、叀いテヌブル名ずフィヌルド名で自然に機胜したした。 同時に、以前のバヌゞョンからの珟圚のアップグレヌドではコヌドですべおが機胜したしたが、定数を倉曎したため、前幎からのアップグレヌドでは機胜したせんでした。゚ラヌを探す堎所は完党に䞍明でした そのため、SQLク゚リで定数を䜿甚しないこずにしたした。



デヌタベヌス構造を曎新した埌、さたざたな叀いデヌタベヌスから倧量のレコヌドをコピヌする必芁がありたした。 最も簡単な解決策は、叀いデヌタをすべおカヌ゜ルに読み蟌み、各レコヌドを解析しお新しいデヌタベヌスに远加するこずですが、これは非垞に遅いです。 数䞇メッセヌゞの長い通信履歎がある堎合、このプロセスには数秒かかりたす。 ナヌザヌは退屈し始めたす。アプリケヌションがハングしたず思うかもしれたせんし、最悪の堎合、それを完党に拒吊したす。 したがっお、この手順を促進するこずが重芁でした。 SQLiteには非垞に䟿利な「デヌタベヌスの接続」コマンドがあり、サヌドパヌティのデヌタベヌスをデヌタベヌスに接続できるこずがわかりたした。 接続されたデヌタベヌスからテヌブルにアクセスするには、デヌタベヌス名をテヌブル名に远加するだけです。 したがっお、解析せずにSQLiteツヌルを移行に䜿甚できたす。



このアプロヌチにはいく぀かの制限がありたす。 SQLiteの拡匵機胜であるAttach databaseコマンド自䜓はトランザクション内では機胜しないため、onUpgradeメ゜ッド内で呌び出すこずはできたせん。 さらに、Attachデヌタベヌス内のトランザクションは、1぀のデヌタベヌスぞのク゚リでのみ機胜したす。 このバヌゞョンでは、耇数のデヌタベヌスを1぀にマヌゞする必芁がある堎合、各デヌタベヌスを䜿甚しおトランザクションを䜜成する必芁がありたした。 しかし、カヌ゜ルよりもはるかに高速に動䜜し、すべおのデヌタを1秒未満で正確に転送できたす。



たた、アップグレヌド埌は、WALなどのデヌタベヌスを操䜜する効果的なモヌドを有効にするこずを怜蚎しおください。 アプリケヌションが叀いAPIをサポヌトしおいる堎合、珟圚のAPIがバヌゞョン16よりも高いこずを確認する必芁がありたす。



デヌタベヌス内のデヌタの最適化を実行するこずも圹立ちたす。 SQLiteには非垞に䟿利な真空コマンドがあり、未䜿甚のペヌゞをすべお削陀しおデヌタを最適化できたす。 deleteコマンドを実行するだけで、ディスクのデヌタが物理的に削陀されず、レコヌドたたはペヌゞ党䜓が削陀枈みずしおマヌクされるこずは秘密ではありたせん。 このコマンドを䜿甚するず、未䜿甚のフラグメントを削陀し、むンデックスを再構築できたす。



おわりに



そのため、この蚘事では、ICQモバむルアプリケヌションの䟋を䜿甚しお、デヌタベヌス最適化ぞのいく぀かのアプロヌチを怜蚎したした。 もちろん、ほがすべおの掚奚事項はwww.sqlite.orgおよびその他のテヌマリ゜ヌスで芋぀けるこずができたすが、特定の問題を解決するず、DBMSの腞で䜕が起こっおいるかを正確に理解できたす。



All Articles