SurrealDB 核心原理详解

拆解 SurrealDB 3.3.0 的核心机制:双模式表、记录链接与图关系建模、全文分析器分词链路、LIVE SELECT 实时订阅、事务与隔离,每项均配实测命令与真实回显佐证。

SurrealDBSurrealDB2026/10/17

SurrealDB 核心原理详解

‍‍​​‌​> 目标:拆解 SurrealDB(实测版本 3.3.0,Ubuntu 22.04.4 LTS)五个核心机制——双模式表、记录链接与图关系两种建模、全文分析器分词链路、LIVE SELECT 实时订阅、事务与隔离——每一项均以实测命令与真实回显佐证,并给出机制协作全景与常见误区。

一、环境信息

  • 操作系统:Ubuntu 22.04.4 LTS(x86_64)
  • 数据库:SurrealDB 3.3.0 stable,服务端 surreal start --user root --pass q1w2e3r4 --bind 0.0.0.0:8000 rocksdb:/opt/data/surrealdb/database.db
  • 数据目录:/opt/data/surrealdb(rocksdb 后端)
  • 实测通道:surreal sql(默认端点 ws://localhost:8000)与 HTTP POST /sql(curl -u root:q1w2e3r4 -H 'surreal-ns: shop' -H 'surreal-db: demo' http://127.0.0.1:8000/sql)
  • 实测库:命名空间 shop、数据库 demo;已有表 user(SCHEMALESS)、post(SCHEMAFUL)、关系边表 wrote、references
  • 本文所有回显均为上述环境真实执行结果,时间戳、随机 ID(如边 ID wrote:tn91oj6bzxzxpg9f5lt4)每次运行会不同

💡 技术拓展:本文与《SurrealDB SurrealQL 查询语言详解》分工不同——那篇按语句类型讲语法,本文按机制讲「为什么这样设计、引擎里发生了什么」。两篇共用同一套实测环境,命令可交叉复现。

二、机制总览

SurrealDB 把「文档、图、搜索、向量」四类能力压在一个 KV 存储抽象之上,关键设计有四点,全部可在实测中观察到痕迹:

  1. 统一 KV 底座:所有表、记录、索引最终都是 RocksDB 里的键值对(启动日志实测可见 Starting kvs store at absolute path rocksdb:/opt/data/surrealdb/database.db,以及 Background thread count: 12 等引擎参数)。表名、记录 ID 直接参与键编码,因此「跨模型」其实是同一存储上的不同读取路径。
  2. 版本化记录:每条记录是一个带版本戳的文档值(REVISION 层),更新即产生新版本,这为并发控制与未来的时间旅行查询提供基础。记录间通过 record<user> 这类 Record Link 或图边表互相引用。
  3. 图遍历全景:关系有两种形态——字段级 Record Link(一跳、可 FETCH 展开)与边表 RELATE(可带属性、可反向、可多跳)。图方向查询 ->/<- 由引擎在键空间上按前缀扫描完成,无需维护邻接表。
  4. 查询执行与索引:EXPLAIN 实测能看到执行计划选择(见 4.2 节,索引命中为 IndexScan,无索引为 TableScan 全扫),全文索引走 ANALYZER 分词后的倒排结构,BM25 打分在查询期计算。

上述四点在后续各节分别用真实回显展开。

三、SCHEMALESS 与 SCHEMAFUL 混用机制

本节验证同一数据库中两种表模式并存的行为差异:先看定义回显,再分别验证 SCHEMAFUL 的强约束与 SCHEMALESS 的可叠加约束。

3.1 同库并存与回显差异

实测在同一数据库同时定义两种表,INFO FOR DB 回显中可清楚看到模式差异:

DEFINE TABLE user SCHEMALESS;
DEFINE TABLE post SCHEMAFUL;
INFO FOR DB;
[{ accesses: {...}, analyzers: {...}, tables: { post: 'DEFINE TABLE post TYPE NORMAL SCHEMAFULL PERMISSIONS NONE', user: 'DEFINE TABLE user TYPE ANY SCHEMALESS PERMISSIONS NONE' }, ... }]

(输出节选:完整回显还包含 apis/buckets/configs/functions/models/modules/params/sequences/users 等键。)

要点:SCHEMALESS 表回显为 TYPE ANY SCHEMALESS,SCHEMAFUL 表为 TYPE NORMAL SCHEMAFULL。二者并存互不影响,按表粒度选择。

3.2 SCHEMAFUL 的强约束行为(真实拒绝回显)

SCHEMAFUL 表写入未定义字段直接报错。实测在 post(SCHEMAFUL)上创建带未定义字段 created_at 的记录:

CREATE post:set_up_db SET title = 'SurrealDB 数据库安装与初始化实战', content = '本文讲解数据库安装、初始化与持久化配置,涵盖 rocksdb 存储引擎要点。', author = user:alex, created_at = time::now();
["Found field 'created_at', but no such field exists for table 'post'"]

补上字段定义后同样语句成功:

DEFINE FIELD created_at ON post TYPE datetime DEFAULT time::now();
CREATE post:set_up_db SET title = 'SurrealDB 数据库安装与初始化实战', content = '本文讲解数据库安装、初始化与持久化配置,涵盖 rocksdb 存储引擎要点。', author = user:alex, created_at = time::now();
[[{ author: user:alex, content: '本文讲解数据库安装、初始化与持久化配置,涵盖 rocksdb 存储引擎要点。', created_at: d'2026-10-17T09:58:06.812611165Z', id: post:set_up_db, title: 'SurrealDB 数据库安装与初始化实战' }]]

3.3 SCHEMALESS 不等于无约束

实测证明 SCHEMALESS 表上 DEFINE FIELD 依然生效,且字段类型校验照常工作。user 表是 SCHEMALESS,但定义了 DEFINE FIELD age ON user TYPE option<int> DEFAULT NULL; 后,写入 age = NULL 直接被类型系统拒绝:

CREATE user:duplicate SET name = 'Dup User', email = 'alex@example.com', age = NULL;
["Couldn't coerce value for field `age` of `user:duplicate`: Expected `none | int` but found `NULL`"]

要点:option<int> 的「空值」语义是 NONE(字段缺省),显式写 NULL 会被拒绝——这是新手最常踩的类型坑。同表上的 UNIQUE 索引在 SCHEMALESS 模式下同样强制:

CREATE user:duplicate SET name = 'Dup User', email = 'alex@example.com', age = 22;
["Database index `idx_user_email` already contains 'alex@example.com', with record `user:alex`"]

3.4 拆解结论

  • SCHEMALESS:表结构自由演进,适合接入期与快速原型;字段定义、索引、断言仍可按需叠加(渐进式收紧)。
  • SCHEMAFUL:未定义字段一律拒绝,适合记录型业务(订单、账务)。
  • 混用策略实测可行:内容类表(如 user)先 SCHEMALESS,事实类表(如 post)直接 SCHEMAFUL;同一查询可 JOIN 两类表(FETCH 跨表展开正常)。

四、记录链接与图关系两种建模对比

SurrealDB 表达「A 关联 B」有两种方式:字段级 Record Link 与边表 RELATE。本节用同一组数据(user / post)实测对比。

4.1 方式一:Record Link(字段引用)

post.author 定义为 DEFINE FIELD author ON post TYPE record<user>;,写入时存引用:

SELECT id, title, author.name AS author_name, author.email AS author_email FROM post FETCH author;
[[{ author_email: 'alex@example.com', author_name: 'Alex Zhang', id: post:set_up_db, title: 'SurrealDB 数据库安装与初始化实战' }, { author_email: 'jamie@example.com', author_name: 'Jamie Li', id: post:graph_model, title: '图关系建模从入门到实践' }, { author_email: 'alex@example.com', author_name: 'Alex Zhang', id: post:query_lang, title: 'SurrealQL 查询语言语法速查' }]]

不写 FETCH 时该字段返回裸引用 author: user:alex;FETCH 后展开为完整记录。关系反向查询需手动写条件(SELECT * FROM user WHERE id IN (SELECT value author FROM post)),无原生反向遍历。

4.2 方式二:边表 RELATE(图关系)

RELATE user:alex->wrote->post:set_up_db SET role = 'lead', at = time::now();
[[{ at: d'2026-10-17T09:58:06.818700886Z', id: wrote:tn91oj6bzxzxpg9f5lt4, in: user:alex, out: post:set_up_db, role: 'lead' }]]

边记录自带随机 ID 与 in/out 端点,可携带任意业务属性(role、at)。方向遍历实测:

SELECT ->wrote->post.title AS titles FROM user:alex;
SELECT <-wrote<-user.name AS writers FROM post:graph_model;
[[{ titles: ['SurrealQL 查询语言语法速查', 'SurrealDB 数据库安装与初始化实战'] }]]
[[{ writers: ['Jamie Li'] }]]

多跳遍历(文章间引用边 references)实测:

RELATE post:graph_model->references->post:set_up_db SET note = '延伸阅读';
RELATE post:query_lang->references->post:set_up_db SET note = '相关篇目';
SELECT ->wrote->post->references->post.title AS chain FROM user:alex;
[[{ chain: ['SurrealDB 数据库安装与初始化实战'] }]]

FETCH 还可直接挂在图路径上,把边与目标记录一并取回(实测):

SELECT * FROM user:alex FETCH ->wrote->post;
[[{ age: 31, created_at: d'2026-10-17T09:58:06.805232168Z', email: 'alex@example.com', id: user:alex, level: 'senior', name: 'Alex Zhang', score: 97, tags: ['admin', 'dev', 'reviewer'], ->wrote: [{ at: d'2026-10-17T09:58:06.818700886Z', id: wrote:tn91oj6bzxzxpg9f5lt4, in: user:alex, out: { author: user:alex, content: '本文讲解数据库安装、初始化与持久化配置,涵盖 rocksdb 存储引擎要点。', created_at: d'2026-10-17T09:58:06.812611165Z', id: post:set_up_db, title: 'SurrealDB 数据库安装与初始化实战' }, role: 'lead' }, ...] }]]

(输出节选:->wrote 数组中只展示第一条边,其余以 ... 省略。)

4.3 选型对比结论

维度Record LinkRELATE 边表
关系属性(如 role/at)无法携带原生支持
反向查询手动子查询<-table<- 原生
多跳遍历不支持->a->b->c 原生(实测两跳)
FETCH 展开一跳展开路径展开(边+目标)
适用一对一归属(post.author)多对多、需元数据的网络关系(关注、引用、点赞)

实测建议:明确「归属」用 Link(省一次边表维护),明确「网络」用 RELATE(反向与多跳是硬需求)。二者可在同一模型中并存(本例 post.author 与 wrote 边同时存在)。

五、全文索引与 ANALYZER 分词链路

全文检索质量取决于分词链路,本节先实测分析器两级结构(TOKENIZERS/FILTERS)的真实分词输出,再验证倒排索引、BM25 打分与高亮的完整链路。

5.1 分析器定义与分词链路

分析器由 TOKENIZERS(切分)与 FILTERS(变换)两级组成,search::analyze() 可直接观察分词结果。中文实测的核心发现:class 分词器按 Unicode 词类切分,对连续中文不切分:

DEFINE ANALYZER zh_class TOKENIZERS class FILTERS LOWERCASE;
RETURN search::analyze('zh_class', '数据库系统原理实战指南');
[['数据库系统原理实战指南']]

因此中文检索必须叠加 NGRAM 类过滤器。实测两种:

DEFINE ANALYZER zh_ngram TOKENIZERS class FILTERS LOWERCASE, EDGENGRAM(2,5);
DEFINE ANALYZER zh_ngram2 TOKENIZERS class FILTERS LOWERCASE, NGRAM(2,5);
RETURN search::analyze('zh_ngram', '数据库系统原理实战指南');
RETURN search::analyze('zh_ngram2', '数据库系统原理实战指南');
[['数据', '数据库', '数据库系', '数据库系统', '据库', '据库系', '据库系统', '据库系统原', '库系', '库系统', '库系统原', '库系统原理', '系统', '系统原', '系统原理', '系统原理实', '统原', '统原理', '统原理实', '统原理实战', '原理', '原理实', '原理实战', '原理实战指', '理实', '理实战', '理实战指', '理实战指南', '实战', '实战指', '实战指南', '战指', '战指南', '指南']]
[['数据', '数据库', '数据库系', '数据库系统', '据库', '据库系', '据库系统', '据库系统原', '库系', '库系统', '库系统原', '库系统原理', '系统', '系统原', '系统原理', '系统原理实', '统原', '统原理', '统原理实', '统原理实战', '原理', '原理实', '原理实战', '原理实战指', '理实', '理实战', '理实战指', '理实战指南', '实战', '实战指', '实战指南', '战指', '战指南', '指南']]

EDGENGRAM 产出「左侧锚定」的 gram(每个 gram 从词元起点开始),NGRAM 产出全量滑窗 gram;对本例二者输出一致(整串 gram 覆盖),差异在更长文本下显现(EDGENGRAM 索引更小、只支持前缀式命中)。中英混排实测(class 先按词类切出英文单词,过滤器再处理):

RETURN search::analyze('zh_ngram', 'Hello SurrealDB World');
[['hello', 'hell', 'hello', 'surr', 'surre', 'surrea', 'surreal', 'surreald', 'surrealdb', 'urre', 'urrea', 'urreal', 'urreald', 'urrealdb', 'rrea', 'rreal', 'rreald', 'rrealdb', 'real', 'reald', 'realdb', 'eald', 'ealdb', 'aldb', 'worl', 'world', 'orld']]

5.2 索引定义、打分与高亮

DEFINE INDEX ft_post_title ON TABLE post FIELDS title FULLTEXT ANALYZER zh_ngram BM25(1.2, 0.75) HIGHLIGHTS;
DEFINE INDEX ft_post_content ON TABLE post FIELDS content FULLTEXT ANALYZER zh_ngram2 BM25;
SELECT id, title, search::score(1.0) AS score FROM post WHERE title @@ '数据库安装' ORDER BY score DESC;
[[{ id: post:set_up_db, score: 0.4721917510032654f, title: 'SurrealDB 数据库安装与初始化实战' }]]

@@ 匹配走倒排索引,search::score(1.0)(1.0 为索引引用序号)返回 BM25 相关性。高亮与偏移实测:

SELECT id, title, search::highlight('<b>', '</b>', 1.0) AS marked, search::offsets(1.0) AS offsets FROM post WHERE title @@ '数据库安装';
[[{ id: post:set_up_db, marked: 'SurrealDB <b>数据库安装与初始化实战</b>', offsets: { "0": [{ e: 21, s: 10 }] }, title: 'SurrealDB 数据库安装与初始化实战' }]]

组合过滤实测(标题命中 + 内容命中才返回):

SELECT id, title, search::highlight('<b>', '</b>', 1.0) AS highlighted FROM post WHERE title @@ '数据库' AND content @@ '图关系';
[[{ highlighted: 'SurrealDB <b>数据库</b>安装与初始化实战', id: post:set_up_db, title: 'SurrealDB 数据库安装与初始化实战' }]]

5.3 版本语法陷阱(实测确认)

SurrealDB 3.0.0 起,全文索引语法从 SEARCH ANALYZER 改为 FULLTEXT ANALYZER(官方文档 Note 明确说明)。实测旧写法直接报语法错误:

DEFINE INDEX ft_post_title ON TABLE post FIELDS title SEARCH ANALYZER zh_ngram BM25 HIGHLIGHTS;
 --< Parse error: Unexpected token `an identifier`, expected Eof
 --> [1:55]
  |
1 | ...LDS title SEARCH ANALYZER zh_ngram BM25 HIGHLIGHTS;
  |              ^^^^^^

而分析器过滤器拼写错误同样有清晰回显(真实踩坑记录):

DEFINE ANALYZER zh_ngram TOKENIZERS class FILTERS LOWERCASE, EDGNGRAM(2,5);
 --< Parse error: Unexpected token `an identifier`, expected a filter
 --> [1:62]
  |
1 | ...OWERCASE, EDGNGRAM(2,5);
  |              ^^^^^^^^

(正确拼写为 EDGENGRAM。)

六、LIVE SELECT 实现机制

本节用两条独立连接实测 LIVE SELECT 的订阅建立、变更推送与注销全生命周期,并归纳其服务端实现语义。

6.1 订阅的建立与推送观察

LIVE SELECT 为表(可带 WHERE 条件)建立实时订阅,服务端在 WebSocket 通道上推送变更事件。CLI REPL 是一条 WS 连接,可完整观察。实测步骤:连接 A(交互式)先发起订阅,连接 B 随后写入:

-- 连接 A
LIVE SELECT * FROM user;
[u'adb75583-e2ec-49ff-bbf1-c4e5644cfd52']
-- 连接 B(另一独立会话)
CREATE user:live_probe SET name = 'LiveProbe', email = 'live@example.com', age = 18, score = 1;

连接 A 立即收到推送(实测原文):

{ action: 'CREATE', id: u'adb75583-e2ec-49ff-bbf1-c4e5644cfd52', result: { age: 18, created_at: d'2026-10-17T10:00:26.478788350Z', email: 'live@example.com', id: user:live_probe, name: 'LiveProbe', score: 1 } }

要点:推送报文三要素——action(CREATE/UPDATE/DELETE)、id(订阅 UUID)、result(变更后的完整记录或记录 ID,取决于 LIVE SELECT DIFF 与否)。

6.2 订阅生命周期:KILL 注销

订阅 UUID 可用于 KILL 注销(实测在连接 A 执行,之后连接 B 再写入不再收到推送):

KILL u'adb75583-e2ec-49ff-bbf1-c4e5644cfd52';

6.3 实现机制说明(结合实测语义)

  • 订阅注册在服务端、绑定到创建它的那条连接(WebSocket 会话):CLI REPL、SDK WS 客户端均走 /rpc 的 WS 通道。
  • 写路径在事务提交后向匹配订阅广播事件(推送中能看到完整记录体,说明事件携带提交后的版本化记录)。
  • 带 WHERE 的订阅(实测 LIVE SELECT * FROM user WHERE name = $value; 可正常建立并返回 UUID)在服务端做条件评估,不匹配的变更不推送。
  • HTTP POST /sql 通道没有推送语义(响应是同步数组),实时能力只存在于 WS/SDK 通道。

七、事务与隔离

本节实测事务的三条终止路径(CANCEL/COMMIT/THROW)与 CLI 管道模式的隐式事务陷阱,并说明底层隔离语义。

7.1 单查询原子事务(实测回滚)

SurrealDB 的事务写在同一条查询里:BEGIN TRANSACTION; 与 COMMIT/CANCEL TRANSACTION; 之间的语句要么全部生效、要么全部回滚。实测三条路径:

BEGIN TRANSACTION; CREATE user:tx_test4 SET name = 'T4', email = 't4@example.com', age = 20, score = 50; UPDATE user:tx_test4 SET score = 66; CANCEL TRANSACTION;
SELECT * FROM user:tx_test4;
[NONE, 'The query was not executed due to a cancelled transaction', 'The query was not executed due to a cancelled transaction', NONE]
[[]]
BEGIN TRANSACTION; CREATE user:tx_test5 SET name = 'T5', email = 't5@example.com', age = 21, score = 50; COMMIT TRANSACTION;
SELECT * FROM user:tx_test5;
[NONE, NONE]
[[{ age: 21, created_at: d'2026-10-17T09:59:32.233976214Z', email: 't5@example.com', id: user:tx_test5, name: 'T5', score: 50 }]]
BEGIN TRANSACTION; CREATE user:tx_test6 SET name = 'T6', email = 't6@example.com', age = 22, score = 50; THROW 'abort_this_txn'; COMMIT TRANSACTION;
SELECT * FROM user:tx_test6;
[NONE, 'The query was not executed due to a failed transaction', 'An error occurred: abort_this_txn', 'Cannot COMMIT: the transaction was aborted due to a prior error']
[[]]

要点:CANCEL 显式回滚、THROW 异常中断均使事务内 CREATE 不落库;tx_test4 与 tx_test6 查询都返回空数组,回滚真实生效。

7.2 CLI 管道模式的隐式事务陷阱(真实报错)

把 BEGIN/COMMIT 分行喂给 surreal sql(管道模式)时,每一行都是独立的隐式事务,跨行 BEGIN/COMMIT 不生效。实测回显:

BEGIN TRANSACTION;
[NONE]
CREATE user:tx_test SET name = 'TxUser', email = 'tx@example.com', age = 20, score = 50;
[[{ age: 20, created_at: d'2026-10-17T09:57:49.971722154Z', email: 'tx@example.com', id: user:tx_test, name: 'TxUser', score: 50 }]]
UPDATE user:tx_test SET score = 60;
[[{ age: 20, created_at: d'2026-10-17T09:57:49.971722154Z', email: 'tx@example.com', id: user:tx_test, name: 'TxUser', score: 60 }]]
CANCEL TRANSACTION;
['Invalid statement: Cannot CANCEL without starting a transaction']

实测结论:该模式下 CREATE/UPDATE 已各自提交(记录实际落库),CANCEL 报错。多语句原子操作必须合并为一条查询(分号分隔写在同一输入行/同一请求体里),HTTP POST /sql 批量提交同理可保证原子性。

7.3 隔离语义说明

存储层为 RocksDB(LSM-KV,实测启动日志见 2.1 节),事务在 KV 层以快照方式执行:事务开始时获取一致视图,写操作在提交时批量落库,因此事务内读不到其他会话的中间状态,提交时与其他写事务串行化。对单机 rocksdb 后端,这是典型的快照隔离(Snapshot Isolation)实现;需要防「写偏斜」的场景应在业务层用 ASSERT 条件或唯一索引兜底(唯一索引冲突实测回显见 3.3 节)。TiKV 后端则复用 Percolator 分布式事务模型(乐观并发 + 中央时间戳),跨节点同样提供事务保证。

八、机制协作全景

一次典型写请求在引擎内的协作路径(每一步均有前文实测对应):

  1. 接入与鉴权:请求抵达 8000 端口(HTTP /sql 或 WS /rpc),系统用户/记录用户凭证解析出 $auth(root 会话实测返回 null,记录用户返回记录 ID 如 person:d4sdynsd70o0izqpily3)。
  2. 解析与路由:SurrealQL 解析为执行计划(版本语法差异实测:3.x 的 FULLTEXT ANALYZER 取代 SEARCH ANALYZER)。
  3. 权限评估:表级 PERMISSIONS 与角色(VIEWER 实测被拒 IAM error: Not enough permissions to perform this action)叠加判定。
  4. 事务包裹:单条查询原子执行(7.1/7.2 节实测),KV 层快照隔离。
  5. 写副作用:记录写入 KV;触发 DEFINE FIELD 的 DEFAULT/VALUE 计算(created_at DEFAULT time::now() 实测自动填充);UNIQUE/全文/向量索引同步更新(5.2 节倒排索引)。
  6. 事件分发:提交后向 LIVE 订阅推送(6.1 节实测推送报文),触发器(DEFINE EVENT)与 changefeed 同阶段执行。
  7. 读路径:SELECT 按执行计划选择 IndexScan/TableScan(EXPLAIN 实测),@@ 走 BM25 打分,->/<- 走图前缀扫描,FETCH 在结果集上二次取回。

💡 技术拓展:同为「单库多模型」,对比 PostgreSQL 的实现路径——PG 用 JSONB(文档)+ 递归 CTE(图)+ tsvector(全文)+ pgvector(向量)四套独立机制拼装,跨能力事务一致但要维护四种索引;SurrealDB 把四类读取路径建在同一 KV 之上,一套事务覆盖全部模型,代价是生态工具链较薄。选型权衡详见《SurrealDB 介绍与选型指南》第七章。

九、常见误区

以下三条误区均由本文实测证据直接支撑,逐条给出纠正结论。

9.1 BSL 许可商用限制

误区:「源码可见 = 随便商用」。实测 LICENSE(v3.3.0 发布件)关键条款:BSL 1.1 的 Additional Use Grant 明确不得将 Licensed Work 作为 Database Service 对外提供(即不能拿它做托管数据库服务卖给客户);内部业务系统使用不受该条限制。Change Date 2030-01-01,之后按 Change License(Apache 2.0)开放;且每个版本首次公开发布满四周年也自动转 Apache 2.0。合规动作:引用版本号记录首次发布日期;DBaaS 形态需联系 SurrealDB Ltd. 购买商业授权。

9.2 SCHEMALESS 滥用

误区:「SCHEMALESS 就是不用设计字段」。实测证明三件事:其一,字段定义在 SCHEMALESS 表上照样生效且类型校验严格(age = NULL 被拒,3.3 节回显);其二,索引照常强制(邮箱唯一冲突,3.3 节回显);其三,SCHEMALESS 表会默默接受任意新字段——长期演化后表内字段集漂移,查询投影与统计随之失控。实践建议:SCHEMALESS 表也要为核心路径 DEFINE FIELD;字段稳定后切换 SCHEMAFUL(DEFINE TABLE OVERWRITE post SCHEMAFUL;)。

9.3 LIVE SELECT 断线与重连

误区:「订阅一次,永久有效」。实测语义:订阅绑定在创建它的连接上(6.3 节),KILL 可主动注销(6.2 节实测)。连接断开(网络抖动、服务重启)后订阅随之失效,客户端重连后必须重新 LIVE SELECT 获取新 UUID,断线窗口内发生的变更需要业务侧补拉(重放 changefeed 或按游标 SELECT)。实践建议:SDK 层封装「重连 → 重新订阅 → 增量补拉」三步;对强一致推送诉求,优先依赖事务而不是事件推送。

十、参考链接