本文提到的一些术语,比如Serializability和Linearizability,解释看Linearizability, Serializability and Strict Serializability。
本文中观点大部分都是参考了CockroachDB多篇官方blog,设计文档,代码以及相关资料,相对来说比较琐碎,而且有些地方没有交代的太清楚,这里尝试将这些资料融合起来。相信看完这篇文章,再看官方文档会更容易。
介绍
CockroachDB是一个支持SQL,支持分布式事务的ACID的分布式数据,支持ANSI SQL的最高隔离级别Serializability。
在一个分布式系统中,要支持Linearizability比较难,因为不同的机器之间时钟有误差,需要一个全局时钟。TiDB选择了和Percolator一样的方案,单点timestamp oracle提供时钟源。Google Spanner直接搞了一个基于硬件的TrueTime API提供相对来说比较精准的时钟。CockroachDB没有原子钟,也没有使用单点timestamp oracle,而是基于NTP来尽量同步机器之间的时钟偏移,NTP误差能达到250ms甚至更多,并且不能严格保证,这导致CockroachDB要保证Linearizability一致性很难,并且性能差。最终虽然CockroachDB支持Linearizability,但是官方不推荐。默认,CockroachDB支持Serializable隔离级别,但是不保证Linearizability。
Serializable
一个真实的数据库系统同一时刻会有很多并发的事务在执行,如何让这些事务觉得只有自己运行在数据库中不受其他事务的任何干扰是一个隔离级别的问题。Serializable就是不受任何干扰,弱一点的隔离级别有Repeatable Read, Read Committed, Read Uncommitted,Snapshot Isolation这些隔离级别多多少少会觉得受到了其他事务的干扰,如Repeatable Read有幻读问题,Snapshot Isolation有write skew问题,具体不赘述。可以参考a-critique-of-ansi-sql-isolation-levels
要实现一个支持Serializable隔离级别的数据库挺难的,很多数据库都不支持Serializable隔离级别,原因有几个,我觉得最重要的原因是性能不行。Oracle 11g默认隔离级别RC,最高隔离级别Snapshot Isolation,业界一些知名数据库对隔离级别的支持看When is "ACID" ACID? Rarely. 然而CockroachDB为了实现Serializable,花了大量的功夫。
一个事务通常包含多个读写操作,操作不同的行/列。数据库系统会对系统中的事务进行调度,事务会交叉执行,而不是一个接着一个。
一共三个事务,上图是数据库系统对这三个事务的一种调度。那么这个调度是不是Serializable的?这个有理论支持: serializability graph。这个理论引入了三种冲突,三种冲突都是对于不同的事务操作同一个数据而言:
RW: W覆盖了R读到的值
WR: R读到了W更新的值
WW: W覆盖了第一个W更新的值