← 揭开黑盒:理解系统的调查课堂

第一章 · 系统的承诺:保存、状态与通信 / 第四单元 · 现代实现与技术路线(拓展)

现代保存:从一个文件,到日志与副本

前三个单元已经让我们沿代码解释一次保存。但如果这份笔记要给十万人使用,或者它记录的是一次付款,原来的实现还够吗?第四单元从这里拓展。我们沿着存储、状态、通信三条路线认识现代实现,最后交付一份有依据的方案选择。

预计用时:45 分钟。前置是完成第一章前三单元和综合解析;本节不要求安装一套分布式数据库。

先给熟悉的程序换一个要求

回看保存实验 D:应用调用 SQLite,事务提交后返回,再由页面读回。现在分别加入三个要求:同时有人修改同一条记录;服务进程在任意一步退出;承载数据库的整台机器不可用。

先写预测:当前哪项机制可能处理哪一种故障?其中至少有两个竞争解释值得区分:“只要有事务,机器坏了也能恢复”与“事务主要约束一组读写,恢复还依赖记录保存在哪些地方”。重新启动原服务可以观察进程退出后的持久化;它无法模拟磁盘完全损坏。我们应当把这两类证据分开。

文件很简单,规则会逐渐长出来

把全部笔记写进一个 JSON 文件,是一种可行的起点。一次完整保存可能是:读取旧文件,修改数组,写临时文件,再将临时文件替换到目标路径。

但替换时读者看到什么,两个写者会不会互相覆盖,数据何时真正到达持久介质,都需要新的约定。原子替换、持久化刷新和备份各有责任,不能仅凭“文件已经存在”推断它们全部成立。

数据库把很多这样的工作组织起来:表与索引负责定位记录;事务限定一组修改如何提交;锁或版本机制协调竞争;日志支持故障恢复。我们在 SQLite 中只写了几行 SQL,背后已经借用了这些实现。

为什么会先写日志

想象要改两个页面。如果直接覆盖原位置,改完第一页就中断,第二页怎么办?一条常见路线是先留下恢复所需的记录,再按协议推进修改与确认。不同数据库使用不同的日志组织与提交协议,不能用这一句话代替具体实现。

SQLite 的 WAL 模式把已变更的页面追加到 WAL 文件;读取者结合自己的读取边界查看数据库与日志,之后检查点把合适的内容写回数据库文件。它能改善某些读写并发,但并不意味着可以同时有任意多个写事务。

本课程的保存实验没有主动启用 WAL。要核对当前模式,可以在自己的实验副本中执行 PRAGMA journal_mode;。查清配置后才能说明实际路径。参考 SQLite WAL 官方说明,尤其留意检查点、读取者和持久化设置。

从“这台机器”走到“多台机器”

新要求常见路线新增的代价与需要验证的事
一个应用内可靠管理结构化数据嵌入式数据库,例如 SQLite文件与事务边界、并发写入方式
多个客户端共同访问数据库服务,例如 PostgreSQL网络失败、连接管理、权限与运维
承载大文件与海量对象对象存储配元数据数据库对象和元数据如何对应,跨系统写入如何恢复
一台机器不可用后继续服务主从复制、故障切换等方案副本延迟、确认条件、切换与旧主处理
修改后还要通知别的系统事务内记录待发事件,再转发转发可能重复,消费端仍需幂等

这些路线长期共存。嵌入式数据库适合本地应用,数据库服务适合共享访问;更新的技术不会使所有旧形式失去用途。

复制也不等于备份:误删可能迅速复制到所有副本。备份则需要保留能恢复到某一时刻的数据,并真正验证恢复过程。副本有几份、保存多久、恢复需要多久,是不同的问题。

沿着一笔修改走一遍

设想“新增笔记后发送通知”。若先写数据库再发消息,两步之间进程退出,笔记可能存在而通知没有发出。若先发消息再提交数据库,接收者又可能收到一条尚未成立的事实。

一种常见设计是在同一事务里保存笔记和待发通知记录。后台读取待发记录、发送、记下处理状态。发送后还没标记就中断,下一轮可能重复发送,因此消息编号与接收侧去重继续有用。这里增加的是可恢复的推进过程,仍要说明通知能否丢失、能否重复以及多久处理。

本节产物:一次有限的选型

为“个人离线日记”与“多人共同记账”各选一种起步方案,画出写入、确认、恢复路径。给每条箭头标一个可能中断点,写清已有机制如何处理。

验收不是方案名字是否足够新。复核者应能指出:数据落在哪里,成功确认发生在什么之后,重复请求如何识别,以及整机损坏时还有什么证据。若问题只要求本地离线保存,可以在验证文件恢复后停下;跨机容灾则需要另一组实验。

接下来进入 现代状态:从轮询到流与协作,看看这些已经写下的变化怎样到达用户眼前。