Firebase Firestore 是 Google 提供的云端 NoSQL 实时数据库,专为移动和 Web 应用程序设计。它以集合和文档的形式存储数据,并在客户端之间自动同步。根据 Firebase 2025 文档,Firestore 支持多区域部署并保证一致性,无需管理服务器即可自动扩展。该数据库与 Firebase Authentication 和 Cloud Functions 集成,无需自有服务器基础设施即可构建完整的后端。
要点
Firebase Firestore 是一个灵活、可扩展的 NoSQL 数据库,由 Google 于 2019 年推出,是 Firebase Realtime Database 的演进版本。它以文档集合的形式存储数据,每个文档包含一组键值对。与传统关系型数据库不同,Firestore 不需要预先定义模式——数据结构根据写入的文档动态形成。
Firestore 与经典云数据库的关键区别在于内置的实时同步。当服务器上的数据发生变化时,所有连接的客户端通过永久的 WebSocket 连接接收更新。这消除了手动轮询服务器的需要,并允许构建具有实时更新的应用程序:聊天、活动提要、协作编辑器和监控系统。
该数据库在所有主要平台上都可用:Android、iOS、Web(JavaScript)以及通过 Admin SDK 使用的服务器端语言。Firestore 为 Swift、Kotlin、JavaScript、Python、Go、Java 和 Node.js 提供 SDK。根据 Google 的数据,Firestore 每天在整个 Firebase 生态系统中处理超过 1000 亿次查询,这证实了其作为生产应用程序基础的可靠性。
在 Firestore 中,数据以分层结构组织。集合是文档的容器,类似于 SQL 中的表,但没有固定模式。文档是包含各种类型字段的记录:字符串、数字、布尔值、数组、嵌套对象和地理点。文档可以包含子集合,这允许构建任意深度的嵌套数据结构。
val db = FirebaseFirestore.getInstance()
val user = hashMapOf(
"name" to "安娜·彼得罗娃",
"email" to "anna@example.com",
"age" to 28,
"isActive" to true
)
db.collection("users")
.add(user)
.addOnSuccessListener { docRef ->
Log.d("TAG", "文档已添加,ID:${docRef.id}")
}
集合中的每个文档都有一个唯一的标识符,可以自动生成或手动设置。Firestore 会自动索引文档的所有字段,无需预先配置索引即可执行具有过滤、排序和限制结果数量的复杂查询。
Firestore 和 Firebase Realtime Database 是 Google 提供的两个云端实时数据库。虽然两者都提供实时同步,但它们在数据模型、扩展和定价方面存在根本性差异。理解这些差异对于为特定项目选择合适的数据库至关重要。
| 特性 | Firestore | Realtime Database |
|---|---|---|
| 数据模型 | 集合和文档 | 单个 JSON 树 |
| 一致性 | 强一致性(strong consistency) | 最终一致性 |
| 查询 | 支持过滤和排序的复合查询 | 仅按一个参数过滤 |
| 扩展性 | 自动、多区域 | 单区域,最多 200k 连接 |
| 定价 | 按读取/写入/删除操作计费 | 按传输数据量计费 |
主要的架构差异是数据模型。Realtime Database 将所有内容存储在一个大的 JSON 树中,这使得深度嵌套查询变得困难。Firestore 使用集合和文档,允许执行具有多个条件的复杂查询。此外,Firestore 提供强数据一致性:成功写入后,所有后续读取保证返回最新数据。
Firestore 凭借多区域架构自动扩展到数百万个并发连接。Realtime Database 仅限于一个区域,最多 200,000 个并发连接。对于计划面向全球受众的项目,Firestore 更受青睐,因为数据会自动在多个 Google 数据中心之间复制。
Firestore 中的数据结构允许使用子集合构建复杂的分层模型。例如,用户可以有一个“订单”子集合,每个订单有一个“产品”子集合。在 Realtime Database 中,这种深度嵌套会导致查询性能问题,因为会加载从根到所需节点的整个路径。
Firestore 使用客户端和服务器之间的永久 WebSocket 连接进行实时数据同步。当应用程序通过快照监听器订阅文档或集合的更改时,SDK 会建立一个通信通道,服务器通过该通道在每次数据更改时发送更新。客户端只接收更改的文档,而不是每次都接收整个集合。
同步机制基于事件流:added(文档出现)、modified(文档更改)和 removed(文档删除)。开发人员可以单独处理每个事件,仅更新相应的 UI 元素。即使有数千个文档,这也能确保高性能,因为只重新绘制更改的组件。
Firestore 的主要优势之一是内置的离线模式支持。SDK 会自动将所有读取的数据缓存在设备上,并在没有网络时继续工作。当应用程序在离线模式下写入数据时,它们被放入本地队列,并在连接恢复时发送到服务器。解决冲突使用 last-write-wins 策略。
val docRef = db.collection("cities").document("SF")
docRef.addSnapshotListener { snapshot, error ->
if (error != null) {
Log.w("TAG", "监听错误", error)
return@addSnapshotListener
}
if (snapshot != null && snapshot.exists()) {
Log.d("TAG", "当前数据:${snapshot.data}")
}
}
缓存大小可以通过 FirestoreSettings 配置。默认使用 100 MB,但对于数据读取密集的应用程序可以增加。还有持久磁盘缓存模式可用,可在应用程序重启后保留。为了管理离线模式的可用性,使用 enableNetwork 和 disableNetwork 方法,允许临时禁用网络通信。
Firestore Security Rules 是一种声明性标记语言,用于在服务器级别控制数据访问。规则确定谁以及在什么条件下可以读取和写入文档。它们在查询执行之前生效,并且不需要单独的服务器逻辑进行授权。在每次读取或写入数据之前,规则会在 Firebase 端进行检查。
访问规则基于允许访问(allow)原则构建。默认情况下,所有访问都被禁止。开发人员逐步在特定条件下为特定操作(read、write、create、update、delete)开放访问。条件可以通过 request.auth 检查用户身份验证,通过 request.resource 检查请求数据,通过 resource 检查现有数据。
// Firestore 访问规则
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// 用户只读取和写入自己的数据
match /users/{userId} {
allow read, write: if
request.auth != null &&
request.auth.uid == userId;
}
// 任何经过身份验证的用户都可以阅读帖子
match /posts/{postId} {
allow read: if request.auth != null;
allow create: if request.auth != null
&& request.resource.data.author == request.auth.uid;
}
}
}
除了访问控制,Security Rules 还允许验证写入数据的结构和类型。例如,可以检查电子邮件字段是否与正则表达式匹配,或者年龄是否不超过 120 岁。验证在写入之前执行,防止在服务器上存储不正确的数据。检查字段使用 request.resource.data 对象,该对象包含整个正在写入的文档。
Firestore 还支持仅通过 Admin SDK 进行服务器写入的集合,客户端无法访问。这对于存储服务信息、API 密钥和不应向用户显示的配置非常方便。为此,只需在规则中禁止相关集合上的所有客户端操作,仅允许通过 Admin SDK 在服务器端进行访问。
让我们来看一个在 Android 应用程序中集成 Firestore 创建待办事项列表(todo)的示例。该应用程序将实时读取任务、添加新任务并标记已完成任务。异步工作使用 Firebase 回调接口和 Kotlin 协程。
开始之前,需要通过 Firebase 控制台将项目连接到 Firebase,并将 google-services.json 文件添加到应用程序模块。然后在 build.gradle 中添加 firebase-firestore-ktx 依赖项和 google-services 插件。库版本应与当前 Firebase BoM 版本匹配,以确保所有 Firebase 组件之间的兼容性。
dependencies {
// Firebase BoM — 版本管理
implementation platform("com.google.firebase:firebase-bom:33.0.0")
implementation "com.google.firebase:firebase-firestore-ktx"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.9.0"
}
配置后,创建数据模型 Task 和用于与 Firestore 交互的存储库。模型包含 id、title、isCompleted 和 timestamp 字段。Firestore 自动将数据类序列化为文档,使用字段名称作为键。用于读取数据的快照监听器通过 snapshotFlow 扩展返回一个 Flow。
data class Task(
val id: String = "",
val title: String = "",
val isCompleted: Boolean = false,
val createdAt: Timestamp? = null
)
class TaskRepository {
private val tasksRef = FirebaseFirestore
.getInstance()
.collection("tasks")
fun getTasks(): Flow<List<Task>> = tasksRef
.orderBy("createdAt", Query.Direction.DESCENDING)
.snapshotFlow()
.map { snapshot ->
snapshot?.toObjects(Task::class.java) ?: emptyList()
}
suspend fun addTask(title: String) {
tasksRef.add(Task(title = title))
}
}
视图模型订阅存储库中的 Flow 并将任务列表传递到 UI 层。添加新任务时,通过协程作用域调用存储库的挂起函数。Firestore 自动同步所有客户端之间的更改:如果一个用户添加了任务,其他用户无需刷新屏幕即可实时看到它。
常见问题
Firestore 是一个 NoSQL 数据库,具有灵活的模式,没有表和 JOIN 查询。数据存储在文档集合中,而不是表行中。与 SQL 不同,Firestore 不需要预先定义模式,可以自动扩展而无需迁移,但不支持集合之间的复杂事务查询。
Firestore 有慷慨的免费限制(Spark 计划):每天 50,000 次读取、20,000 次写入和 20,000 次删除。超出后使用 Blaze 计划,按实际使用付费:每 100,000 次读取 0.06 美元,每 100,000 次写入 0.18 美元。价格取决于区域和传输的数据量。
Firestore 使用 last-write-wins 策略解决冲突:文档的最后一次写入完全替换前一次。为了更精细的控制,可以使用事务(原子读取-写入操作)和批量写入,确保对多个文档的操作的完整性。
是的,Firestore 支持通过 Firebase 控制台或 gcloud CLI 导入和导出数据。导出以 Cloud Firestore Export 格式执行,并保存在 Google Cloud Storage 中。数据可以在 Firebase 项目之间迁移,或下载到 BigQuery 和其他工具中进行分析。
Firestore 没有内置的全文搜索功能。对于此任务,Google 建议集成 Algolia 或 Meilisearch,或者使用带有 Elasticsearch 的 Cloud Functions。Firestore 的内置查询仅支持相等性、范围和字段存在性检查,不支持子字符串搜索。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。