ElectricSQL 中的 Shapes 同步机制详解
什么是 Shapes
在 ElectricSQL 系统中,Shapes 是控制数据同步的核心概念。它允许开发者定义需要从云端 Postgres 数据库同步到本地应用的数据子集。
数据子集同步的必要性
想象一下,云端 Postgres 数据库中存储了大量数据,但通常我们并不需要将所有数据都同步到本地设备上。这既可能因为网络带宽限制,也可能出于数据隐私考虑,或者仅仅是本地设备存储空间有限。Shapes 提供了一种精确控制数据同步范围的方式。
Shapes 的组成要素
一个 Shape 由以下三个关键部分组成:
- 表定义:指定要同步的基础表
- Where 条件(可选):过滤需要同步的行
- 列选择(可选):指定需要同步的列
表定义详解
表定义是 Shape 的必选部分,它必须对应 Postgres 数据库中的一个实际表。表名可以简单如 projects
,也可以包含模式前缀如 foo.projects
。如果省略模式前缀,则默认使用 public
模式。
分区表支持
ElectricSQL 支持声明式分区表,既可以订阅整个分区表,也可以订阅特定分区。例如:
CREATE TABLE measurement (
city_id int not null,
logdate date not null,
peaktemp int,
unitsales int
) PARTITION BY RANGE (logdate);
订阅根表 measurement
会同步所有分区的数据,而订阅特定分区如 measurement_y2025m03
则只同步该分区范围内的数据。
Where 条件详解
Where 条件用于过滤表中需要同步的行,它必须是一个有效的 PostgreSQL 查询表达式。支持的特性包括:
- 数值类型、布尔值、UUID、文本等字段的比较
- 算术运算、比较运算、逻辑运算符等
- 位置参数占位符(如
$1
)防止 SQL 注入
但需要注意以下限制:
- 只能引用目标行的列
- 不能执行表连接或引用其他表
- 不能使用非确定性函数如
count()
或now()
列选择详解
列选择允许开发者精确控制需要同步的列。如果指定,则只同步列出的列;如果省略,则同步所有列。必须始终包含主键列,列名必须与数据库模式中完全一致,必要时使用引号。
Shapes 订阅机制
本地客户端通过订阅 Shapes 来同步数据,通常使用客户端库实现。同步服务维护这些订阅,并将新数据和数据变更流式传输到本地客户端。
HTTP API 方式
开发者可以直接使用 HTTP API 手动同步 Shapes:
# 初始同步请求
curl -i 'http://localhost:3000/v1/shape?table=foo&offset=-1'
# 实时更新请求
curl -i 'http://localhost:3000/v1/shape?table=foo&live=true&offset=...&handle=...'
TypeScript 客户端方式
更高级的方式是使用 TypeScript 客户端:
import { ShapeStream, Shape } from '@electric-sql/client'
const stream = new ShapeStream({
url: `http://localhost:3000/v1/shape`,
params: {
table: `foo`
}
})
const shape = new Shape(stream)
// 等待数据加载完成
await shape.rows
// 订阅数据变更
shape.subscribe(({ rows }) => {
// 处理变更数据
})
性能考量
Where 条件的评估对数据吞吐量有重要影响。ElectricSQL 将 Where 条件分为两类:
- 优化条件:特定形式的条件(如
field = constant
)可以高效评估 - 非优化条件:其他形式的条件
使用优化条件时,ElectricSQL 可以维持约 5,000 行变更/秒的吞吐量,不受 Shape 数量影响。而非优化条件下,吞吐量与 Shape 数量成反比。
优化条件类型
目前优化的条件包括:
field = constant
:对常量值的等值检查field = constant AND another_condition
:组合条件中的优化部分a_non_optimized_condition AND field = constant
:顺序无关的组合条件
当前限制
- 单表限制:目前 Shapes 仅支持单表,不支持旧版本中的包含树(include trees)功能
- 不可变性:Shape 定义一旦创建就无法修改,需要创建新订阅来改变同步数据
- 表删除处理:删除表时需要手动删除相关的 Shapes
实际应用建议
对于需要同步关联数据的场景,可以采用以下变通方案:
- 对于一级关联(如项目及其问题),可以订阅两个 Shapes:
projects where="id=..."
和issues where="project_id=..."
- 对于多级关联(如项目、问题和评论),可以反规范化设计,在底层表中添加项目 ID 字段
这些方案虽然增加了设计复杂度,但在当前版本下提供了可行的解决方案。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考