原问题:如何理解数据库的内部一致性和外部一致性?
前言
提问者应该是看完了 Google Spanner 的论文,对其中的外部一致性这个词不太了解,所以提出了这个问题。之前看 Spanner 的时候,我也不是很理解外部一致性的具体含义,只是当时并没有钻这个 "牛角尖"。最近突然想把这个问题弄清楚,所以整理了下面几位的回答。第一位作者的回答点明了 ACID 的 C 和 CAP 的 C 是不一样的。这里我想说的是,学习计算机的同学应该在不同的课程或书籍中都听过或看到过一致性这个词,但是它们涉及的领域及其含义却是完全不同的。包括分布式领域中的分布式事务的一致性和 Paxos 协议解决的一致性,这两者也不是一回事。在理解基础概念的区别后,再看第二位和第三位的回答便可对这个问题有清晰的认识。
作者鸣嵩的回答
ACID 的 C 和 CAP 的 C 是不一样的。
数据库教科书里提炼的,ACID 的 C 指的是从业务层面定义约束,例如银行转账场景,转入和转出金额要平衡,又或者外键指向的行必须存在,这个 C 一方面依赖数据库的保证,例如原子性,也依赖于业务特性和业务层代码实现。
CAP 的 C 是现代分布式系统中大家经常谈到的一致性,其内涵和外延比较丰富。例如,ACID 中的 I,在分布式系统中如何保证并发 Query 的隔离级别 Isolation,可以认为是一种一致性;多副本间达到一致状态机如 Paxos 和 Raft,也是一种一致性。
作者郁白的回答
最初在数据库文章中见到的 "外部一致性",是 Google 提出的,它是为了解释在分布式事务中遇到的问题而提出的。这里的一致性描述的是非 lock base 的事务引擎下,多个事务之间,数据如何相互可见的性质。
多个并发事务读写的数据(或数据范围),它们能够被数据库感知到冲突(读写之间也算冲突),因此它们在时间线上的顺序由数据库确定,这样的事务间一致性问题可以被认为是内部一致性问题,Ansi Sql Isolation 定义的隔离级别就是定义内部一致性的。
而先后执行(执行区间不重叠)的多个事务,它们的在时间线上的顺序由用户确定,数据库无法感知到他们之间的关系,它们之间的一致性问题可以被认为是外部一致性问题。
外部一致性问题在 lock base 或 mvcc 实现的单机数据库中并不存在,而在分布式事务中比较常见的不满足外部一致性的情况是 "写后读",即提交成功后不能立即能够被读到;还有一种更复杂的情况,可以称之为 "半写后读",比如客户端依次执行以下事务:
T1: write A;
T2: write B;
T3: read A and B;
如果没有全局时钟,那么就可能出现 T3 读取到最新的 B,但是没能读取到最新的 A(又或者是能读到最新的 A,却读不到最新的 B)。所以 "内部" 和 "外部" 最重要的区别是可见顺序由数据库(内部)确定,还是由用户(外部)确定。
作者欢歌的回答
对外部一致性的认知,主要来自于 Spanner 的 paper。写下自己读 paper 过程中的理解吧。个人认为,外部一致性描述的不是并发问题,而是在物理时间上先后发生的事务间的关系。外部一致性的目标是:假设事务 T1 执行完成后,T2 才启动,保证让 T2 看到 T1 的结果。
这个目标看起来比较好实现,因为 T2 开始时,T1 已经执行完成了。那为什么外部一致性成为 Spanner 的主要问题?主要来自两个方面:
(1) T1 和 T2 的时间戳可能来自位于不同 region 的 server(server 一般是某个 Paxos Group 的 Leader);
(2) 不同 region 间的 server 之间,时间没法严格同步,即存在误差。(虽然 Spanner 用了高大上的硬件以及配套算法来同步时钟,仍然不能绝对保证跨 region 的服务器的时钟严格同步。因为跨了延迟达到 100ms 的广域网。)
举个例子来讨论吧: 用户 A 给帐户 X 存款 10 万(对应事务T1),发生在中国;用户 B 查询帐户 X 的余额(对应事务 T2),发生在美国。我们把服务器的误差稍微放大点,比如10 s。
(1) 存款事务 T1 执行时,获取的提交时间戳为 t1;
(2) T1 执行完成后,A 打电话给 B,说钱已经存上了;
(3) B 去查询(事务T2),T2 获取的时间戳可能是 t2 - 5s; 由于 t2-5s < t1,按照时间戳比较,根本查询不到 T1 的结果(按照物理时间,T1 在 T2 启动前已经完成。二者不是并发的,但执行结果违背了物理时间上事务发生的顺序,即违背了外部一致性)。
原问题链接:https://www.zhihu.com/question/56073588