时序数据库MVCC机制:读写并发控制与一致性视图

尔悦

2026-10-28 /

并发控制有两条路线:基于锁的悲观控制,与基于多版本的乐观控制。锁的实现直观——写者加锁、读者排队——但在写入速率每秒数十万条的负载下,锁竞争会把查询延迟推向不可预测。MVCC(Multi-Version Concurrency Control)的思路相反:每次修改产生数据的新版本而非就地覆盖,读者访问一致性快照,读写互不阻塞。对写入密集的系统而言,MVCC几乎是必然选择,时序数据库也不例外。

多版本存储与一致性视图

MVCC的核心数据结构是版本链。每行数据携带版本号(通常与事务ID或时间戳绑定),修改操作追加新版本并链接到旧版本之上。读事务启动时获取一个快照点,随后只能看到该点之前已提交的版本——这构成一致性视图。快照的获取成本必须极低,通常是原子读取一个全局计数器,否则高并发下快照本身会成为瓶颈。

版本链的维护成本体现在两处。存储开销上,旧版本在被全部活跃快照释放前不可回收,长事务会阻止版本清理,造成存储膨胀。查询开销上,定位可见版本需要沿链遍历,链越长代价越高。这两个成本项决定了MVCC系统的两个关键守护参数:最老活跃快照的年龄上限,与版本垃圾回收的触发水位。

快照读的实现细节直接影响高并发表现。全局快照若由单一计数器保护,计数器本身会成为缓存行竞争热点;成熟的实现采用每线程本地缓存加周期性全局同步的方式削减竞争。读事务持有快照期间,版本回收器必须为其保留可见版本——这就是长查询阻塞垃圾回收的机制根源。防护手段是双重的:查询侧设最大执行时长,超时强制释放快照;回收侧设版本保留上限,超限则牺牲长查询的一致性保证。两个上限冲突时,写入密集系统通常选择保写入。

隔离级别的实现代价

隔离级别

读异常防护

MVCC实现方式

并发代价

读未提交

无

直接读最新版本

最低

读已提交

防脏读

每条语句取快照

低

可重复读

防不可重复读

事务级快照

中

可串行化

防幻读

快照+写冲突检测

高

级别越高,快照存活期越长,版本回收越滞后。通用关系型数据库默认读已提交或可重复读,是写少读多负载下的合理折中。

快照隔离(SI)是MVCC的主流实现,但它并不等价于可串行化:写偏斜(write skew)异常在SI下合法存在。两个事务各自读取一致快照、修改不相交的数据行、但联合违反某个跨行约束时,SI无法检测。时序场景的写入以互不交集的设备数据为主,跨行约束稀少,写偏斜的实际风险远低于OLTP——这是快照隔离在监控类负载中被广泛接受的工程理由,而非理论完备性证明。

追加写场景下的MVCC简化

时序负载有一个对MVCC极为友好的特征:以追加写为主,更新与删除稀少。监控数据、传感数据落库后极少回改,版本链在多数表上退化为单版本——多版本机制的实际开销趋近于零,而读写不阻塞的收益完整保留。性能瓶颈因此从版本管理转移到别处:快照分配的扩展性,与时间戳即版本的排序保证。

不少时序数据库索性以时间戳天然充当版本号:同一测点同一时间戳的重复写入,后到者覆盖先到者。这一简化放弃了通用语义下的多版本历史,换来写入路径上零冲突检测。代价是乱序写入的处理逻辑被推到存储层——延迟到达的数据需要插入已落盘的时间区间,而非简单追加。

乱序写入的处理深度影响版本设计。严格单版本模型下,迟到数据触发就地插入,已压缩的数据块需要解包重排;保留短窗口的多版本则允许迟到数据作为新版本追加,查询时再按时间戳归并。前者的写入放大与后者的查询放大形成对称取舍,多数引擎选择前者并辅以乱序容忍窗口——窗口之外的迟到数据直接拒绝或写入旁路表,由应用层决定补录策略。

批量写入的事务边界

时序场景的事务需求与OLTP不同。业务关心的原子性单位不是多行联合更新,而是单次批量写入:客户端一次提交的上万条记录,要么全部可见,要么全部不可见。半批可见的状态对下游聚合是灾难——按分钟窗口统计的均值会因半批数据而瞬间失真。

实现上,批量原子性与WAL天然耦合:一批写入对应一段连续LSN的日志记录,提交标记落在最后一条。崩溃恢复时,缺少提交标记的批次被整体回滚。TDengine在写入协议层面提供这一保证,客户端SDK的批量接口默认携带事务边界,服务端按批次提交、按批次可见。对调用方而言,需要关注的只剩批次大小的选取——过大的批次拉长单批提交延迟,过小则丧失批量IO的收益,工程上常见的折中点在几千到几万条之间。

跨表事务是另一个边界问题。严格意义上,监控数据的写入不存在跨设备事务需求——两台设备的温度读数之间没有一致性约束。但元数据变更例外:新增设备时的"建子表、写标签、注册采集配置"需要原子完成,否则查询侧会看到半初始化的设备。工程实现通常把元数据操作收敛到独立的系统表事务,与数据写入路径分离,避免高吞吐写入与低频但关键的元数据操作互相阻塞。

结语

MVCC在时序领域的演进方向是进一步收窄语义:放弃通用更新支持,换取写入路径的确定性延迟。选型评估时,与其核对隔离级别的标称支持列表,不如实测三项指标:长查询执行期间写入延迟是否稳定、批量提交的原子性边界是否清晰、乱序写入的吞吐衰减幅度。这三项覆盖了时序负载对并发控制的全部真实诉求。评估方法上还有一项常被忽略的测试:在持续写入下并发执行大跨度聚合查询,观察两者的延迟分布是否互相抬升。读写真正隔离的系统,两条曲线应当各自平稳;若查询期间写入P99明显上移,说明锁或资源竞争仍在关键路径上。