# PocketBase 底层技术架构深度分析 > 对标项目:PocketBase v0.37.1(Go) vs raisfast(Rust) > 目的:分析 PocketBase 的核心架构设计,识别值得借鉴的特性,制定实施优先级。 ## 一、架构概览 | 维度 | PocketBase (Go) | raisfast (Rust) | |------|----------------|-----------------| | 语言 | Go 1.25 | Rust 2024 | | 数据库 | 嵌入式 SQLite(modernc.org/sqlite,纯 Go 无 CGO) | 嵌入式 SQLite(sqlx + libsqlite3-sys) | | HTTP 框架 | 自研路由(`core.ServeEvent.Router`) | Axum 0.8 | | 二进制体积 | ~12MB(单文件可执行) | ~28MB | | 扩展方式 | Go 框架 / 内嵌 JS VM(goja) | Lua / JS / WASM 插件 | | 无状态 JWT | 不存储 token,纯 HS256 无状态 | 存储 refresh token 到 DB | | 实时推送 | SSE 内建 | 无 | | Admin UI | 内建(`/_/`) | 无(仅有博客前台) | ## 二、核心设计理念 ### 1. 极简单文件部署 PocketBase 的核心哲学是 **"下载即用"**:单个二进制文件包含所有功能(SQLite、Web UI、API server、Auth、文件上传),零配置启动。Go 的交叉编译特性使其天然适合这个模型。 ### 2. 双数据库架构 PocketBase 使用两个 SQLite 文件: - **`data.db`** — 业务数据(collections/records) - **`auxiliary.db`** — 日志和临时系统数据 这样做的好处是日志写入不会干扰业务查询的 WAL 锁,值得借鉴。 ### 3. Collection-Driven Schema PocketBase 的核心抽象是 **Collection**(等价于 raisfast 的 ContentType)。每个 Collection 有: - 字段定义(schema) - 5 条 API Rule(list/view/create/update/delete) - 自动的 CRUD REST API 这与 raisfast 的 TOML schema 驱动方式高度一致。 ## 三、值得借鉴的 6 个关键设计 ### 借鉴 1:表达式级 API Rule 系统(高优先级) 这是 PocketBase 最强大的功能。每个 Collection 的 5 条 Rule(listRule/viewRule/createRule/updateRule/deleteRule)都是**运行时表达式**,不是简单的公开/私有标志。 ``` // 示例:只返回已发布且作者为自己的文章 status = "published" && author = @request.auth.id // 示例:只有管理员可以删除 @request.auth.role = "admin" // 支持跨 collection 查询 @collection.orders.user ?= @request.auth.id ``` **PocketBase 的 filter 引擎**(`ganigeorgiev/fexpr`)支持: - 比较:`= != > >= < <=` - 模糊匹配:`~ !~`(LIKE) - 多值操作:`?= ?!= ?~`(ANY/AT LEAST ONE OF) - `@request.*` 上下文:auth user、body、query、headers - `@collection.*` 跨表引用 - `@now` 时间宏 - `:isset` / `:changed` / `:length` / `:each` 修饰符 - `geoDistance()` 地理距离函数 - `strftime()` 时间格式化 **raisfast 现状**:`check_api_access()` 只是简单的枚举(public/authenticated/admin/none),没有表达式级别的过滤。 **建议**:实现一个 `RuleEngine`,将 API Rule 解析为 SQL WHERE 子句。可以先支持基础子集: - 字段比较:`status = "published"` - 认证上下文:`@request.auth.id` - 逻辑组合:`&&` / `||` ### 借鉴 2:SSE 实时订阅(高优先级) PocketBase 内建了 **Server-Sent Events (SSE)** 实时订阅: ``` GET /api/realtime → 建立 SSE 连接 POST /api/realtime → 设置订阅主题 ``` - 订阅粒度:整个 Collection 或单条 Record - 自动权限检查:订阅 Collection 用 listRule,订阅 Record 用 viewRule - 5 分钟无消息自动断开(防泄漏连接) **raisfast 现状**:没有实时推送能力。 **建议**:基于 Axum 的 `tokio-stream` + SSE 实现。架构: - `EventBus` 已存在,可复用 - 新增 `/api/realtime` SSE endpoint - 客户端发送 `POST /api/realtime` 设置订阅 - 每次 CUD 操作后,通过 EventBus 广播 → SSE 推送 ### 借鉴 3:skipTotal 优化(中优先级,低工作量) PocketBase 的列表 API 支持 `?skipTotal=true` 参数,跳过 `COUNT(*)` 查询,`totalItems` 和 `totalPages` 返回 `-1`。 **为什么重要**:SQLite 的 `COUNT(*)` 在大数据集上会全表扫描。当用户只是翻页浏览时,第二页开始不需要总数。 **raisfast 现状**:每次列表查询都执行 `SELECT COUNT(*)`。 **建议**:在 `ContentQuery` 加 `skip_total: bool` 字段,`ListParams` 加 `skip_total` 参数,条件跳过 count 查询。 ### 借鉴 4:Batch API(中优先级) PocketBase 支持 `POST /api/batch`,在一个事务中批量执行多个 create/update/upsert/delete 操作: ```json { "requests": [ { "method": "POST", "url": "/api/collections/posts/records", "body": {"title": "test1"} }, { "method": "PATCH", "url": "/api/collections/posts/records/xxx", "body": {"title": "test2"} }, { "method": "DELETE", "url": "/api/collections/posts/records/yyy" } ] } ``` - 整个 batch 在单个事务中执行,失败全部回滚 - 减少 HTTP 往返次数 **raisfast 现状**:没有批量操作 API。 **建议**:新增 `POST /api/v1/batch` endpoint,接收请求数组,在单个 SQLite 事务中执行。 ### 借鉴 5:无状态 JWT(低优先级,架构差异) PocketBase **不存储任何 JWT token**: - Access token:HS256 签发后不存 DB - 没有 refresh token 机制 - "登出" = 客户端删除 token - `authRefresh` 接口不废止旧 token,只是返回新 token **raisfast 现状**:有完整的 refresh token 机制(存 DB),更安全但也更慢。 **建议**:这是架构取舍,不需要改。raisfast 的双 token 方案更适合需要强安全控制的场景(企业 CMS),这是差异化优势。 ### 借鉴 6:内置 Admin Dashboard UI(高优先级,差异化机会) PocketBase 的杀手级特性是开箱即用的 Admin UI(`/_/`),功能包括: - Collection 可视化管理(增删改字段) - Record 数据浏览和编辑 - API Rule 配置界面 - Auth 用户管理 - 日志查看 - 系统设置 **raisfast 现状**:前端 `web/` 是博客前台,不是管理后台。 **建议**:基于现有的 Next.js + shadcn/ui 搭建 Admin Dashboard,核心页面: 1. Collection Manager(字段拖拽编辑) 2. Record Browser(表格 + 筛选) 3. API Rule Editor(表达式编辑器) 4. 用户管理 ## 四、性能优化对照 | 优化项 | PocketBase | raisfast 现状 | |-------|-----------|--------------| | SQLite PRAGMA | busy_timeout=10000, WAL, synchronous=NORMAL, cache_size=-32000 | 已优化(Phase 1.1) | | JWT 缓存 | 每次解析但 HS256 开销极小 | 已缓存 DecodingKey(Phase 1.2) | | Filter→SQL 编译 | `fexpr` 解析为 AST → 直接映射 SQL | 尚未实现表达式过滤 | | 列表缓存 | 无内置缓存(纯 SQL) | DashMap TTL 缓存(Phase 1.3) | | 并发数据结构 | Go map + sync.RWMutex | DashMap + ArcSwap(Phase 2.1/2.2) | | Relation 批量化 | WHERE IN 批量查询 | 已实现批量 OneToMany/ManyToMany(Phase 2.3) | ## 五、PocketBase 核心依赖栈 | 组件 | 库 | 说明 | |------|-----|------| | SQLite 驱动 | modernc.org/sqlite | 纯 Go,无 CGO | | 数据库抽象 | pocketbase/dbx | 自研 query builder | | JS 引擎 | dop251/goja | ECMAScript 2020 | | 表达式解析 | ganigeorgiev/fexpr | 自研 filter 表达式引擎 | | JWT | golang-jwt/jwt/v5 | HS256 | | 密码哈希 | golang.org/x/crypto | bcrypt | | 图片处理 | disintegration/imaging | 缩略图生成 | | MIME 检测 | gabriel-vasile/mimetype | 文件类型识别 | | 邮件发送 | domodwyer/mailyak | SMTP | | CLI | spf13/cobra | 命令行框架 | | 文件监听 | fsnotify/fsnotify | 热重载 | ## 六、实施优先级建议 | 优先级 | 功能 | 工作量 | 用户价值 | |-------|------|-------|---------| | P0 | skipTotal 优化 | 1-2h | 立即提升列表查询性能 | | P1 | SSE 实时订阅 | 1-2d | 前端实时更新的核心能力 | | P1 | 表达式级 API Rule | 3-5d | 安全模型质变,对标 Strapi | | P2 | Batch API | 1-2d | 减少前端 HTTP 往返 | | P2 | Admin Dashboard UI | 1-2 周 | 可用性的关键差异 | | P3 | 双数据库分离 | 0.5d | 日志写入不影响业务性能 |