Androidデータベースの設計方法

Android開発者として、モバイルアプリケーションでデータベースを設計するには2つの異なるアプローチに直面しなければなりませんでした。 おそらく、ここで提示された声明は誰かに明白に見えるかもしれませんし、あるいは誰かに新しいアイデアを与えたり、間違いからそれらを救うかもしれません。 一般的に、長い紹介なしでビジネスに取りかかります...



問題に関する2つの見解



ご存じのように、大学はすべてのルールに従ってデータベースを構築することを教えています:サブジェクトエリアをエンティティに分解し、属性を強調表示し、主キーを定義し、エンティティ間の関係を定義し、これらすべてを少なくとも3つの標準形式にするなど。 このアプローチの「副作用」の1つは、クエリでより多くの結合が必要になるため、かなり強力な分解と正規化による読み取り操作のパフォーマンスの低下です。 また、テーブルにあるレコードが多いほど、それらは長く続きます。



ここでは、モバイルプラットフォームの非常に限られたハードウェア機能、特にごく少量のRAMを追加します。 すでに十分ではないため、これに加えて、AndroidはOSバージョンに応じてプロセスごとに利用可能なRAMの量を16〜48 MBに制限します。 また、これらの数メガバイトのうち、アプリケーション自体も存在するため、DBMSはほんの一部しか取得しません。 結論として、SQLite自体は、その機能を考慮して、2レベルのトランザクション分離のみをサポートしています。 それらはシリアル化されるか、完全に無効になります!



アプリケーションのパフォーマンスがDBMSのパフォーマンスに依存し始めており、代わりのアプローチが来るかもしれない状況では、キーバリュー指向と呼びましょう。 エンティティを属性に分解し、各属性のテーブルに個別のフィールドを作成する代わりに、エンティティはBLOB型の1つの単一フィールドに「そのまま」保存されます。つまり、シリアル化されます。



完全に明確にするための例を考えてみましょう。 Javaコードのデータモデルを次のようにします。

class Group { private Long _id; private String number; private List<Student> students; // getters and setters ... } class Student { private Long _id; private String name; private String surname; private Group group; // getters and setters ... }
      
      





したがって、「標準」バージョンでは、対応する属性セットを持つ2つのテーブルを取得します。

 create table Group( _id primary key integer autoincrement, number text); create table Student( _id primary key integer autoincrement, name text, surname text, group_id integer foreign key);
      
      





このプロジェクトでは、さらに多くのエンティティと属性があり、さらに、サーバーまたはフラグフラグとの最後の同期の日付、変更されたデータを更新するためにエンティティをサーバーに送信するかどうかなど、さまざまなサービスフィールドがここに追加されます。



キーと値のアプローチを適用すると、テーブルは次のようになります

 create table Group( _id primary key integer autoincrement, value blob); create table Student( _id primary key integer autoincrement, value blob, group_id integer foreign key);
      
      





同時に、グループと生徒は異なるテーブルに従って別々にシリアル化されます。 または一般的に次のように:

