Files
ChuSQL/docs/architecture.md
T

17 KiB
Raw Blame History

架构与组件

ChuSQL 采用客户端 / 服务端架构。正常运行时,只有数据库服务直接访问数据文件;离线初始化与恢复由 bootstrap 完成。

组件与访问路径

Web / CLI / 其他客户端
          ↓ TCP
    chusql-server
          ↓
  SQL 引擎 → Rust 存储层 → 磁盘
组件 职责
chusql-server 登录、会话、鉴权、SQL 执行和事务
chusql-web Web 管理界面,通过 TCP 访问服务
csql 命令行客户端,通过 TCP 执行 SQL
chusql-core/engine 解析、语义检查、优化及执行
chusql-core/storage catalog、页面、B+ 树、缓存、WAL 与持久化
csql-bootstrap 安装引导及离线系统目录修复、恢复、重置

服务默认监听 127.0.0.1:7777,Web 默认监听 127.0.0.1:7778。配置位置和平台数据目录见 config.md。

数据目录

[storage] data_dir 是持久化数据的根目录,诊断日志由 log_files 单独指定。

位置 内容
system/ 身份、凭据、角色、授权和预定义类型目录
databases/<数据库名>/ 业务数据库的 catalog、数据与索引
WAL 与检查点文件 已提交写入的恢复记录及刷盘进度

安装只初始化系统数据库,业务数据库由用户创建。表存在性以 catalog 为准,不能仅凭数据文件判断。

会话与并发

  • 每条 TCP 连接拥有独立会话,当前数据库和事务互不影响。
  • 读请求可并行,写请求由存储层协调。
  • Web 与 CLI 共享服务端的权限和执行规则,不直接读写数据目录。
  • Web 登录会话分别持有普通 API / SQL 连接与表格编辑连接,编辑事务按页面标识固定数据库。表格读取携带编辑标识时使用该事务快照;普通查询不进入编辑事务。
  • 同一 Web Cookie 的请求串行执行。编辑提交以固定保存点重放本地编辑集,失败整体回退;成功回执按提交标识去重。SQL 控制台事务另按页面与标签标识限制连接访问。

SQL 查询处理

查询依次经过解析、基于 schema 的语义检查、优化和执行。库名、表名、列名及别名在语义层解析;存储层使用精确的表名键。

能力 实现与边界
派生表 内层独立解析,必须有别名,不能引用外层列;输出列按别名暴露
WITH / CTE 解析时展开为派生表;不支持递归或物化,多次引用分别执行
ANY / ALL 按 OR / AND 的三值逻辑求值;空集的 ANY 为假、ALL 为真
子查询复用 Filter 和计算列按外层绑定分组;JOIN 条件按首次遇到的绑定惰性复用
成本估算 使用行数、不同值数和数值列的 16 桶等宽直方图
索引选择 优化器判断适用条件与成本,存储层管理实际索引

派生表可根据 schema 和透明算子估计行数并参与安全的连接重排;缺统计或可能报错时保守处理。输出列投影可下推,但保留所有计算项的求值,避免隐藏错误。内存存储提供实时行数和封顶去重统计。JOIN 条件的子查询在首次遇到外层绑定时求值,缓存仅限当前连接;空行对不触发求值,普通条件仍逐行计算。尚无半连接或通用物化。统计信息只影响计划选择,优化不能改变查询结果语义。

完整 SQL 写法及限制见 syntax.md。

快照事务

阶段 行为
BEGIN 将当前数据库读为会话内存快照
事务内执行 读取快照与自身修改,看不到其他连接后续提交
SAVEPOINT 保存当前快照;回滚可退回指定保存点
COMMIT 比对基线与现值,确认无写写冲突后原子应用差分
ROLLBACK 丢弃会话快照与保存点

有整数 id 的表按行比对并提交差分;其他表按整表替换提交。发生冲突时返回 serialization_failure 并保留事务,由调用方回滚或重试。事务内不支持 DDL 或切换数据库。

