分布式数据库系统在逻辑上可以看作是一个完整的系统,用户就像在使用一个单机的数据库系统;但是,从物理的角度来看,它是一个网络系统,包括若干个物理上分散的节点,节点之间通过网络连接起来,通过网络协议交换数据。分布式数据系统需要处理网络故障和节点故障。网络故障会直接导致分区事件(CAP原理中的P,即网络故障导致网络被分割成多个子部分),影响系统的可用性;节点故障可能会导致单点故障,即当数据为单副本的情况下,节点故障会直接导致部分数据无法访问。为了避免单点故障,数据需要有多个副本,这样系统的可用性就大大提高了。节点故障也可以触发分区事件。除了上述问题,分布式数据库系统也可能带来不一致。比如staleread的问题,即读操作发生在数据项被更新之后。这时候应该读取到数据项的最新值,却读取到了旧值。出现这个问题的原因是分布式数据库系统没有统一的时钟,会导致读取数据出现倒序的情况。这种情况在单机系统中是不存在的。这里所说的不一致现象,以及与之类似的不一致现象,在这里统称为数据读取顺序不符合数据生成顺序,简称分布式不一致。为了解决分布式不一致性问题,许多学者经过广泛研究提出了多种分布式一致性概念,如线性一致性(linearizability)、顺序一致性(sequentialconsistency)、因果一致性(causalconsistency),以及GoogleSpanner的外部一致性(外部一致性)等。分布式数据库系统需要解决分布式不一致性问题,让观察者读取到满足一致性的数据,从而保证数据之间的逻辑始终有序。本节余下部分将讨论这个问题:首先讨论一般分布式系统面临的问题,然后讨论数据异常引起的一致性问题,最后讨论与分布式数据库相关的其他问题。分布式数据库系统面临的问题为了应对事务失败和管理事务,单机数据库系统提供了UNDO日志、回滚段等措施来实现事务回滚;为了处理系统故障,使用了WAL技术,日志的目的是在事务发生之前持久化存储;为了应对介质故障,专门提供了逻辑备份和物理备份等多种手段,目的是实现数据在数据级、日志级和物理数据块级的冗余存储。与单机数据库系统相比,分布式数据库系统除了存在上述问题外,还面临着更多的挑战。这些挑战源于分布式数据库系统的架构,它不同于单机数据库系统,因此在技术层面存在差异。1.Schema异常Schema异常是指由于数据库架构的原因,用户造成的数据异常。严格来说,这不属于数据库系统领域的数据异常。从用户的角度来看,事务一直在进行,但是在读写数据时,会出现类似的顺序问题和数据异常等问题,本书将此类异常统称为架构异常。架构异常与分布式架构有关。分布式架构包括一主一备架构、一主多备架构和多主多备架构。在分布式架构中,前端可能有一个类似代理的组件,为用户提供透明和高可用的服务。代理组件屏蔽了后端多个单机系统的故障。所有的操作都在一个事务中进行,模式抛出的异常也是数据异常。下面讨论了一种可能导致读取的数据不一致的已知模式异常。下面以MySQL的主从架构为例进行说明(其他数据库的类似架构也存在类似的隐患)。这种不一致就是这样产生的。MySQL支持Master-Slave架构。假设事务T在Master上执行。此时首先根据条件“score>90”进行查询,发现没有符合条件的交易,所以BinlogFile中的数据写入成功,假设为95(交易提交),然后复制该流程已关闭,导致复制失败。当Master重启时,会直接提交数据95,然后Master会异步拷贝数据95给Slave。不过此时,原来的Slave可能已经切换到master开始提供服务了。比如新事务写入数据98,但是原来Master上的95还没有复制到新Master上,这样就会造成两台MySQL主机数据不一致。如果主备MySQL服务前面有代理服务器,对于用户来说,这会屏蔽后台的主备服务,用户会认为“只有一个MySQL”提供服务,所以95%的数据丢失是的用户不能接受。还有一种情况,如果代理服务器在原Master宕机后并没有结束用户的事务T,而是将事务T连接到原机器上,将原机器换成新的Master。这时候对于新的Master来说,会发生两个事务,一个新的事务T1在某个WHERE条件下写入98,另一个是继续执行的原事务T。如果此时原来的事务T再次发起读操作(逻辑上还是在同一个事务中),你会发现你写的数据95%都消失了,这对用户来说是不能接受的。从分布式一致性的角度来看,这违反了“Read-your-writes”(读你写的)原则。从事务的角度来看,可能会出现“幻读”,即根据“score>90”条件再次查询,额外读取了事务T1写入的98,从而出现事务数据异常。和上面类似,官方也描述了MySQL上Master和Slave数据不一致的问题。如下图1所示,如果数据扩展为多个副本,则读操作扩展为允许从任意副本读取数据,写操作扩展为允许向任意副本写入数据。如果是去中心化架构(即没有单一全局事务管理机制),出现网络分区或延迟,从事务一致性和事务一致性的角度观察数据读写操作,会发现更复杂的问题分布式一致性。图1多副本异常图分布式算法和协议讨论了多副本情况下副本间数据同步和数据可见性的异常情况。使用的例子如图1所示,通过Leader节点记录到数据库中。事实结果是德国赢得了世界杯。但是当数据从Leader节点同步到两个不同的Follower节点时,Alice和Bob在同一个房间,从不同的Follower节点查询世界杯比赛新闻。没完没了的消息。两人得到的信息不同,于是产生了分歧。这也是分布式架构中支持Follower读的多副本导致的不一致问题。2.分布式一致性和事务一致性为了帮助大家充分理解分布式系统中存在的问题,我们不妨打个比方。如果世界上只有一个人,那么这个世界上的关系就很简单,但是一旦人多了,就会形成一个“社会”。其中,社会关系是指人与人之间建立的关系,这种关系会随着人数的增加而不断复杂化。这种复杂的社会关系与数据库的结合就是一个分布式数据库系统。社会中的人相当于分布式数据库系统中的一个物理节点或物理节点中的一份数据。图2以NewSQL系统的架构为例,描述了分布式数据库中存在的多个问题。由于分布式数据库需要存储海量数据,对数据进行分治,因此引入了数据分片的概念。从逻辑上看,每个节点的数据都是一个或多个数据分片,但数据库必须满足“高可用、高可靠”和在线实时服务的特点,所以每个数据分片都有多个副本。数据的多副本使得分布式数据库的“一致性”问题更加复杂。下面我们从读和写两个不同的角度来感性认识一下分布式数据库的不一致性。首先,图2所示的分布式数据库系统有四个数据分片——A、B、C、D,每个分片有三个副本,每个分片的三个副本中有一个是leader,另外两个是Follower(比如Raft分布式协议中的Leader和Follower)。图2 分布式数据库一致性问题的关系图其次,对于写操作,有两种情况如图2所示。1)写入单个数据分片—W1:这种情况下,一个事务不能操作在多个节点上,所以这样的事务是典型的单节点事务,类似于单机数据库系统中的事务。通过单节点上的事务处理机制可以保证写入单个数据分片具有ACID属性。为了实现写入单个数据分片的数据一致性,只能使用数据库系统中的并发访问控制技术,如2PL(Two-phaseLocking,两阶段锁定)、TO(TimestampOrdering,时间戳排序)),MVCC(MultipleVersionConcurrencyControl,多版本并发控制)等。2)写入多个数据分片—W2:通过一个事务写入多个数据分片,是典型的分布式事务。这时就需要分布式并发访问控制等技术来保证分布式事务的一致性,就需要使用2PC(Two-phaseCommit,两阶段提交)技术来保证跨节点写操作的原子性。另外,如果需要实现强一致性(详见5.6小节),还需要考虑在分布式数据库范围内保证ACID中的C和CAP中的C的强一致性的结合(即序列化)性与顺序一致性的线性一致性组合)。很多数据库系统,比如Spanner,都是采用线性一致性、SS2PL(StrongStrict2PL)技术和2PC技术来实现分布式写事务的强一致性。CockroachDB、Percolator等分布式数据库使用类OCC技术进行并发访问控制来保证事务的一致性(可序列化),并使用2PC来保证分布式提交的原子性,但都没有做到强一致性。其中,CockroachDB只实现了顺序序列化。保证分布式事务一致性的技术有很多,将在第4章详细讨论。对于写入多个数据分片的情况,由于每个数据分片内部有多个副本,如何保证副本之间的数据一致性也是一个典型的分布式系统一致性问题(第2章会详细讨论分布式系统的一致性问题,第3章会详细讨论共识算法加持下的多副本一致性问题),著名的Paxos、Raft和其他协议用于解决分布式系统的多副本共识问题。在这种情况下,如图1-6所示的A的Leader和B的Follower的组合通常不会发生写操作。如果一个系统支持多写操作,那么在多个数据分片的Leader上会同时发生多次写操作。对于读操作,也有如下两种情况如图2所示。2.1)读取单个数据分片——R1:如果一个事务只涉及单个节点,则必须保证事务读取操作的数据一致性(由节点上的事务机制保证)。如果涉及多个节点,此时R1会分为R11和R12两种读取模式。R11方法用于读取Leader:由于执行写操作时leader是最先写入的,如果写事务已经提交,可以保证R11读取的数据是最新提交的数据.如果写事务没有提交,那么如果此时Leader上采用了MVCC技术,R11就会读取olddata。这样的读取机制可以保证R11读取数据的一致性;如果Leader采用阻塞式并发访问控制机制,读操作会被阻塞,直到写事务提交,所以在这种机制下,R11读取提交的值,从而保证读数据的一致性。也就是说,在这种情况下,保证数据一致性依赖于单节点上的事务并发访问控制机制。同时,这也意味着分布式数据库系统中单节点的事务处理机制应该具备完整的事务处理功能。R12的方法用于读取Follower:读取Follower时,分为以下两种情况。在一个分片内,主从副本(即Leader和Follower)之间存在强同步(Leader向所有Follower同步数据,申请成功后返回结果给客户端)。这样的话,不管你读Leader还是Follower,数据必须是完全一样的,读到的数据必须是一致的。Leader和Follower之间存在弱同步(Leader将结果返回给客户端,无需等待所有Follower同步数据并应用成功)。如果采用多数协议,则可以实现弱同步。此时Leader和Follower之间写入数据会有延迟,即从Follower读取的数据可能是旧数据,但是由于事务的读取操作只涉及一个节点,所以不会有读取操作数据不一致的问题。.这就好比MySQL主备复制系统中备机可以提供只读服务一样。2)读取多个数据分片—R2:注意本例中的读取操作会跨越多个分片/节点。如果事务处理机制不当,就会出现不一致。这样的不一致问题可能是事务的不一致,也可能是分布式系统的不一致。下面仍以图1-6为例进行介绍。假设只读取了A和B两个数据分片,则有以下四种情况。读A的Leader和B的Leader,这种情况称为全L问题。事务的一致性:如果有一个全局的事务管理器,此时读取多个数据分片的操作就像在单机系统中读取数据一样。通过阻塞并发访问控制协议或MVCC(全局快照点)等技术,可以保证读取操作过程中不会出现数据异常。因为其他事务的写操作会给本事务的读操作带来数据不一致,所以可以通过全局并发访问控制协议(比如全局阻塞并发访问控制协议等技术)来避免事务层面的数据不一致问题。但是如果没有全局的并发访问控制协调器,很容易出现跨节点的数据异常,所以需要通过特定的并发访问控制协议来控制。分布式系统的一致性:这种问题只存在于“读取A的Leader和B的Leader”的结构中。分布式数据库需要通过实现“强一致性”来避免分布式和并发带来的分布式问题。事务数据系统中的一致性问题。具体可能出现的问题将在第2章介绍。阅读A的Leader和B的Follower,这种情况称为LF问题。B的Leader和Follower之间存在延迟,即传输存在延迟,导致主备复制数据不一致。如果支持“读A的Leader和B的Follower”的方式,需要保证读节点(A的Leader节点,B的Follower节点)上有一个共同的事务状态。阅读A的Follower和B的Leader。这种情况称为FL问题。问题的分析和解决方法同上。阅读A的Follower和B的Follower,这种情况称为全F问题。问题的分析和解决方法同上。如果读取数据时同时存在事务一致性和分布式系统一致性问题,那么就需要使用强一致性来解决。一般来说,事务的一致性是由于并发事务之间对同一数据项的并发访问(读、写、读、写冲突)造成的,而分布式一致性是由于多个节点分散,节点使用自己的时钟,以及在单个节点上发生的操作没有顺序。本书节选自《分布式数据库原理、架构与实践》,经出版社授权发布。
