- 数据库
- NoSQL
- 嵌入式数据库
- 实时数据库
【免费下载链接】rxdb
The local-first database that runs on every JS runtime and replicates with your existing backend - no vendor, no lock-in - https://rxdb.info/
Horizon 曾是 RethinkDB 官方的客户端实时数据库方案,但因公司停运而停止维护,且从未实现离线支持。本文以 RxDB 为主线,完整梳理 Horizon 的架构缺陷,并逐一给出 RxDB 在响应式查询、离线优先、多标签同步、灵活复制、冲突解决、Schema 校验与迁移、静态加密等方面的对等乃至超越能力,帮助团队完成从"在线订阅式架构"到"本地优先数据库"的迁移。
什么是 Horizon?
Horizon(又称 horizon.io)是构建在 RethinkDB 之上的开源后端平台,由 RethinkDB 公司于 2016 年 5 月发布。其目标是让前端 JavaScript 开发者无需编写和维护传统的 REST 或 GraphQL API,即可直接获得实时数据通道。
Horizon 客户端库直接连接 Horizon 服务器进程,服务器再与 RethinkDB 通信。开发者通过集合(collection)和一套返回 RxJS Observable 的流式 API 操作数据。一个典型的 Horizon 应用如下:
// Horizon: 连接后端 const horizon = Horizon(); // 获取集合引用(对应 RethinkDB 的表) const messages = horizon('messages'); // 存储一条文档 messages.store({ id: 'msg-1', text: 'Hello, Horizon!', createdAt: new Date() }); // 监听实时变化 —— 返回 RxJS Observable messages.watch().subscribe(allMessages => { renderMessages(allMessages); }); // 仅获取一次(非实时) messages.fetch().subscribe(allMessages => { console.log(allMessages); }); // 排序与分页 messages.order('createdAt', 'descending').limit(20).watch().subscribe(recent => { renderRecentMessages(recent); });watch()方法是核心:它连接到 RethinkDB 的 changefeed,数据一变就向客户端推送更新后的结果集。这在 2016 年非常有吸引力——在那个时代,不借助轮询构建实时应用需要大量基础设施工作。
Horizon 还提供:
- 认证:本地用户名/密码、GitHub OAuth、Google OAuth 及其他 Provider;
- 权限系统:通过规则控制哪些用户可读/写哪些文档;
hz命令行工具:用于脚手架项目和运行本地开发服务器;- 可嵌入现有 Node.js 应用:满足需要在数据层之外编写自定义服务端逻辑的团队。
Horizon 的时间线
- 2016 年 5 月:Horizon 随
hzCLI 与 JavaScript 客户端库公开亮相。 - 2016 年 10 月:RethinkDB Inc. 宣布停运,公司未能建立起可持续的商业模式。
- 2017 年 2 月:Linux 基金会(经云原生计算基金会 CNCF)接管 RethinkDB 并改以 Apache 2.0 授权,但 Horizon 并未获得同等对待。
- 2016 年至今:Horizon 未获得任何有意义的更新,GitHub 仓库实质归档。
停运几乎发生在发布之后不久,Horizon 从未有机会成熟。社区从 2016 年起就在 GitHub Issue 中强烈要求的离线支持等规划功能,从未落地,并在项目被放弃后不了了之。
Horizon 做得好的地方
对于"纯在线实时 Web 应用"这一狭窄场景,Horizon 显著减少了开发者需要编写的代码量:订阅集合、直接在 UI 中渲染结果,完全不用写服务端路由。基于 Observable 的 API 在当时相当超前,把响应式编程范式带进了客户端-服务端数据层。
Horizon 的短板在哪里
没有离线支持
Horizon 从未为离线场景设计。每次查询、读取、写入都必须与 Horizon 服务器保持实时网络连接。连接一旦断开,watch()返回的 Observable 流会停止发射,任何store()、update()、remove()调用都会静默失败或直接抛错。
离线支持是 Horizon 仓库中最受请求的功能之一,但从未被设计,更别说实现。对大多数现代应用而言,离线能力并非锦上添花:用户期望应用在地铁、信号差的建筑、移动数据昂贵的地区都能工作。网络一断应用就坏,这样的应用是残缺的。
项目已被放弃
Horizon 无人维护:没有安全更新,没有对现代 Node.js 版本的兼容修复,没有 TypeScript 类型定义,也没有对 issue 和 PR 的响应。2025 年在全新项目里安装 Horizon,必须绕开与现代工具链的依赖冲突。
底层 RethinkDB 仍偶有社区维护版本,但 Horizon 与之独立,无法从中获益。任何建立在 Horizon 之上的团队都迟早面临迁移问题。
服务端架构,无客户端存储
Horizon 不在客户端保存任何数据,所有数据都存于服务端 RethinkDB。客户端库只是一个订阅机制,而非数据库。这意味着:
- 离线时应用完全无法提供数据;
- 没有本地查询缓存,查询参数一变就是一次新的网络请求;
- 无法在本地写入数据后再同步;
- 关闭并重新打开应用,必须重新从服务器拉取全部数据。
这种架构与离线优先(offline-first)模式从根本上互斥——离线优先要求把本地存储当作主要数据源,把服务器当作同步目标而非强制依赖。
没有冲突解决机制
Horizon 没有提供两个用户并发修改同一文档时的冲突解决机制。服务端权威模型决定了由 RethinkDB 的 last-write-wins(最后写入胜出)行为定夺结果。实践中,两个用户同时修改同一文档时,其中一方的修改被静默覆盖,且双方都毫不知情。
对协作类应用这是重大缺陷:开发者没有任何 API 能检测冲突、检查两个版本、或应用合并策略。
高度固化的结构
Horizon 要求特定的项目结构、特定的服务进程(hz serve)和自带的认证系统。把它集成进已有后端、非 Node.js 服务器、或已有认证层的项目,需要大量变通。权限系统围绕 Horizon 自己的用户模型设计,难以适配现有用户库或角色体系。
RxDB 如何覆盖同样的能力(并且更多)
RxDB 与 Horizon 共享核心思想:数据变更应通过响应式、Observable 风格的 API 自动传播到 UI。但 RxDB 把这一能力实现在客户端——一个完整的本地数据库——而不是一个指向远端服务器的订阅机制。
客户端响应式查询
在 RxDB 中,每个查询都是 Observable。订阅查询时立即拿到当前结果集,此后无论变更来自本地写入还是复制事件,Observable 都会重新发射:
import { createRxDatabase } from 'rxdb/plugins/core'; import { getRxStorageIndexedDB } from 'rxdb/plugins/storage-indexeddb'; const db = await createRxDatabase({ name: 'myapp', storage: getRxStorageIndexedDB() }); await db.addCollections({ messages: { schema: { title: 'message schema', version: 0, primaryKey: 'id', type: 'object', properties: { id: { type: 'string', maxLength: 100 }, text: { type: 'string' }, roomId: { type: 'string' }, createdAt: { type: 'number' } }, required: ['id', 'text', 'roomId', 'createdAt'], indexes: ['createdAt'] } } }); // 订阅某房间的消息,按创建时间排序 db.messages.find({ selector: { roomId: 'room-42' }, sort: [{ createdAt: 'asc' }] }).$.subscribe(messages => { renderMessages(messages); // 立即调用,且每次变更都会调用 });这等价于 Horizon 的watch()API,但数据来自本地 IndexedDB 存储而非远端服务器。无论在线还是离线,UI 的行为完全一致。
RxDB 用event-reduce 算法让响应式更新保持高效。核心实现在 src/event-reduce.ts,其中calculateNewResults()会检查能否通过直接应用变更事件来更新已有查询结果,而无需对存储重跑完整查询(src/event-reduce.ts)。为保证这一点,集合层维护了一个ChangeEventBuffer,缓冲最近的变更事件供查询在.exec()时使用(src/change-event-buffer.ts,默认缓冲上限为 100 条,见 src/change-event-buffer.ts)。这使得高写入负载下 UI 仍能保持快速响应。
完整的离线优先运行
用户在没有网络时打开 RxDB 应用,一切功能照常:读来自本地存储,写进本地存储。没有报错、没有等待服务器的加载转圈、没有缺失数据。
网络恢复后,RxDB 的复制插件自动把本地变更与远端后端同步,应用在离线和在线状态间无缝切换,单个功能无需任何代码改动。
这正是 离线优先架构:RxDB 把本地存储当作主要数据源,服务器只是同步目标而非正常运行的前提。Horizon 的架构恰恰相反——服务器是唯一数据源,离线运行根本不可能。
多标签页支持
Horizon 每个浏览器窗口维持一条连接。用户打开同一应用的两个标签页,每个标签页各自持有与服务器的 WebSocket 连接,标签页间的本地状态可能分叉。
RxDB 提供 SharedWorker 存储模式,让单个数据库实例运行在共享 Worker 中,所有标签页共享这一个实例。一个标签页的写入会立即反映到其他所有标签页的响应式查询中,无需额外代码:
import { getRxStorageSharedWorker } from 'rxdb/plugins/storage-shared-worker'; const db = await createRxDatabase({ name: 'myapp', storage: getRxStorageSharedWorker({ workerInput: new SharedWorker( new URL('rxdb/plugins/storage-shared-worker/worker.js', import.meta.url), { type: 'module' } ) }) });对于只需一个标签页执行的后台任务(如运行复制),RxDB 提供 leader election 插件:一个标签页被选为 leader 处理后台工作,leader 标签页被关闭时其他标签页自动接管:
import { RxDBLeaderElectionPlugin } from 'rxdb/plugins/leader-election'; import { addRxPlugin } from 'rxdb/plugins/core'; addRxPlugin(RxDBLeaderElectionPlugin); // 只有 leader 标签页运行复制 await db.waitForLeadership(); startReplication(db);从源码看,leader election 基于broadcast-channel库的createLeaderElection()实现,并以数据库名 + token 为键复用电极器(src/plugins/leader-election/index.ts),从而保证同一数据库在多个标签页间只会选出一个 leader。
灵活复制到任意后端
Horizon 要求 Horizon 服务器,而 Horizon 服务器又要求 RethinkDB,整条技术栈被固定死。RxDB 把存储与复制解耦,两者均可独立配置。
RxDB 在本地存储数据,并通过插件系统复制到后端。你可以复制到应用已使用的任意后端:
import { replicateRxCollection } from 'rxdb/plugins/replication'; const replicationState = await replicateRxCollection({ collection: db.messages, replicationIdentifier: 'messages-http-v1', pull: { handler: async (checkpoint, batchSize) => { const since = checkpoint?.updatedAt ?? 0; const response = await fetch( `/api/messages?since=${since}&limit=${batchSize}` ); const data = await response.json(); return { documents: data.documents, checkpoint: data.checkpoint }; } }, push: { handler: async (rows) => { const response = await fetch('/api/messages', { method: 'POST', body: JSON.stringify(rows), headers: { 'Content-Type': 'application/json' } }); return response.json(); // 返回冲突文档或 [] } }, live: true, retryTime: 5000 }); // 可观察的复制状态 replicationState.active$.subscribe(active => console.log('Syncing:', active)); replicationState.error$.subscribe(err => console.error('Sync error:', err));pull handler 从检查点(checkpoint)之后拉取服务端变更;push handler 把本地变更推给服务端。复制协议足够简单,任何后端语言都能实现服务端。不要求运行特定服务进程、使用特定数据库、或采纳特定权限模型。
这个通用复制插件的实现位于 src/plugins/replication/index.ts,replicateRxCollection()的默认参数正是live = true、retryTime = 1000 * 5(即 5 秒,src/plugins/replication/index.ts),与上面的示例一致。断线重试的逻辑在 src/plugins/replication/replication-helper.ts 的awaitRetry():浏览器环境下若navigator.onLine为 false,会同时等待online事件与retryTime超时,二者先到者触发重试。
对常见的后端形态,RxDB 提供了开箱即用的插件:
| 插件 | 适用场景 |
|---|---|
| HTTP 复制 | 任意 REST API 端点 |
| GraphQL 复制 | GraphQL API,包括 AWS AppSync |
| WebSocket 复制 | 低延迟服务端推送 |
| CouchDB 复制 | CouchDB 或 PouchDB 服务器 |
| Firestore 复制 | Google Cloud Firestore |
可插拔的存储后端
RxDB 的存储层与查询、复制逻辑分离。同一套应用代码,可根据运行环境切换不同的存储引擎:
| 环境 | 存储方案 |
|---|---|
| 浏览器(标准) | IndexedDB |
| 浏览器(高吞吐) | OPFS(Origin Private File System) |
| React Native / Expo | 基于 expo-sqlite 或 op-sqlite 的 SQLite |
| Node.js / Electron | SQLite(better-sqlite3) |
| 多标签页浏览器 | SharedWorker |
| 测试 / CI | Memory |
切换存储只需改一行:
import { getRxStorageOpfs } from 'rxdb/plugins/storage-opfs'; const db = await createRxDatabase({ name: 'myapp', storage: getRxStorageOpfs() // 浏览器中改用 OPFS 获得更高吞吐 });Horizon 没有对等能力:数据始终在服务端 RethinkDB,没有本地存储、没有存储抽象、也没有在无 RethinkDB 实例时运行应用的办法。
冲突解决
Horizon 继承了 RethinkDB 的 last-write-wins 冲突模型,两个并发写导致静默覆盖,开发者没有机制检测、检查或合并冲突版本。
RxDB 为每个集合提供可配置的 conflictHandler。复制过程中同一文档的两个版本相遇时,conflictHandler 被调用并返回仲裁后的文档:
await db.addCollections({ messages: { schema: messageSchema, conflictHandler: async ({ newDocumentState, realMasterState }) => { // 保留更新时间较新的版本 if (newDocumentState.updatedAt >= realMasterState.updatedAt) { return { documentData: newDocumentState }; } return { documentData: realMasterState }; } } });对于多用户并发编辑同一文档的协作场景,RxDB 支持 基于 CRDT 的冲突解决。CRDT 以确定性方式合并并发编辑,无需中心权威判定谁赢:
import { getCRDTSchemaPart, RxDBcrdtPlugin } from 'rxdb/plugins/crdt'; import { addRxPlugin } from 'rxdb/plugins/core'; addRxPlugin(RxDBcrdtPlugin); const messageSchema = { version: 0, primaryKey: 'id', type: 'object', properties: { id: { type: 'string', maxLength: 100 }, text: { type: 'string' }, roomId: { type: 'string' }, crdts: getCRDTSchemaPart() }, crdt: { field: 'crdts' } };CRDT 插件的源码中,集合通过crdt字段把增量操作合并进文档,并使用storageToken标识操作创建者(src/plugins/crdt/index.ts),保证跨客户端、跨标签页的操作序列可确定性合并。
Schema 校验与 TypeScript 支持
Horizon 存储和返回纯 JavaScript 对象,不做任何校验。客户端存错字段名或类型,RethinkDB 照单全收,脏数据还会经 changefeed 传播给所有其他客户端。
RxDB 在写入存储前,用 JSON Schema 校验每个文档。非法文档在写入步骤就被拒绝,不会进入本地存储、也不会经复制扩散:
try { await db.messages.insert({ id: 'msg-001', // 'text' 字段必填但缺失 roomId: 'room-42', createdAt: Date.now() }); } catch (err) { console.error(err); // Schema 校验错误:缺少 'text' }RxDB 还会从 Schema 自动生成 TypeScript 类型。find()、insert()、upsert()等集合方法全部带类型,数据库操作拥有 IDE 自动补全与编译期安全。
Schema 迁移
应用演进时数据模型会变:新增必填字段、重命名属性、重构嵌套对象,都需要更新既有存量文档。
Horizon 没有迁移系统。改变数据形态后,你得自己写脚本连上 RethinkDB 逐条手改,框架零帮助。
RxDB 内置 Schema 迁移系统。Schema 版本号递增时,RxDB 会在数据库对应用可用之前,自动对所有本地存量文档执行迁移策略:
await db.addCollections({ messages: { schema: messageSchemaV2, // version: 1 migrationStrategies: { 1: (oldDoc) => { // 从 version 0 迁移:为 'threadId' 补默认值 return { ...oldDoc, threadId: oldDoc.threadId ?? 'main' }; } } } });迁移在每个客户端本地独立执行,不需要协调的服务端发布,也不需要手写数据库更新脚本。
静态加密
Horizon 通过 WebSocket 在浏览器与 RethinkDB 之间明文传输数据,RethinkDB 中也原样存储。设备一旦被攻破,浏览器会话遗留的 IndexedDB 数据无需解密即可读。
RxDB 内置 加密插件,在写入本地存储前对单个文档字段加密;读取时解密,应用看到的是明文,而底层存储只有密文:
import { wrappedKeyEncryptionCryptoJsStorage } from 'rxdb/plugins/encryption-crypto-js'; import { getRxStorageIndexedDB } from 'rxdb/plugins/storage-indexeddb'; const db = await createRxDatabase({ name: 'myapp', storage: wrappedKeyEncryptionCryptoJsStorage({ storage: getRxStorageIndexedDB() }), password: 'your-encryption-passphrase' }); const schema = { version: 0, primaryKey: 'id', type: 'object', properties: { id: { type: 'string', maxLength: 100 }, text: { type: 'string' }, sensitiveData: { type: 'string' } }, encrypted: ['sensitiveData'] // 在 IndexedDB 中以密文存储 };可观察的变更流
Horizon 的watch()从服务端推送完整更新后的结果集。RxDB 在集合和文档两个层级都提供可观察的变更流,在本地即可细粒度感知变更事件:
// 订阅 messages 集合的所有变更 db.messages.$.subscribe(changeEvent => { console.log('Operation:', changeEvent.operation); // INSERT, UPDATE, DELETE console.log('Document ID:', changeEvent.documentId); console.log('Document data:', changeEvent.documentData); }); // 订阅某个具体文档的变更 const doc = await db.messages.findOne('msg-001').exec(); doc.$.subscribe(updatedDoc => { console.log('Document updated:', updatedDoc?.text); });这些事件源自本地数据库:本地写入会触发,经复制到达的文档也会触发,离线时同样照常触发,完全不依赖服务器连接。
上手 RxDB
安装 RxDB 与 RxJS:
npm install rxdb rxjs创建数据库、添加集合、写入文档并订阅响应式查询:
import { createRxDatabase, addRxPlugin } from 'rxdb/plugins/core'; import { RxDBDevModePlugin } from 'rxdb/plugins/dev-mode'; import { getRxStorageIndexedDB } from 'rxdb/plugins/storage-indexeddb'; addRxPlugin(RxDBDevModePlugin); const db = await createRxDatabase({ name: 'chatapp', storage: getRxStorageIndexedDB() }); await db.addCollections({ messages: { schema: { title: 'message schema', version: 0, primaryKey: 'id', type: 'object', properties: { id: { type: 'string', maxLength: 100 }, text: { type: 'string' }, roomId: { type: 'string' }, createdAt: { type: 'number' } }, required: ['id', 'text', 'roomId', 'createdAt'], indexes: ['roomId', 'createdAt'] } } }); // 写入一条消息 await db.messages.insert({ id: 'msg-001', text: 'Hello from RxDB!', roomId: 'room-42', createdAt: Date.now() }); // 响应式查询:立即发射当前状态,且每次变更都会重新发射 db.messages.find({ selector: { roomId: 'room-42' }, sort: [{ createdAt: 'asc' }] }).$.subscribe(messages => { console.log('Current messages:', messages.map(m => m.text)); });以上流程完全离线可用;需要服务端同步时,再接上一个复制插件即可。
对比总结
| 维度 | Horizon | RxDB |
|---|---|---|
| 项目状态 | 2016 年起被放弃 | 2016 年起持续维护 |
| 运行位置 | 客户端连接服务器 | 客户端完整数据库 |
| 离线支持 | 无(所有操作都需联网) | 完整离线优先运行 |
| 响应式查询 | watch()从 RethinkDB 推送 | 本地存储上的 Observable 查询 |
| 数据位置 | 远端 RethinkDB 服务器 | 本地存储(IndexedDB、OPFS、SQLite) |
| 后端依赖 | 必须运行 Horizon + RethinkDB | 任意后端或无需后端 |
| 复制 | Horizon 专有协议 | 可插拔(HTTP、WebSocket、CouchDB、GraphQL、自定义) |
| 冲突解决 | Last-write-wins(静默覆盖) | 可配置 handler 或基于 CRDT 合并 |
| Schema 校验 | 无 | 每次写入强制 JSON Schema 校验 |
| Schema 迁移 | 手写脚本 | 内置版本化迁移策略 |
| 多标签页支持 | 每标签页独立连接 | SharedWorker(所有标签页共享状态) |
| 静态加密 | 无 | 内置字段级加密插件 |
| TypeScript 支持 | 无(仅 JavaScript) | 由 Schema 自动生成类型 |
| 认证 | 内置 OAuth Provider(已过时) | 交由既有后端处理,无锁定 |
| 权限 | Horizon 专属规则系统 | 交由既有后端处理 |
| 安全更新 | 无(已放弃) | 随活跃开发持续跟进 |
| 许可证 | Apache 2.0 | Apache 2.0 |
常见问题
Q:RxDB 能否替换基于 RethinkDB 的现有应用中的 Horizon?
可以。RxDB 接管客户端数据层,RethinkDB 保留在服务端,在其前面包一层薄 API(REST 或 WebSocket),再用 RxDB 的 自定义复制 或 WebSocket 复制 插件同步客户端 RxDB 与服务端 RethinkDB 的数据。
主要区别在于:Horizon 本身就是完整的客户端-服务端协议,而用 RxDB 时 API 层由你掌控。这让你完全掌握认证、限流和数据访问规则,而不是受制于 Horizon 专属的权限模型。
Q:RxDB 的响应式模型与 Horizon 的watch()API 相比如何?
Horizon 的watch()接入 RethinkDB 的 changefeed 系统并推送更新后的结果集——集合中任一文档变化,Horizon 就把整个更新后的数组发给订阅者。
RxDB 在 API 层面类似:订阅查询立即得到当前结果集,相关数据变化时 Observable 重新发射更新后的数组。区别在数据来源:Horizon 从远端服务器拉取,watch()需要实时连接;RxDB 从本地存储发射,响应式查询在线离线行为一致。RxDB 用 event-reduce 算法高效计算结果集更新(src/event-reduce.ts、src/change-event-buffer.ts),避免每次变更都重跑完整查询,高写入负载下也能保持响应式更新流畅。
Q:RxDB 能配合 React、Vue、Angular 等框架使用吗?
可以。RxDB 与框架无关,其响应式查询返回 RxJS Observable,可与任意框架集成。RxDB 为 React 提供便捷 hooks(useRxQuery、useRxDocument),把 Observable 订阅封装进 React 状态模型;Angular 可直接用 async pipe 消费 RxJS Observable;Vue 中普通 Observable 订阅可与ref、reactive搭配。
Horizon 在 API 层面同样与框架无关,但其依赖早已过时,不 fork 就很难与现代框架版本集成。
Q:RxDB 在离线后如何重连?
RxDB 的复制插件持续运行并自动重试。网络不可用时 pull/push handler 失败,RxDB 等待retryTime毫秒后重试(默认 5000ms,浏览器环境下还会监听online事件提前恢复,见 src/plugins/replication/replication-helper.ts)。网络恢复后,复制从最近的成功检查点继续。写入不会丢失:离线期间写入的文档保存在本地,连接恢复后推送到服务器。UI 中的响应式查询全程保持最新——本地写入立即反映,无需等待服务器确认。
Q:RxDB 适合生产环境吗?
RxDB 自 2016 年以来持续活跃开发,被多家公司用于生产应用,并通过 premium 插件 建立可持续的商业模式以支撑长期维护。与 2016 年后毫无维护的 Horizon 不同,RxDB 持续新增存储后端、修复浏览器兼容性问题并改进性能。
- 数据库
- NoSQL
- 嵌入式数据库
- 实时数据库
【免费下载链接】rxdb
The local-first database that runs on every JS runtime and replicates with your existing backend - no vendor, no lock-in - https://rxdb.info/
相关推荐
Cachex 实战疑难解答:解决分布式缓存 9 大核心问题
Cachex 实战疑难解答:解决分布式缓存 9 大核心问题 引言:你是否正被这些 Cachex 问题困扰? 在高并发 Elixir 应用中,Cachex 作为功
数据库NoSQL嵌入式数据库实时数据库Terraform Stacks 运行时内部架构解析:基于隐式数据流求值的声明式语言运行时
Terraform Stacks 运行时内部架构解析:基于隐式数据流求值的声明式语言运行时 本文面向 Terraform 的维护者与内核研究者,系统剖析 Sta
数据库NoSQL嵌入式数据库实时数据库RxDB 作为 PouchDB 的现代替代方案:响应式查询、可插拔存储与无修订树开销的离线优先数据库
RxDB 作为 PouchDB 的现代替代方案:响应式查询、可插拔存储与无修订树开销的离线优先数据库 许多开发者起初因为 PouchDB 成熟的 CouchDB
数据库NoSQL嵌入式数据库实时数据库
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考