WAL 与崩溃恢复

  1. 一组写操作与提交标记写入 WAL,每条记录带递增 LSN。
  2. 数据和 catalog 刷盘后,检查点推进到已完成提交的 LSN。
  3. 按检查点截断日志前缀,保留尚未确认刷盘的记录。
  4. 重启时只重放检查点之后的已提交组,丢弃未提交组,并进行幂等检查。

建表、删表、建索引、删索引和数据修改均进入 WAL。LSN 跨重启续号,SystemStatus 的 last_lsn 表示检查点 LSN。

若系统目录损坏导致服务无法启动,使用离线 bootstrap。repair、recover 与 reset 的范围及操作步骤见 bootstrap.md。

页面与空间回收

  • 删除行先将槽位标记为墓碑。
  • 死空间达到页大小的四分之一时,随删除整理页内活行。
  • 整理保留槽位编号,索引中的页号与槽号仍有效。
  • compact 通常逐页整理;存在 DROP COLUMN 留下的隐藏列时整表重写。

不进行跨页填洞;没有 ORDER BY 时仍保留当前扫描顺序,但应用应使用显式排序获得稳定结果。

身份、授权与系统目录

系统表统一使用 _system 前缀,身份与凭据目录对普通语句隐藏。SUPERUSER 拥有最高权限,system_catalog_manager 提供受限目录管理能力;两者不通过角色成员关系继承。

机制 行为
角色继承 沿成员关系取得权限并集;拒绝自继承与环
对象授权 数据库 CONNECT / CREATE,表 SELECT / INSERT / UPDATE / DELETE;ALL 按作用域展开
转授权 WITH GRANT OPTION 允许将持有的权限授予其他身份
撤销 授予者撤销自身来源;所有者或最高权限身份管理对象授权,级联清理无合法来源的下游
稳定对象 catalog 分配数据库及表编号,授权与所有者绑定对象和身份编号,删除后不复用
所有者 建表者获得表所有权;启用角色可继承所有权,数据库所有权不隐含他人表的数据权限

对象权限目录 __system_object_acl 对普通语句和存储请求隐藏。权限快照按字符分块,在系统库写锁内比较版本并通过同一 WAL 组替换;服务启动恢复建表前持久化的创建者声明,旧名称授权只迁移一次到现存稳定对象。

类型目录说明见 types.md,身份字段与权限边界见 users-and-roles.md。

权限变更与会话失效

账号、属性和授权变化会标记受影响的在线连接。角色变化先展开成员及转授权来源关系,确定涉及的登录身份。共享读写门允许查询并行,并将授权及对象结构变更与查询排序;事务提交再次验证权限和稳定对象编号。

TCP 连接在下一次请求时返回 unauthorized 并关闭;空闲连接不被强行中断。权限逐条查询,避免继续使用旧授权。Web 会话还通过账号版本校验,口令或身份变更使旧凭据失效。

查询引擎扩展设计(尚未实现)

按 bound IR → 子查询物化 / 递归 CTE 推进;DOMAIN 约束见类型文档。以下设计尚未实施,当前能力见前文;实施状态以 agent-credientials/progress.txt 为准。

bound IR

列与作用域绑定

保留语法 AST 用于解析和诊断;语义检查产出独立 BoundStatement,规划、优化及执行逐步只接收它。列引用绑定为作用域编号、来源编号、列编号和类型,保留原始位置用于诊断;相关列明确标记所属外层作用域,不再通过字符串补限定名辨认身份。

表达式类型与错误语义

表达式节点携带结果类型、nullable、已解析运算符/函数标识和显式转换。没有证据证明安全时 may_error 为 true;优化不得删除或提前求值可能报错的表达式。未定型 NULL 只参与推导,最后绑定到上下文类型;不允许流入存储 schema。DOMAIN 身份保留在类型节点中,比较可以用底层语义,赋值需经过领域校验。

schema 与对象版本

绑定仅使用 schema,不能扫描数据或打开索引。catalog 解析对象后提供 schema 与版本;执行前再次验证稳定对象编号和 schema 版本。统计独立附着,不纳入绑定身份。内存存储使用与磁盘相同的绑定规则。

实施与验收

