然而,对于 MySQL Group Replication Multi-Primary 模式,严格来说他的事务隔离级别是快照事务隔离级别(Snapshot Isolation,下简称:SI)。
今天,姜老师来聊聊数据库的快照事务隔离级别。
Snapshot Isolation 概述
很多 DBA 同学在面试的时候会被问到事务的隔离级别,然后会说Read Uncommitted、Read Committed、Repeatable Read、Serializable。
是的,但这四大事务隔离级别的定义姜老师更喜欢称其为古典事务隔离级别,即 ANSI SQL 92 定义的事务隔离级别。
ANSI SQL 92 定义的事务隔离级别依次解决 Dirty Read(脏读),Repeatable Read(不可重复读)、Phatom(幻读)。
在当时的学术界,认为只要解决了这三个问题,那么事务就是具有真正的隔离性。
最终推导出具有两阶段加锁的 Serializable 事务隔离级别可以保证完整的隔离性。
The fundamental serialization theorem is that well-formed two-phase locking guarantees serializability.
但是!后来 Hal Berenson、Jim Gray 他们在 1995 年发表了一篇新的论文《A critique of ANSI SQL Isolation levels》[1],用来批判 ANSI SQL的事务隔离级别。
在论文的摘要中,可以看到如下内容:
ANSI SQL-92 [MS, ANSI] defines Isolation Levels in terms of phenomena: Dirty Reads, Non-Repeatable Reads, and Phantoms. This paper shows that these phenomena and the ANSI SQL definitions failto characterize several popular isolation levels, including the standard locking implementations of the levels. Investigating the ambiguities of the phenomena leads to clearer definitions; in addition new phenomena that better characterize isolation types are introduced. An important multiversion isolation type, Snapshot Isolation, is defined.
在论文的总结部分,可以看到如下内容:
In summary, there are serious problems with the original ANSI SQL definition of isolation levels.
可以说,这篇论文应该基本上把 Jim Gray 在自己书中 《Transaction Processing: Concepts and Techniques》[2],对于事务隔离级别的定义进行”彻头彻尾“的批判。
论文的大意是除了 ANSI SQL 定义的三种并发问题,还有其他并发问题,如 Lost Update(P4)、Read Skew(A5A)、Write Skew(A5B)、New Phantom(A3B)等。
之前定义的事务隔离级别,除了 SRZ,都无法解决。
然后论文引出了新的事务隔离级别 SI ,相比之前除了 SRZ,SI 有着更好的隔离性。
Such applications will find Snapshot Isolation better behaved than either: it avoids the lost update anomaly, some phantom anomalies (e.g., the one defined by ANSI SQL),it never blocks read only transactions, and readers do not block updates.
为了简洁说明,这里仅举例 Read Skew 的问题,看下面的测试用例:
可以看到事务 T1 读取到了事务T2修改后的数据,因此不符合隔离性的要求。但是这个场景不在 ANSI SQL 定义的 Dirty Read,Repeatable Read、Phatom 范畴内。上述场景就是论文中定义的 Read Skew:
A5A Read Skew Suppose transaction T1 reads x, and then a second transaction T2 updates x and y to new values and commits. If now T1 reads y, it may see an inconsistent state, and therefore produce an inconsistent state as output. In terms of histories, we have the anomaly:
A5A: r1[x]...w2[x]...w2[y]...c2...r1[y]...(c1 or a1)
在 RC、RR 隔离级别下,是无法解决上述问题的,只有通过设置隔离级别为 SRZ。
但是 SRZ 隔离级别需要遵循两阶段加锁,即对每条读取到的记录加锁,可能会被写操作堵塞,因此使用 SRZ 隔离级别后,数据库并发性能较差。
然而, SI 事务隔离级别不会有 Read Skew问题,同时读取操作也不会阻塞变更操作。
简单来说,SI 有着更好的隔离性,以及比 SRZ 更好的性能,甚至可以比肩 RC 事务隔离级别。
BTW,论文中谈及的 (Basic)SI 隔离级别也没有解决 Write Skew 的问题。
但是在之后的论文《Serializable isolation for snapshot databases》[3], SSI (Serializable Snapshot Isolation) 彻底达到了事务隔离性的要求。
Snapshot Isolation 实现原理
论文《A critique of ANSI SQL Isolation levels》对于 SI 的实现原理做了大致介绍,原理还是非常简单的,主要是以下几个主要过程:
(1)事务T1读取第一条记录时,分配一个 Start-Timestamp ,这个值是单调递增的;
(2) 任何事务修改的记录会被写入到快照中,以便事务需要访问这些历史记录版本;
(3) 当事务T1提交时,会获得一个 Commit-Timestamp 。事务 T1 能提交的前提是不存在其他事务T2,修改事务 T1 中的变更的任何一条记录。原文:
The transaction successfully commits only if no other transaction T2 with a Commit-Timestamp in T1’s execution interval [Start-Timestamp, Commit-Timestamp] wrote data that T1 also wrote.
(4) 若事务提交时存在冲突,哪个事务先提交,则提交成功,这个机制称为:First-committed-wins。
从上面的实现原理看,SI 本质是一种乐观锁的机制,读不会因为写操作而阻塞,写只有在提交时才会进行冲突检测。
所以,若每个事务绝大部分情况下更新的记录都不冲突,则 SI 隔离级别有着极好的性能表现,也就是前面说的性能不输 RC。
那么,SI 就完美无缺了么?它的缺点是什么呢?
其实在生产环境中,他的缺点是比较致命的。
一方面,他需要假设事务绝大部分情况下更新的记录都不冲突,若存在热点,如类似秒杀这样的场景,则 SI 的性能会严重退化。
另一方面,对于每条记录的快照需要保存在内存中,以类似 RowVersion + Start-Timestamp 的形式存在。如若存在大事务,则需要较大的内存使用。因此,支持 SI 隔离级别的数据库,如 PostgreSQL 数据库,需要设置使用 SSI 隔离级别的内存使用量。当然,这个问题新版本 PG 貌似已经解决[3]。
MGR 与 SI
讲了这么多 SI 的知识点,其实现在大家就会发现 MGR Multi-Primary 模式的隔离级别本质是 SI。
首先,虽然 MGR Multi-Primary 模式是 Share Nothing 的架构,但其允许在多个节点中并发写入数据,我们要将 MGR 集群看成一个大的数据库实例。
其次,MGR Multi-Primary 模式是一种乐观锁机制,多个事务在并发提交的时,会在各节点上进行全局冲突检测。若存在事务之间有更新同一行的记录情况,则回滚事务。冲突检测的原理是基于WriteSet,回滚的原则依然遵循 First-committed-wins。
最后,MGR 需要严格控制事务大小,当事务太大时,Certification_info 会占用大量的内存,从而导致系统的不稳定。参数 group_replication_transaction_size_limit 用于控制事务大小,类似对于 SI 隔离级别内存使用上限的控制。
MySQL 源码也有对 SI 实现的简单说明:
总结来说,以前单实例数据库的 SI 隔离级别,事务提交时的冲突检测仅在单个进程中完成。
但对于 MGR Multi-Primary 这样的集群来说,事务交前,会通过 Paxos 协议发送 Certification_info 到 MGR 中的每个节点,然后再进行冲突检测。
其中,Certification_info 是一个map,由
typedef std::unordered_map<std::string, Gtid_set_ref *> Certification_info;
再次提醒,对于 SI 事务隔离级别来说,提交时可能会失败。
即在 MGR Multi-Primary 模式下,正常的提交可能失败!!!
ERROR 3101 (HY000) at line 4: Plugin instructed the server to rollback the current transaction.
一方面,在 MGR 中,事务提交失败并不代表数据库发生了故障,业务需要有重试逻辑(理解乐观锁机制)。
另一方面,业务侧一定要做好对于上述错误码的监控,如果很多,则表示你的 MGR Multi-Primary 模式使用姿势存在很大的问题。
总结
MySQL Group Replication Multi-Primary 是目前为止关系型数据库最伟大的产品,但很多同学并不能充分发挥其优势。
这就如拿着特斯拉 Model S 当燃油车去跑赛道,最后分数肯定不理想。
所以,理解 SI 隔离级别,是理解 MySQL Group Replication Multi-Primary 的第一步,也是充分发挥 MGR 全面潜力的第一步。
MGR,你准备好了么?
参考文献:
[1]. https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/tr-95-51.pdf
[2]. J. Gray and A. Reuter, “Transaction Processing: Concepts and Techniques”
[3]. https://courses.cs.washington.edu/courses/cse444/08au/544M/READING-LIST/fekete-sigmod2008.pdf
[4]. https://drkp.net/papers/ssi-vldb12.pdf