Firebase Realtime Databaseは、Googleが2012年にモバイルアプリ・ウェブアプリ向けにリリースしたクラウドJSONリアルタイムデータベースです。すべてのデータは1つの大きなJSONツリーに保存され、WebSocket接続を通じて接続されたクライアント間でリアルタイムに同期されます。公式ドキュメントによると Firebase, 2025、Realtime Databaseは最大200,000の同時接続を処理でき、1秒あたり最大1,000の同時書き込みをサポートします。このデータベースはサーバーインフラは不要で、iOS、Android、Web、サーバープラットフォーム向けのSDKを提供します。
メインポイント
Firebase Realtime Databaseは、接続されたすべてのクライアント間でリアルタイムにデータを保存および同期するクラウドNoSQLデータベースです。2012年にFirebaseとしてリリースされ、モバイル開発者向けの初のクラウドリアルタイムデータベースとなりました。データはJSONフォーマットで表現され、各ノードが固有のパスを持つ階層的なツリーに組織されます。
Realtime Databaseの主な価値は、組み込みの同期機能です。いずれかのデバイスでアプリがデータを変更すると、持続接続を通じて他のすべてのクライアントが即時に更新を受け取ります。これにより、クライアント間のデータ転送のための独自の同期メカニズム、WebSocketサーバー、REST APIを実装する必要がありません。
このデータベースは、主なプラットフォームすべてにSDKを提供しています: Android (Java, Kotlin)、iOS (Swift, Objective-C)、Web (JavaScript)、およびAdmin SDKを通じたサーバー環境。Googleによると、Realtime Databaseは世界中306e1.5百万件以上の現役Firebaseプロジェクトで使用されています。より現代的なFirestoreが登場したにもかかわらず、Realtime Databaseは簡単なデータ構造のプロジェクトにおいて人気のある選択肢であり続けています。
リレーショナルデータベースとは異なり、Realtime Databaseはテーブルや行を使用しません。すべてのデータは、ネストされたJavaScriptオブジェクトのような単一のJSONツリーです。例えば、ユーザーとそのメッセージを保存するために、users/userId/nameとmessages/messageId/textのような階層構造が作られます。ツリーの各パスは文字列で、このパスで直接データにアクセスできます。
{
"users": {
"user1": {
"name": "イワン ペトロフ",
"email": "ivan@example.com"
},
"user2": {
"name": "マリヤ ソコロワ",
"email": "maria@example.com"
}
},
"messages": {
"-Nabc123": {
"text": "こんにちは!",
"userId": "user1"
}
}
}
重要な特徴として、深いネストはパフォーマンスに影響します。アプリが特定のパスでデータを読み込むと、そのパスのすべての子ノードがロードされます。そのため、データ構造はできるだけフラットに設計し、3〜4階層を超えるネストを避けることが推奨されます。この問題を回避するために、データの正規化を解除するデノーマライゼーションが使用されます。
Realtime DatabaseとFirestoreは、Googleからの2つのクラウドリアルタイムデータベースとしてよく比較されます。選択はプロジェクトの特定の要件、つまり、クエリの複雑さ、必要な一貫性、予想されるロードに依存します。各データベースの強みを理解することで、正しいアーキテクチャ的決定ができます。
Realtime Databaseの主な利点は、同期レンタンシが低いことです。すべてのデータが追加の抽象レイヤーなしで単一のJSONツリーに保存されているため、Firestoreよりも同期が高速です。更新デリバリの速度が重要なアプリケーション(チャット、オンラインゲーム、共同編集システム)では、Realtime Databaseがより適した選択肢です。
Realtime Databaseは、簡単なデータ構造で更新頻度が高いシナリオに最適です。典型的な例としては、チャット、リアルタイムのいいね、タイピングインジケーター、ユーザーの在級状態などがあります。また、プロトタイプや予算が制限されたプロジェクトにも適しており、価格はオペレーション数ではなくデータ量に基づいています。
一方、複雑なクエリ(複数フィールドでのフィルタリング、ソート、統計)が必要なアプリケーションでは、Firestoreのほうが遠に強力な機能を提供します。Realtime Databaseは1つのパラメータでのフィルタリングしかサポートせず、複数のフィールドでの並行ソートもできません。クライアント側で複雑なデータ分析を予定している場合は、Firestoreのほうが実用的です。
Realtime Databaseは、双向データ同期のために持続的なWebSocket接続を使用します。クライアントが特定のパスでsetValueまたはupdateChildrenを呼び出すと、オープンチャネルを通じてデータがFirebaseサーバーに送信されます。サーバーは変更を適用し、ミリ秒内にサブスクライブしたすべてのクライアントに更新を配信します。各接続は固有のセッションキーで識別されます。
サブスクリプションのメカニズムは、listenerを通じて動作します。開発者は特定のノードの変更にサブスクライブしたり(addListenerForSingleValueEvent)、継続的な更新を受け取ったり(addValueEventListener)できます。データが変更されるたびに、指定されたパスの完全なデータスナップショットを伴うonDataChangeコールバックが発火されます。これは、変更されたドキュメントのみが受け取られるFirestoreとは異なります。
Realtime Databaseは、ディスクキャッシュによりAndroidおよびiOSでオフラインモードをサポートしています。SDKはデータのローカルコピーを保持し、ネットワークなしでも書き込みオペレーションを処理し続けます。接続が弩復すると、すべての蓄積された変更がサーバーに送信されます。競合解決には最終書き込み勝ちのストラテジーが使用されますが、開発者はServerValue.TIMESTAMPを使って競合解決のためのカスタムロジックを実装できます。
val database = FirebaseDatabase.getInstance()
val myRef = database.getReference("messages")
// データの書き込み
myRef.push().setValue(
hashMapOf(
"text" to "新しいメッセージ",
"timestamp" to ServerValue.TIMESTAMP
)
)
// 継続更新による読み出し
myRef.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
val data = snapshot.getValue()
Log.d("TAG", "データ: $data")
}
override fun onCancelled(error: DatabaseError) {
Log.w("TAG", "エラー: ${error.message}")
}
})
トラフィックとパフォーマンスを最適化するために、特定の子ノードの変更をトラックする場合は、value listenerの代わりにchild listenersを使用することが推奨されます。ChildEventListenerは、子要素の追加、変更、削除、移動に対して独立したコールバックを提供し、UI更新をより精密に制御でき、データ変更のたびにリスト項目をすべて再描画する必要がありません。
Realtime Databaseは、データアクセス制御のために宣言的なルール言語を使用します。ルールは、JSONツリーの各パスで誰がデータを読み書きできるかを記述します。これらは各リクエストの前にFirebaseサーバーでチェックされ、許可にサーバーサイドのロジックは不要です。ルールは、変数、組み込みオブジェクト、関数をサポートし、柔軟なアクセス設定ができます。
デフォルトでは、すべてのユーザーに対してデータベースアクセスが禁止されています。開発者は、ツリーの各階層で".read"および".write"ルールを使用して段階的にアクセスを開放します。条件では、auth変数による認証、リクエストタイプ、dataオブジェクトによる既存データを確認できます。また、書き込まれたデータの検証をnewDataオブジェクトでサポートしています。
{
"rules": {
"users": {
"$uid": {
// オーナーのみがデータを読むことができます
".read": "$uid === auth.uid",
// オーナーのみが書き込めます
".write": "$uid === auth.uid",
// 書き込み時のフィールド検証
".validate": "newData.hasChildren(['name', 'email'])"
}
},
"messages": {
// 認証済みのユーザーなら誰でも読めます
".read": "auth !== null",
// 認証済みユーザーのみが書き込めます
".write": "auth !== null",
".indexOn": ["timestamp"]
}
}
}
ルールは".indexOn"ディレクティブによるデータインデックスもサポートしています。これがないと、orderByChildによるソート付きクエリは拒否または非効率的に実行されます。インデックスは、特定のフィールドによるソートが実行される各パスに対して指定されます。ルールはカスケードし、深いルールが親ルールをオーバーライドし、ある階層でアクセスが定義されていない場合は、親ルールに基づいて許可または拒否されます。
Realtime Databaseは5つのデータタイプをサポートしています: String、Number、Boolean、Map、List。ネストの深さは32階層まで、単一ノードの最大サイズは256 MBまでです。効率的なデータベース利用のためには、フラットなデータ構造を設計し、大量のデータをロードする深いクエリを避けるためにデノーマライゼーションを使用することが推奨されます。
ユーザーステータス(オンライン/オフライン)を管理するAndroidアプリにRealtime Databaseを統合する実践例を紹介します。このアプリは、リアルタイムで更新される各ユーザーの現在のステータスを併せbリスト表示します。ユーザー識別にはFirebase Authenticationを、非同期処理にはコルーチンを使用します。
始めるには、アプリモジュールのbuild.gradleファイルにfirebase-database-ktx依存関係を追加してください。ライブラリバージョンはFirebase BoMで管理され、すべてのコンポーネントの互換性が確保されます。依存関係を追加したら、ApplicationクラスまたはViewModelのレイジー初期化でFirebaseを初期化する必要があります。
dependencies {
implementation platform("com.google.firebase:firebase-bom:33.0.0")
implementation "com.google.firebase:firebase-database-ktx"
implementation "com.google.firebase:firebase-auth-ktx"
}
設定後、ユーザーを管理するリポジトリが作成されます。各ユーザーは、name、email、statusフィールドを持つ/users/{uid}ツリーのノードで表されます。ステータスの追跡には、クライアントの接続が切れたときに自動的に書き込みを実行するFirebaseの特別な機構であるonDisconnectが使用されます。これにより、クライアント側の追加コードなしで、アプリ閉鎖時やネットワーク失気時にユーザーのステータスが自動的に"offline"に変更されます。
class PresenceRepository {
private val database = FirebaseDatabase.getInstance()
private val auth = FirebaseAuth.getInstance()
private val presenceRef = database
.getReference("presence")
fun trackPresence() {
val uid = auth.currentUser?.uid ?: return
val userRef = presenceRef.child(uid)
userRef.onDisconnect().setValue("offline")
userRef.setValue("online")
}
fun getPresenceStream(): Flow<Map<String, String>> =
presenceRef.snapshotFlow()
.map { snapshot ->
(snapshot.value as? Map<*, *>)
?.mapKeys { it.key.toString() }
?.mapValues { it.value.toString() }
?: emptyMap()
}
}
この例の主な要素はonDisconnectです。この機構により、クライアントの接続が切れたときにサーバーで実行される書き込みオペレーションを設定できます。この場合、ユーザーが切断されると、アプリの終了イベントを処理する必要なく、ステータスが自動的に"offline"に設定されます。アプリがクラッシュしても、FirebaseがonDisconnectオペレーションを実行し、他のユーザーは正しいステータスを見ることができます。
よくある質問
Realtime Databaseは単一のJSONツリーにデータを保存し、より低い同期レンタンシを提供します。Firestoreはドキュメントコレクションを使用し、複雑なクエリと強い一貫性をサポートします。Realtime Databaseは簡単なチャットやステータスに適しており、Firestoreは複雑なデータ構造や分析を含むアプリに適しています。
Realtime Databaseの単一ノードの最大サイズは256 MBです。ネストの深さは32階層に制限されています。単一のFirebaseプロジェクトでは、Sparkプランで最大5つ、Blazeプランで最大100つのRealtime Databaseインスタンスを作成でき、データを分散することができます。
Realtime DatabaseはFirebase Authenticationと統合します。セキュリティルールでは、認証済みユーザーのuidを含むauth変数が利用可能です。開発者はデータ所有者のuidを確認することで、JSONツリーの個々のノードレベルでアクセスを制限できます。匿名ユーザーおよび未認証ユーザーは auth = null です。
はい、Realtime DatabaseはrunTransactionメソッドによりトランザクションをサポートしています。トランザクションは、単一ノードの読み出し・変更・書き込みオペレーションの原子性を保証します。並行した変更が発生した場合、トランザクションは現在のデータで再実行されます。カウンター、レーティング、データ一貫性が重要なシナリオに役立ちます。
はい、Realtime DatabaseはAndroidおよびiOSでオフラインモードをサポートしています。SDKはデータをローカルにキャッシュし、ネットワークなしでも書き込みオペレーションを処理し続けます。接続弩復後、すべての蓄積変更がサーバーと同期されます。オフラインモードを有効にするには、対象のノードでkeepSynced(true)メソッドを使用します。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。