 create table Group( _id primary key integer autoincrement, value blob);
      
      





グループが1つのテーブルのすべての生徒と直接シリアル化されている場合。

両方のアプローチの長所と短所、およびこれから得られるメリットを検討してください。



アプローチ、賛否両論の比較



リレーショナル代数の特徴


標準的なアプローチを使用すると、リレーショナルアプローチを使用することに慣れているすべての利点が得られます。つまり、データの選択、フィルタリング、並べ替え、データベーススキーマの変更に便利なSQL言語です。 エンティティのコレクションを取得するには、必要な条件を形成し、データベースからデータを取得するだけです。 キーバリューアプローチでは、データのフィルタリングまたは整理のタスクは開発者にあります。



DBファイルサイズ


標準的なアプローチを使用する場合、通常、データベースファイルは小さくなります。 これは、正規化の結果として、データストレージ中の冗長性が不足しているためです。 理論的には、正規化の度合いが高いほど冗長性は低くなりますが、このデータを読み取る際のデータベースの負荷は増加します。 大量のリソースがテーブル結合に費やされます。 キーと値のアプローチを使用する場合、データの冗長性の程度は高くなります。これは、通常、正規化のレベルがはるかに低く、データベースファイルサイズが大きくなるためです。



データベーススキーマを変更する際の柔軟性


通常、プロジェクトの開発では、データベーススキーマが複数回変換され、新しいフィールドが追加され、以前に使用されたフィールドが削除され、エンティティをいくつかの新しいものに分割するか、逆に非正規化して複数のテーブルを1つに結合できます。 スキームを更新するときに、データベースに蓄積されたデータを犠牲にすることができれば、すべてが簡単です。スキームを更新するたびに新しいデータベースファイルを作成し、古いものを削除します。 しかし、データを保存して新しい形式に変換する必要がある場合はどうでしょうか?

この実施形態では、標準的なアプローチには利点がある。 データベーススキーマを必要な形式に変換し、新しいフィールドをデフォルト値で更新するか、1つまたは別のロジックを使用して計算する適切な更新スクリプトを作成するだけで十分です。 シリアル化を使用する場合、データベーススキーマの更新はそれほど簡単な作業ではなくなりました。 すべてのデータを保存してスキームを変換し、データ自体を更新し、それらを希望し、新しいフィールドを初期化し、シリアル化して戻す必要があります。 操作の論理的な複雑さ、および更新に必要な時間は増加しています。



エンティティインスタンスへのアクセスの同期


キーと値のアプローチの主な欠点の1つは、本質的に1つのフィールドのみを変更するために、オブジェクト全体をデシリアライズする必要があることです。 これにより、オブジェクトへのアクセスが非常に複雑になります。 たとえば、グループがすべての学生とともにデータベースにシリアル化されている場合、学生の一人の名前を変更するには、データベースからグループ全体を削除し、名前を1つ変更して保存する必要があります。 アプリケーションに同じエンティティで動作できる複数のストリーム、サービス、および/またはコンテンツプロバイダーがある場合、タスクは何倍も複雑です。 「ライター」の可能性が高いほど、ロックが発生し、オブジェクトへのアクセスを同期するのが難しくなります。 標準的なアプローチの場合、この問題はDBMSレベルで解決されます。



性能


一方では、キーと値のアプローチにより、少量のデータをフェッチする際のパフォーマンスが向上します。 結合の数が減り、特定のリクエストとDBMS全体が高速になります。 一方、大量のデータの場合、オブジェクト全体とともにシリアル化されたフィールドでこのデータをフィルター処理または並べ替える必要がある場合、この操作では最初にすべてのエンティティを読み取り、次にすべての過剰をフィルター処理する必要がありますパフォーマンスを得るためではなく、さらに悪化するためです。 あるいは、標準的なアプローチでフィルタリングまたはソート要求に関連するフィールドを保存し、エンティティの残りをBLOBaとして保存することもできますが、そのような混乱を維持するのは困難です。



コード量


標準的なアプローチでは、SQLコードの量、データベーススキーマを作成および変更するためのさまざまなスクリプト、クエリと条件、DAOオブジェクトなどが増加します。 Key-Valueでは、このようなコードの量は削減されますが、DBMSが標準的なアプローチで行う場合、これらすべてを「手動で」行う必要があるため、条件によるさまざまな並べ替え、グループ化、フィルタリングを実行するコードの量が増えており、必要なクエリのみを記述する必要があります



連載


キーと値のアプローチのマイナスは、ご存じのように、標準のJavaシリアライゼーション/デシリアライゼーションの使用に関連するパフォーマンスの低下に起因する可能性があります。 ここでは、代わりに、この問題を解決するライブラリの1つ、たとえばGoogleのprotobufを使用できます。 スピードに加えて、protobufを使用する場合の追加のプラスは、バージョン管理です。 このプロトコルは、オブジェクトのバージョン管理をサポートします。



おわりに



少し厄介な結果になりましたが、一般的に言っておきたいのは、どちらのアプローチも良いことです。上記のすべての長所と短所を考慮して、状況に応じて選択する必要があります。 原則として、パフォーマンスの問題がない場合は、より柔軟性のある標準的なアプローチを使用することをお勧めします。 これらの問題が発生し始めたら、非正規化を使用してみてください。 おそらく、プログラムにいくつかの重要な領域しかない場合、これはすべてを解決できます。 永続的なパフォーマンスの問題が発生し、非正規化が保存されない場合は、キーと値のアプローチを詳しく調べる価値があります。



All Articles