背景知识
在开始阐述Aries是什么之前,需要先交代几个常识性的概念,作为引出Aries的铺垫。
数据库体系结构
图1大致描述了一个(分布式)数据库应该包含的组件,其中箭头方向大致描述了一个请求被处理的顺序。一个请求(以DML为例),会先经过Query Process层进行解析、优化处理,最终生成物理执行计划(就是用具体的算法替换逻辑计划里的各个算子),交由执行引擎Excutor Engine进行处理。接下来就是数据库最核心的部分了,Storage Engine存储引擎接收Excutor Engine发来的命令,执行实际的数据存取操作,Storage Engine会决定数据的物理组织形式、索引的类型等,Storage Engine中较为重要的几个模块有:Buffer/Cache Manager负责管理从磁盘加载哪些数据、哪些数据应该被缓存在内存中;File Manager用于管理磁盘空间的分配和决定数据在磁盘上存储的物理数据结构(索引的存储结构、行数据的存储结构等);Integrity Manager负责对数据进程完整性检查;Transaction Manager负责事务的管理,主要包含事务的并发控制(Concurrency Control)和故障恢复(Crash Recovery)。最终,存储在磁盘上的数据包含行数据、索引数据、统计数据和schema数据等。
图1 数据库系统结构
当然,数据库种类、功能繁多,无论是单机的还是分布式的,因此上图只是罗列出一些通用、基本的组件,生产中的数据库会在此基础上添加更多的模块,比如复制、备份、管控等。
事务
介绍完数据库的体系结构,接下来再回顾下数据库事务中常见的一些概念。2PC(两阶段提交)是大家耳熟能详的词汇了,和它类似的还有一个2PL(两阶段锁),那它们是什么关系呢?如果再加上MVCC呢?接下来,本文就对它们做一下简单的对比和介绍。
2PC是一种分布式事务的提交算法 ,其主要目的是为了保证分布式事务的原子性(Atomicity),即在分布式中有多节点参与的情况下,如何保证一个事务要么在所有参与节点中执行成功(事务提交),要么全部执行失败(事务回滚),虽然2PC本身有很多缺点(如协调器单点故障、参与者同步阻塞等),但是现实中它还是分布式事务的主要提交算法(当然有很多2PC的变种版本,如Percolator)。而2PL和MVCC( Snapshot Isolation是其一种实现)都是事务的并发控制算法(手段),用于保证事务的隔离性(Isolation),即所有的并行事务被以何种调度方式进行执行。我们都知道,事务是需要ACID四个属性的,前面我们只讨论了A和I,对C和D都没提及,对于C而言,在我的上一篇文章中已经解释过,本质上它不属于数据库本身的责任(区别于CAP理论中的C),因此本文不再赘述。而对于D而言,其表示持久性(Durability),意思是一个事务一旦提交成功,那么事务产生的影响必须是持久化的、稳定不变的。显然,现实环境中(特别是分布式、大规模集群环境下),机器硬件发生故障、内核崩溃和业务代码core dump是非常频繁的(后文我们统称为Crash),如何保证在任意时间点发生Crash,机器重启(包括业务进程)后数据库还能恢复到之前的一致状态(已经提交的事务执行redo保证事务的Durability,未提交的事务执行undo回滚保证事务的Atomicity),那么就是数据库中另一个重要的组件:Crash Recovery 来保证了。
可能有人会有疑问了,既然2PC主要用来保证事务提交的Atomicity,Crash Recovery貌似也可以保证Atomicity,那么它们之间有什么关系呢?其实,2PC更多的应于在分布式事务中,这里就要区分一下了。在分布式事务中,如图2所示,通常把一个分布式事务称之为全局事务(由协调器Coordinator和参与者Participants节点组成),而在每个参与者节点上也会存在本地的事务,通常称之为局部事务。全局事务一定是建立在局部事务的基础上的,因为如果任何一个Participant上的局部事务都无法保证,那何谈全局分布式事务呢?因此,本文讲的Crash Recovery主要是用来保证局部事务的Atomicity和Durability(但是有时候其实没有必要分的非常清楚,因为一旦保证了局部事务,那么自然全局事务也就保证了)。
图2 全局事务与局部事务
steal、force、redo、undo
经过前文的论述,数据库中Crash Recovery模块主要用来保证(局部)事务的Atomicity和Durability。那么它是怎么做到的呢?很多人肯定都知道redo log和undo log