由表及里、由外到内是人们认识事物的普遍规律,所以从两个视角来观察:
外部视角,外部特性:分布式数据库是服务于写多读少、低延时、海量并发 OLTP(联机交易)场景的,具备海量数据存储能力和高可靠性的关系型数据库。
内部视角,内部构成:将技术细节收敛到产品内部,以一个整体面对业务应用。
对于分布式系统而言,一致性是在探讨当系统内的一份逻辑数据存在多个物理的数据副本时,对其执行读写操作会产生什么样的结果,这也符合 CAP 理论对一致性的表述。而在数据库领域,『一致性』与事务密切相关,又进一步细化到 ACID 四个方面。其中,I 所代表的隔离性(Isolation),是『一致性』的核心内容,研究的就是如何协调事务之间的冲突。因此,在谈论分布式数据库的一致性时,实质上是在谈论 数据一致性 和 事务一致性 两个方面。
数据一致性有一个前提,就是同时存在读操作和写操作。把两个因素加在一起,就是多副本数据上的一组读写策略,被称为『一致性模型』(Consistency Model)。
观察数据一致性有以下两个视角:
从状态的视角来看,任何变更操作后,数据只有两种状态:所有副本一致或者不一致。在某些条件下,不一致的状态是暂时,还会转换到一致的状态;而那些永远不一致的情况几乎不会去讨论,所以习惯上会把不一致称为『弱一致』。相对的,一致就叫做『强一致』。
强一致:在全同步复制模式下,主备库同步 binlog 时,主库只有在收到备库的成功响应后,才能向客户端反馈提交成功。客户端获得响应时,主备库的数据副本已经达成一致,后续操作没有问题,但这种模式的副作用非常大,主要有以下两点:
弱一致:NoSQL 产品是应用弱一致性的典型代表,但对弱一致性的接受仍然是有限度的,这就是 BASE 理论中的 E 所代表的最终一致性(Eventually Consistency),弱于最终一致性的产品就几乎没有了。
对于最终一致性,可以这样理解:在主副本执行写操作并反馈成功时,不要求其他副本与主副本保持一致,但在经过一段时间后这些副本最终会追上主副本的进度,重新达到数据状态的一致。
这个『经过一段时间』含义比较模糊,需要从操作视角分析。
最终一致性,在语义上包含了很大的不确定性,所以很多时候并不是直接使用,而是加入一些限定条件,也就衍生出了若干种一致性模型。因为它们是在副本不一致的情况下,进行操作层面的封装来对外表现数据的状态,所以都可以纳入操作视角。有以下几个模型代表:
综上所述,提到的一致性模型强度排序如下:
线性一致性 > 顺序一致性 > 因果一致性 > { 写后读一致性,单调一致性,前缀一致性 }
注:CAP 的 C 也是 Consistency,是多副本、单操作的数据一致性;而 ACID 里的 C 是指单副本、多操作的事务一致性。Paxos 这类共识算法,可以看作是复制协议的一种,虽然有时也叫做一致性协议,但这个一致性是指 Consensus。Consensus 是实现数据一致性目标下的具体技术,但并不是唯一的选择。
加餐:Aurora 不是分布式数据库,主要原因就是 Aurora 依然是不支持写入能力的水平扩展。Aurora 是亚马逊推出的云原生数据库,它采用计算与存储分离的思想,计算能力垂直扩展,存储能力水平扩展。究其原因,它的存储系统是直接架设在自家的分布式存储系统(S3)之上的;而计算节点仍然是单节点,所以是垂直扩展。当然 Aurora 也像 MySQL 一样是支持一写多读的,根据亚马逊的官方说明,可以配置 15 个备节点来分流读操作的压力。由于 Aurora 的元数据会缓存在主节点上的,在发生变更时,主备同步数据有一个小的延迟(小于 100 毫秒),这就造成备节点不能承接写入功能,读也不能保证严格的数据一致性。
NoSQL 以 BASE 为理论基础,BASE 其实代表了三个特性:
BASE 的意义只在于放弃了 ACID 的一些特性,从而更简单地实现了高性能和可用性,达到一个新的平衡。
在数据库中,『事务』是由多个操作构成的序列。1970 年詹姆斯 · 格雷(Jim Gray)提出了事务的 ACID 四大特性,将广义上的事务一致性具化到了原子性、一致性、隔离性和持久性这 4 个方面。
虽然 ACID 名义上并列为事务的四大特性,但它们对于数据库的重要程度并不相同。
第一个是一致性,它是其中存在感最低的特性,可以看作是对『事务』整体目标的阐述。它并没有提出任何具体的功能需求,所以在数据库中也很难找到针对性的设计。
第二个是持久性,它不仅是对数据库的基本要求。如果仔细琢磨下持久性的定义,就会发现它的核心思想就是要应对系统故障。可以把系统故障分为两种:
第三个是原子性,是数据库区别于其他存储系统的重要标志。在单体数据库时代,原子性问题已经得到妥善解决,但随着向分布式架构的转型,在引入不可靠的网络因素后,原子性又成为一个新的挑战。
最后一个是隔离性,它是事务中最复杂的特性。隔离性分为多个隔离级别,较低的隔离级别就是在正确性上做妥协,将一些异常现象交给应用系统的开发人员去解决,从而获得更好的性能。
可以说,事务模型的发展过程就是在隔离性和性能之间不断地寻找更优的平衡点。甚至可以说事务的核心就是隔离性。而不同产品在事务一致性上的差别,也完全体现在隔离性的实现等级上,所以必须搞清楚隔离等级具体是指什么。
最早、最正式的对隔离级别的定义,是 ANSI SQL-92(简称 SQL-92),它定义的隔离级别和异常现象如下所示:
虽然 SQL-92 得到了广泛应用,不少数据库也都遵照这个标准来命名自己的隔离级别,但它对异常现象的分析还是过于简单了。所以在不久之后的 1995 年,Jim Gray 等人发表了论文 "A Critique of ANSI SQL Isolation Levels"(以下简称 Critique),对于事务隔离性进行了更加深入的分析。
Critique 丰富和细化了 SQL-92 的内容,定义了六种隔离级别和八种异常现象。其中最关注的是快照隔离级别。因为在 SQL-92 中可重复读(Repeatable Read, RR)与可串行化(Serializable)两个隔离级别的主要差别是对幻读(Phantom)的处理。但随着 Critique 的发表,快照隔离被明确提出,这个说法就不适用了,因为快照隔离能解决幻读的问题,但却无法处理写倾斜(Write Skew)问题,也不符合可串行化要求。
因此,今天,使用最广泛的隔离级别有四个,就是已提交读、可重复读、快照隔离、可串行化。
Critique 对幻读的描述大致是这样的,事务 T1 使用特定的查询条件获得一个结果集,事务 T2 插入新的数据,并且这些数据符合 T1 刚刚执行的查询条件。T2 提交成功后,T1 再次执行同样的查询,此时得到的结果集会增大。这种异常现象就是幻读。
幻读与不可重复读都是在一个事务内用相同的条件查询两次,但两次的结果不一样。差异在于,对不可重复读来说,第二次的结果集相对第一次,有些记录被修改(Update)或删除(Delete)了;而幻读是第二次结果集里出现了第一次结果集没有的记录 (Insert)。一个更加形象的说法,幻读是在第一次结果集的记录『间隙』中增加了新的记录。所以,MySQL 将防止出现幻读的锁命名为间隙锁(Gap Lock)。
跟幻读相比,写倾斜 要稍微复杂一点,这里用一个黑白球的例子来说明。
首先,箱子里有三个白球和三个黑球,两个事务(T1,T2)并发修改,不知道对方的存在。T1 要让 6 个球都变成白色;T2 则希望 6 个球都变成黑色。
/* T1: */
begin;
update ball set color = white where color = black;
commit;
/* T2: */
begin;
update ball set color = black where color = color;
commit;
最终的执行结果是,盒子里仍然有三个黑球和三个白球。如果还没有发现问题,可以看看下面串行执行的效果图,比较一下有什么不同。
如果先执行 T1 再执行 T2,6 个球都会变成黑色;调换 T1 与 T2 的顺序,则 6 个球都是白色。
根据可串行化的定义,多事务并行执行所得到的结果,与串行执行(一个接一个)完全相同
。比照两张图,很容易发现事务并行执行没有达到串行的同等效果,所以这是一种异常现象。也可以说,写倾斜是一种更不易察觉的更新丢失。
既然『快照隔离』这么重要,为什么会被 SQL-92 漏掉呢?
这是由于 SQL-92 主要考虑了基于锁(Lock-base)的并发控制,而快照隔离的实现基础则是多版本并发控制(MVCC),很可能是由于当时 MVCC 的应用还不普遍。当然,后来,MVCC 成为一项非常重要的技术,一些经典教材会将 MVCC 作为一种独立的选择,与乐观并发控制和悲观并发控制并列。其实,在现代数据库中 MVCC 已经成为一种底层技术,用于更高效地实现乐观或悲观并发控制。有了 MVCC 这个基础,快照隔离就成为一个普遍存在的隔离级别了。
总的来说,分布式数据库大多可以分为两种架构风格,一种是 NewSQL,它的代表系统是 Google Spanner;另一种是从单体数据库中间件基础上演进出来的,被称为 Prxoy 风格,没有公认的代表系统。Prxoy 这个名字太笼统,没有反映架构的全貌,要有一个具体的架构模板来指代这种风格,这里选择 PostgreSQL-XC(PGXC)。
要搞清楚分布式数据库的架构风格,就要先了解『数据库』的架构。这里说的数据库仍然默认是关系型数据库。以下是一张数据库的架构图:
这张图从约瑟夫 · 海勒斯坦 (Joseph M. Hellerstein) 等人的论文 "Architecture of a Database System" 中翻译而来。文中将数据库从逻辑上拆分为 5 个部分:
单体数据库的功能看似已经很完善了,但在面临高并发场景的时候,还是会碰到写入性能不足的问题,很难解决。因此,也就有了向分布式数据库演进的动力。要解决写入性能不足的问题,大家首先想到的,最简单直接的办法就是分库分表。
分库分表方案就是在多个单体数据库之前增加代理节点,本质上是增加了 SQL 路由功能。这样,代理节点首先解析客户端请求,再根据数据的分布情况,将请求转发到对应的单体数据库。
代理节点需要实现三个主要功能,它们分别是客户端接入、简单的查询处理器和进程管理中的访问控制。
另外,分库分表方案还有一个重要的功能,那就是分片信息管理,分片信息就是数据分布情况,是区别于编目数据的一种元数据。不过考虑到分片信息也存在多副本的一致性的问题,大多数情况下它会独立出来。
显然,如果把每一次的事务写入都限制在一个单体数据库内,业务场景就会很受局限。因此,跨库事务成为必不可少的功能,但是单体数据库是不感知这个事情的,所以要在代理节点增加分布式事务组件。
同时,简单的分库分表不能满足全局性的查询需求,因为每个数据节点只能看到一部分数据,有些查询运算是无法处理的,比如排序、多表关联等。所以,代理节点要增强查询计算能力,支持跨多个单体数据库的查询。
随着分布式事务和跨节点查询等功能的加入,代理节点已经不再只是简单的路由功能,更多时候会被称为协调节点。
协调节点与数据节点,实现了一定程度上的计算与存储分离,这也是所有分布式数据库的一个架构基调。但是,因为 PGXC 的数据节点本身就是完整的单体数据库,所以也具备很强的计算能力。
相对于 PGXC,NewSQL 有着完全不同的发展路线。NewSQL 也叫原生分布式数据库,它的每个组件在设计之初都是基于分布式架构的,不像 PGXC 那样带有明显的单体架构痕迹。
NewSQL 的基础是 NoSQL,更具体地说,是类似 BigTable 的分布式键值(K / V)系统。分布式键值系统选择做了一个减法,完全放弃了数据库事务处理能力,然后将重点放在对存储和写入能力的扩展上,这个能力扩展的基础就是分片。引入分片的另一个好处是,系统能够以更小的粒度调度数据,实现各节点上的存储平衡和访问负载平衡。
分布式键值系统由于具备这些鲜明的特点,所以在不少细分场景获得了成功(比如电商网站对于商品信息的存储),但在面对大量的事务处理场景时就无能为力了(比如支付系统)。这种状况直到 Google Spanner 横空出世才被改变,因为 Spanner 基于 BigTable 构建了新的事务能力。
除了上述内容,NewSQL 还有两个重要的革新,分别出现在高可靠机制和存储引擎的设计上。
高可靠机制的变化在于,放弃了粒度更大的主从复制,转而以分片为单位采用 Paxos 或 Raft 等共识算法。这样,NewSQL 就实现了更小粒度的高可靠单元,获得了更高的系统整体可靠性。存储引擎层面,则是使用 LSM-Tree 模型替换 B+ Tree 模型,大幅提升了写入性能。
从系统架构上看,NewSQL 的设计思想更加领先,具有里程碑意义,而 PGXC 的架构偏于保守。但 PGXC 的优势则在于稳健,直接采用单机数据库作为数据节点,大幅降低了工程开发的工作量,也减少了引入风险的机会。总的来说,NewSQL 的长处在架构设计,PGXC 的长处则在工程实现。
加餐:预写日志(WAL)写成功,但是数据表写失败,要怎么处理?
事实上,对大多数的数据库来说,实时写入数据时,并不是真的将数据写入数据表在磁盘中的对应文件里,因为数据表的组织形式复杂,不像 WAL 那样只是在文件尾部追加,所以 I / O 操作的延迟太长。因此,写入过程往往是这样的:记录 WAL 日志,同时将数据写入内存,两者都成功就返回客户端了。这些内存中的数据,在 Oracle 和 MySQL 中都被称为脏页,达到一定比例时会批量写入磁盘。而 NewSQL 所采用的 LSM-Tree 存储模型也是大致的思路,只不过在磁盘的数据组织上不同。写入内存和 WAL 这两个操作构成了一个事务,必须一起成功或失败。
Jim Gray 等:A Critique of ANSI SQL Isolation Levels
Joseph M. Hellerstein et al.:Architecture of a Database System
加西亚 - 莫利纳 等:《数据库系统实现》
本文由 caroly 创作,如果您觉得本文不错,请随意赞赏
采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载 / 出处外,均为本站原创或翻译,转载前请务必署名
原文链接:https://caroly.fun/archives/分布式数据库一
最后更新:2022-02-07 13:37:05
Update your browser to view this website correctly. Update my browser now