迁移顺序为查询表达式 → RelOp 输入 → 写语句和约束;过渡适配器单向转换且仅位于边界,不维护两套优化规则。验收用现有查询全集并补同名遮蔽、相关列、别名、NULL/DOMAIN 和错误时序回归;既有可观察顺序保持。

通用子查询物化

惰性执行与缓存身份

引入查询内 LazyMaterialize 算子:首次实际需要时执行,保留结果行序与重复;非相关查询每语句一次,相关查询按 bound IR 外层列绑定值缓存。缓存键沿用规范值相等、NULL 与领域底层语义,并绑定子计划和执行视图;不能跨语句、会话或权限版本复用。

求值语义

首版只物化已证明确定且无副作用的子计划;可能报错的表达式保持现有首次使用和短路路径,无法证明错误时序相同的改写禁用。空输入不触发子查询,标量多行仍在实际消费时报错。ANY/ALL、IN、EXISTS 各自保留三值及空集规则,不能用集合去重替代结果。

预算与回退

预算同时限制单缓存、查询总缓存和相关键数量;超过预算时在执行该次子计划前选择不缓存的原路径,不能重跑已执行一半的计划。磁盘溢写作为后续阶段,需明确临时目录、类型编码、权限及取消清理。EXPLAIN 展示物化范围与估算,运行统计不泄露缓存行值。

验收条件

验收比较启用/禁用的结果多重集、行序、首次错误、空输入、NULL、重复外层绑定、多次 CTE 引用、预算及取消;不确定函数不可复用。

递归 CTE

首版语法范围

保留现有非递归 WITH 行为;新增 WITH RECURSIVE 走绑定作用域,不在解析期无限展开。首版仅单一自引用、锚点 UNION ALL 递归项;自引用只能出现在递归项 FROM,锚点不可引用自身。互递归、多个自引用、递归项聚合/排序/LIMIT、外连接及非单调子查询明确拒绝。UNION 去重递归留后续设计,不能冒用 UNION ALL。

类型绑定与迭代

锚点和递归项输出列数相同;显式列名唯一,先由锚点确定类型,再校验递归项可赋值转换,不按运行数据扩宽。无法给锚点 NULL 定型时要求显式类型。执行用 worktable,递归项仅读取上一轮 delta,按轮次和每轮既有行序追加输出,保留 UNION ALL 重复。

资源预算与终止

拟定默认深度 1000、累计行数 100000、内存 64 MiB,并受服务端上限限制;达到任一预算返回 resource_limit,不返回截断结果。深度定义为递归项执行轮数;delta 为空正常终止。外层 LIMIT 首版不作为终止证明,先完整执行递归再消费,避免隐藏循环/错误。取消释放所有 worktable。

验收条件

验收覆盖零轮、多轮、重复路径、循环超限、NULL/类型冲突、空锚点、名称遮蔽、非法自引用和查询取消。EXPLAIN 明确递归与预算。

存储与事务扩展设计(尚未实现)

先引入稳定行编号和 MVCC,再实施跨页 compact 与并行写。前置条件是新页格式、版本化 WAL、离线升级工具及恢复验收。

新格式必须标版本,旧二进制拒绝打开。旧数据在保留备份的停服副本上升级、校验后发布,不混用新旧写入路径。每阶段先运行改动模块的规定测试入口,全量门禁由所有者运行;性能数据不能代替语义、恢复或权限验收。

行版本 MVCC

身份与可见性

引入库内单调 row_id,独立于用户 id、主键及物理槽号;删除不复用。每版本包含 row_id、版本编号、创建/结束提交序号与前版本定位,未提交版本只在事务私有工作集内。读视图固定 snapshot_csn,选取创建序号不大于快照且结束序号大于快照的版本,再合并自身修改。提交序号与 WAL LSN 分离,映射随提交记录持久化。

每连接仍只允许一个事务,保持快照隔离和保存点语义;不声称可串行化,写偏斜仍可能发生。写集记录读取的基线版本,提交验证该版本仍为最新;同 row_id 更新/删除冲突返回 serialization_failure。唯一键冲突在当前已提交状态和本次写集联合检查,不能仅用开始快照验证。无业务 id 的表也按 row_id 提交,不再整表替换。

