31

大量のデータ(「顧客」、「製品」、「注文」など)を含むAndroidアプリを設計しており、レコードが必要になるたびにSQLiteにクエリを実行する必要はありません。データベースへのクエリはできるだけ避けたいので、特定のデータを常にメモリに保持することにしました。

私たちの最初のアイデアは、2つの単純なクラスを作成することです。

  1. 「MemoryRecord」:基本的にオブジェクトの配列(string、int、double、datetimeなど)、テーブルレコードからのデータ、およびこれらのデータをこれから出し入れするためのすべてのメソッドを含むクラス。配列。

  2. 「MemoryTable」:基本的に[Key、MemoryRecord]のマップと、このマップを操作し、データベースにレコードを挿入/更新/削除するためのすべてのメソッドを含むクラス。

これらのクラスは、データベースにあるあらゆる種類のテーブルから派生します。もちろん、上記にリストされていない他の便利な方法もありますが、現時点では重要ではありません。

そのため、アプリを起動するときに、これらのクラスを使用してSQLiteデータベースからメモリにこれらのテーブルをロードし、データを変更する必要があるたびに、メモリを変更してすぐにデータベースに投稿します。

しかし、私たちはあなたからの助け/アドバイスを求めています。そのようなことを実装するために、より単純または効率的な何かを提案できますか?それとも、すでに私たちのためにそれを行っているいくつかの既存のクラスですか?

私はあなたたちが私に見せようとしていることを理解しています、そして私はそれをありがとうございます。

しかし、2000レコードのテーブルがあり、それらのレコードをリストする必要があるとしましょう。それぞれについて、他の30個のテーブル(1000レコードのテーブルと10レコードのテーブル)をクエリして、リストに追加情報を追加する必要があります。これは、「飛行中」です(ご存知のとおり、非常に高速である必要があります)。現時点で)。

ここで、「これらすべての「結合」を使用してメインクエリを作成し、必要なものをすべて1つのステップにまとめるだけです。データベースが適切に設計されている場合など、SQLiteは非常に高速になります...」と言います。

OK、しかしこのクエリは非常に複雑で確実になりますが、SQLiteは非常に高速ですが、「遅すぎる」でしょう(私が確認したように、2〜4秒で、これは私たちにとって許容できる時間ではありません)。

もう1つの複雑な点は、ユーザーの操作に応じて、関連するテーブルが同じではないため、すべてのレコードを「再クエリ」する必要があり、別のテーブルのセットに「再結合」する必要があることです。

したがって、代替手段は、結合なしでメインレコードのみを取得し(これは、ユーザーが何をしたり、望んでいるかに関係なく)、データが必要になるたびに他のテーブルにクエリを実行することです。10レコードしかないテーブルでは、同じレコードを何度もフェッチすることに注意してください。この場合、SQLiteが高速であっても、一種の「メモリキャッシュ」からレコードを取得するよりも、クエリ、カーソル、フェッチなどのコストが常に高くなるため、時間の無駄になります。すべてのデータを常にメモリに保持するのではなく、頻繁にクエリを実行する一部のテーブルだけを保持する予定であることを明確にしておきます。

そして、最初の質問に行き着きました。これらのレコードを「キャッシュ」するための最良の方法は何ですか?「なぜデータをキャッシュする必要があるのか​​」ではなく、それに焦点を当てて議論するのが本当に好きです。

4

2 に答える 2

69

プラットフォーム上のアプリの大部分(連絡先、メール、Gmail、カレンダーなど)はこれを行いません。これらのいくつかは、潜在的に大量のデータを含む非常に複雑なデータベーススキーマを持っており、これを行う必要はありません。あなたが提案していることは、明確な利益なしに、あなたに大きな苦痛をもたらすでしょう。

最初に、効率的なクエリを実行できるようにデータベースとスキーマの設計に集中する必要があります。データベースアクセスが遅いと私が考えることができる主な理由は2つあります。

  • あなたは本当に複雑なデータスキーマを持っています。
  • 非常に大量のデータがあります。

大量のデータを処理する場合、とにかくすべてをメモリに保持する余裕はないため、これは行き止まりです。複雑な構造がある場合は、どちらの場合も、パフォーマンスを向上させるためにそれらを最適化することでメリットが得られます。どちらの場合も、データベーススキーマが優れたパフォーマンスの鍵となります。

実際にスキーマを最適化することは少し難しいかもしれませんが(私はそれについての専門家ではありません)、注意すべき点は、クエリする行にインデックスを正しく作成したり、効率的なパスをとるように結合を設計したりすることです。 。この分野であなたを助けることができる人はたくさんいると確信しています。

また、プラットフォームのデータベースのいくつかのソースを調べて、優れたパフォーマンスを実現するための設計方法のアイデアを得ることができます。たとえば、Contactsデータベース(特に2.0以降)は非常に複雑で、さまざまな種類のクエリを含む比較的大きなデータや拡張可能なデータセットで優れたパフォーマンスを提供するために多くの最適化が行われています。

アップデート:

これは、データベースの最適化がいかに重要であるかを示す良い例です。Androidのメディアプロバイダーデータベースでは、プラットフォームの新しいバージョンでスキーマが大幅に変更され、いくつかの新機能が追加されました。既存のメディアデータベースを新しいスキーマに変更するためのアップグレードコードは、実行に8分以上かかる場合があります。

エンジニアは、実際のテストデータベースのアップグレード時間を8分から8秒に短縮する最適化を行いました。60倍のパフォーマンスの向上。

この最適化は何でしたか?

アップグレードの時点で、アップグレード操作で使用される重要な列に一時的なインデックスを作成することでした。(そして、完了したら削除します。)したがって、アップグレード中に使用される列の1つにインデックスを作成するために必要な時間も含まれていても、この60倍のパフォーマンスの向上がもたらされます。

SQLiteは、自分が何をしているのかを知っていれば、非常に効率的である可能性があるものの1つです。また、使い方に注意を払わないと、ひどいパフォーマンスになってしまう可能性があります。ただし、パフォーマンスの問題が発生している場合は、SQLiteの使用方法を改善することで修正できるので安全です。

于 2010-09-10T01:53:36.250 に答える
5

もちろん、メモリキャッシュの問題は、データベースとの同期を維持する必要があることです。データベースのクエリは実際には非常に高速であることがわかりました。ここで事前に最適化されている可能性があります。さまざまなデータセットを使用したクエリで多くのテストを実行しましたが、10〜20ミリ秒以上かかることはありません。

もちろん、それはすべてデータの使用方法によって異なります。ListViewsは、多数の行を処理するために非常によく最適化されています(5000の範囲でテストしましたが、実際の問題はありません)。

メモリキャッシュを使用する場合は、コンテンツが変更されたときにデータベースにキャッシュを通知させてから、キャッシュを更新することができます。そうすれば、誰でもキャッシングについて知らなくてもデータベースを更新できます。また、データベース上にContentProviderを構築する場合、registerContentObserverを使用して登録すると、ContentResolverを使用して変更を通知できます。

于 2010-09-09T19:12:09.453 に答える