过去两年,并行 EVM 成了公链圈最热闹的话题之一。Monad、Sei、Aptos、Bitroot 都在讲同一件事:交易不再排队串行执行,而是尽量并发跑起来,冲突了再处理。这套思路听起来像是区块链行业的原创发明,但翻开数据库文献就会发现,它的骨架早在 1981 年就已经写好了。
那一年,康奈尔大学的 H. T. Kung 与 John T. Robinson 在《ACM Transactions on Database Systems》第 6 卷第 2 期发表了论文《On Optimistic Methods for Concurrency Control》,系统提出了乐观并发控制(Optimistic Concurrency Control,OCC)的完整框架。理解这篇论文的逻辑,再回头看今天的并行 EVM 方案,会发现区块链工程师其实是在一个新的约束条件下,重新实现了数据库领域四十多年前就已经想清楚的问题。这篇文章想把这条脉络理清楚:OCC 最初解决的是什么,它和悲观锁、MVCC 是什么关系,以及区块链在借用这套思想时,到底多加了哪些数据库世界不需要考虑的约束。
1981 年那篇论文在说什么
Kung 和 Robinson 面对的问题很直接:并发控制传统上依赖加锁,但锁的维护成本、死锁检测和调度开销在事务数量增多时会显著拖慢系统。他们提出了一个反直觉的假设:如果大多数事务之间并不真正冲突,那么完全可以先让它们并发执行,把检查冲突的工作推迟到最后一刻,而不是在一开始就为潜在的、也许根本不会发生的冲突支付锁的代价。论文原文把这种方法形容为依赖事务回退(transaction backup)作为主要控制手段,"寄希望于"冲突不会发生,这也是"乐观"这个词的来源。
具体的实现被拆成三个阶段。事务首先进入读阶段(Read Phase),在这个阶段自由地读写数据,但所有写入都只作用于事务私有的工作副本,不会立刻反映到共享数据库里。随后进入验证阶段(Validation Phase):事务在进入这一阶段时会被分配一个时间戳,系统据此检查这笔事务的执行结果是否仍然符合按时间戳排序的可串行化要求,也就是它读到的数据有没有在它读取之后、验证之前,被另一笔更早提交的事务修改过。验证通过后,才轮到写阶段(Write Phase),私有副本里的写入在这一步被真正合并进共享数据库。论文里进一步区分了串行验证和并行验证两种实现:前者把验证和写入当作一个不可分割的原子操作来做,后者则允许验证和写入并发进行,以换取更高的吞吐,但需要额外的机制保证正确性。
这套框架的价值不在于消灭冲突,而在于把冲突处理的时机从"执行前预防"改成了"执行后检查"。这个时机的挪动,后来成了几乎所有乐观并发系统的共同起点,包括今天的区块链并行执行引擎。
悲观锁为什么显得笨重
要理解 OCC 的吸引力,得先看看它替代的方案。两阶段锁协议(2PL)是关系数据库里最经典的悲观并发控制手段:事务在访问数据前必须先申请锁,在整个事务生命周期里锁只会越拿越多(增长阶段),直到提交或回滚才开始释放(收缩阶段)。这套机制的正确性很容易证明,但代价也很直观。锁表本身要占用内存和 CPU 时间去维护;事务之间如果按不同顺序申请同一批锁,就有死锁的风险,系统必须额外跑死锁检测或者用超时机制强行打断;长事务持有锁的时间越长,后面排队等待的事务就越多,整体并发度反而被压低。
OCC 的思路正好相反:不预先加锁,而是赌一把,赌大部分事务读写的数据集合互不重叠。如果这个赌注成立,系统完全跳过了锁的申请、维护和释放开销,吞吐会明显优于悲观方案。但赌注也可能落空:如果并发事务频繁读写同一批数据,大量事务会在验证阶段失败,被迫重新执行,重复劳动带来的浪费甚至可能超过悲观锁本该付出的成本。这也是为什么 OCC 从提出之初就被贴上了"低竞争场景更划算"的标签,这个判断在四十多年后的区块链场景里依然成立,只是"竞争"变成了"链上交易之间的读写集冲突"。
MVCC:另一条相关但不同的路径
如果关注过 PostgreSQL 或者 Oracle 的并发实现,一定听说过多版本并发控制(Multi-Version Concurrency Control,MVCC)。MVCC 和 OCC 经常被放在一起讨论,但两者解决问题的角度并不完全一样。MVCC 的核心特征是"读者永远不会阻塞写者":系统为每条数据保留多个版本,一个事务开始时会拿到一个一致性快照,之后无论其他事务如何修改数据,它看到的始终是快照那一刻的版本。PostgreSQL 的并发模型正是建立在这套快照隔离机制之上,读写之间几乎不产生互斥。
微软 SQL Server 的内存数据库引擎 Hekaton 则提供了一个把 MVCC 和 OCC 结合起来的例子。据微软研究院公开的技术资料,Hekaton 采用的是一种基于时间戳的乐观并发控制变体,叠加在多版本存储之上,并通过运行时代码生成为每类事务编译专用的执行逻辑,以降低解释执行的开销。另一个常被引用的案例是麻省理工学院团队在 2013 年发布的内存数据库 Silo,它用去中心化的时间戳做验证,并且刻意避免为只读记录产生任何共享内存写入,从而减少多核环境下的缓存一致性流量。后续的 TicToc(Yu 与 Pavlo 提出)进一步把时间戳的分配做成"事后计算"而不是"事前分配",让并发度不再受限于过早锁定的时间顺序。这些系统的共同点是:它们都承认单纯的悲观加锁在多核、高并发环境下代价过高,转而用版本管理和延迟验证去换取吞吐,只是具体怎么组合版本控制与验证逻辑,各家给出了不同答案。
这里有必要澄清一个容易混淆的地方:OCC 关注的是"检测冲突、决定是否回滚"的验证逻辑,MVCC 关注的是"如何存储和暴露数据的多个版本"。两者并不互斥,现实中的高性能系统往往是把某种版本管理方案和某种乐观验证策略叠加使用,而不是二选一。
区块链搬走了骨架,但换了游戏规则
把镜头切回区块链。Aptos 团队在论文《Block-STM: Scaling Blockchain Execution by Turning Ordering Curse to a Performance Blessing》(arXiv:2203.06871)中,明确把自家的并行执行引擎定位为软件事务内存(Software Transactional Memory,STM)与乐观并发控制思想的结合:交易先被乐观地并行执行,执行完之后再做验证,一旦验证发现冲突就中止并重新执行受影响的交易。这套流程和 Kung 与 Robinson 描述的读阶段、验证阶段、写阶段几乎一一对应。
但 Block-STM 论文特意强调了区块链场景和通用 STM、通用数据库 OCC 场景的一个关键差异:区块链上的交易在进入执行阶段之前,已经由共识层确定了一个全局顺序,这个顺序是不能改变的。传统数据库里的 OCC 通常不预设事务之间的先后关系,验证阶段需要动态判断谁先谁后;而区块链恰恰相反,顺序是既定的,执行引擎要做的是"在不违反这个既定顺序所隐含的因果关系"的前提下,尽可能把交易分散到多个核心上跑。Aptos 团队把这一点称作"把排序的诅咒变成性能的祝福":正因为顺序已经定死,系统可以用一种协作调度(collaborative scheduling)机制,让验证失败的交易只需要针对那个具体的读写冲突重新执行,而不必像通用 STM 那样处理任意到达顺序的事务。根据该论文披露的测试数据,Block-STM 在低竞争负载下每秒可执行超过 16 万笔非平凡的 Move 交易,在高竞争负载下仍能维持 8 万笔以上,相对完全串行执行的额外开销最高不超过 30%。这组数字具体成立与否高度依赖测试时的硬件配置、合约类型和冲突率设定,但它验证了一个方向性的结论:预先确定的交易顺序,确实可以被用来简化本该复杂得多的验证逻辑。
区块链独有的约束:所有人必须重放出同一个答案
如果只看到"先执行、后验证、冲突就重跑"这一层,很容易误以为区块链的并行执行只是把数据库 OCC 原样搬了过来。实际上还有一层数据库场景里不存在的要求,那就是确定性重放。
在单机数据库里,OCC 只需要保证同一个数据库实例内部的可串行化,不同数据库实例之间不需要对同一批事务给出逐字节相同的执行轨迹。区块链完全不是这么回事:一条链上成百上千个独立节点,各自拿到共识确定的同一份交易顺序后独立执行,必须得到完全相同的最终状态,任何一个节点算出不一样的结果,都会造成状态分叉,这是比性能损失严重得多的问题。这意味着并行 EVM 在设计冲突检测和重执行逻辑时,不能引入任何依赖线程调度顺序、系统时钟或者浮点运算的非确定性行为,否则同一笔交易在不同节点、不同并行度下可能算出不同结果。Bitroot 在自身文档中提到的"三阶段冲突检测机制"以及对状态根一致性校验的强调,本质上也是在为这条确定性红线服务:预执行阶段的依赖分析、执行中的实时冲突监控、执行后的状态根校验,层层叠加的目的都是确保无论并行度如何调度、重执行发生在哪个环节,最终写入区块的状态转换都和"假设交易严格串行执行"得到的结果完全一致。
这也解释了为什么区块链的并行执行工程比数据库 OCC 更复杂:数据库只需要保证可串行化这一个正确性标准,区块链除了可串行化,还要保证跨节点、跨执行环境的比特级一致,并且要在拜占庭容错的假设下防止恶意验证者通过操纵执行顺序或伪造状态根来牟利。乐观并行 EVM 里为什么要反复强调"确定性",根源就在这里。
从数据库直觉到链上工程
回到最初的问题:为什么要用数据库的 OCC 去理解区块链执行?因为这条脉络能帮工程师少踩很多重复的坑。读写集怎么定义、验证阶段该检查什么、冲突后是重跑整个批次还是只重跑受影响的交易,这些问题数据库领域已经反复验证了几十年,Silo、Hekaton、TicToc 给出的答案各有取舍,而区块链团队面对的选择空间并没有本质不同,只是多了一条"全网确定性重放"的硬约束,以及"拜占庭环境下不能信任任何单一执行结果"的额外前提。
理解了这套三阶段模型的来龙去脉,再去看 Bitroot、Aptos、Sei 这些项目各自的冲突检测细节,会更容易分辨哪些是工程实现上的取舍,哪些是数据库理论早就给出答案的基本功。下一篇会把镜头拉回 Bitroot 自身,说清楚它作为一条乐观并行 EVM 高性能 Layer 1,具体想解决谁的问题、边界在哪里。