索引键定位 row_id,索引查询再按快照检查版本;更新键保留旧键候选直到所有活跃快照不再需要,不能只索引最新值。扫描先保持旧格式导入的行序,更新不改 row_id 的扫描位置,新插入按持久化序号排序;不得借 MVCC 无意改变现有无 ORDER BY 输出。

提交、恢复与回收

首版仍串行提交:验证 → 分配 CSN → WAL 记录版本变化及提交 → fsync → 应用 → 统一发布可见 CSN。读者仅获取已完整应用的可见序号;持久化提交后应用失败继续采用 recovery_required 封闭请求,不能把已提交组当作失败撤销。不引入未提交脏页,因此首版保持 redo-only;若将来允许提前刷未提交页,必须另行设计 undo/状态恢复。

保存点记录私有写集边界与不可变根,回退不撤销其他事务版本。活跃快照登记器包括流式扫描和物化消费者;最老快照决定旧版本回收界线。回收同时处理 heap 和索引,读定位有 pin,取消/断线可靠注销。长事务受现有寿命及新增保留空间预算限制,不能为了回收静默推进其快照。

升级在停服副本中为旧行分配 row_id、初始版本和顺序,重建索引,验证行数/值/约束后切换;不得把重复业务 id 合并。新 WAL 与页格式显式版本化,截断仍遵循已刷盘检查点,不顺带改成页 LSN 检查点。

验收条件

验收包含重复/缺失业务 id、旧快照与键更新、删除重插、保存点、唯一并发冲突、写偏斜边界、长读回收、断线及提交每个持久化边界的崩溃恢复;复测 BEGIN/COMMIT 的传输和比较成本。

跨页 compact

定位与影子页

使用 row_id 到版本位置的存储内部定位映射,索引不保存可失效的页/槽定位。首版在表维护独占锁下进行,等待该表读 pin 释放;其他表仍可运行。以快照回收界线保留所有可能可见版本,按既有逻辑扫描顺序打包到影子页和定位映射,随后 WAL 记录并原子切换文件代次。

恢复与旧版本保护

旧代次直到无读引用且新代次检查点完成才删除;提交前崩溃丢弃影子文件,提交后重放切换。映射缺失、重复 row_id/版本和校验失败明确报错,不返回缺行扫描。隐藏列一并按当前 schema 改写,旧快照需要的结构不得提前删除。跨页整理不改变对象编号、行编号、值或输出顺序。

验收条件

验收验证前后全量扫描和索引点/范围查一致、快照旧版本、隐藏列、页损坏、空间不足、取消与文件切换崩溃;记录回收页数及 IO,不能只比较耗时。

并行写

实施顺序与锁范围

先允许事务私有写集准备并行,继续保留短串行 WAL/CSN 发布;第二阶段按稳定表编号与 row_id 排序取得写锁、唯一键锁。DDL/维护使用库结构锁,跨表事务必须一次取得完整锁集,禁止边执行边任意升级。权限读门在执行和最终复查期间保持现有语义;目录及 ACL 修改仍单独排序。

唯一键锁与取消

唯一键锁使用与值相等一致的规范编码,缺主键也使用 row_id 锁。等待有取消与超时,超时返回明确错误且保持/回退事务遵循现有错误分类;固定锁序避免内部锁环,不承诺支持任意用户锁或死锁自动重试。

提交发布顺序

允许不同表/行的验证和页面准备并行,但同一页短 latch、catalog 高水位、索引节点和 WAL 写入仍保护。发布 CSN 必须按提交顺序形成连续已应用前缀,后提交不能跨过未完成组而对读者可见。group commit 为后续性能阶段,不能改变每组原子性和持久化失败语义。

验收条件

验收覆盖同表不同行、同一行、唯一键冲突、跨表相反顺序、DDL/compact/撤权竞争、超时取消和 WAL 故障;用确定同步点断言并发结果,再单独测吞吐与等待时间。