当前位置: 首页 > 科技观察

分布式数据库:如何保证分布式场景下数据的高性能?

时间:2023-03-19 19:20:41 科技观察

一致性是高可用的先决条件在现实世界中,分布式数据库的节点并不总是处于活动状态并且能够相互通信。但是,上述故障应该不会影响数据库的可用性。换句话说,从用户的角度来看,整个系统必须继续运行,就好像它没有遭受任何故障一样。系统高可用是分布式数据库的一个极其重要的特性,即使在软件工程中,我们也总是力求实现高可用和最小化停机时间。为了使系统具有高可用性,系统需要设计为能够容忍一个或多个节点的崩溃或无法访问。为此,我们需要引入上一讲提到的复制技术,其核心是利用多个冗余副本来提高系统的可用性。然而,一旦添加了这些副本,我们就面临着保持数据的多个副本同步,以及在故障后恢复系统的问题。这就是MySQL复制开发中引入的RPO概念,即不仅要保证系统可用,还要求数据一致。因此,高可用性必须尽可能满足业务连续性和数据一致性这两个指标。而我们即将介绍的CAP理论会告诉我们还有第三个因素——网络分区会影响可用性。它会告诉我们,在网络分区下,可用性和一致性是不能同时满足的。CAP理论和注意事项首先,可用性衡量系统成功处理每个请求和响应的能力。可用性被定义为用户感知的系统的整体响应能力。但实际上,我们希望组成系统的各种组件保持可用。其次,我们希望每个操作都是一致的。一致性在这里被定义为原子一致性或可线性化的一致性。线性一致性可以理解为:在分布式系统中,所有相同副本上的操作历史可以看作是一个日志,它们的操作在日志中的顺序是相同的。线性化简化了系统可能状态的计算,并使分布式系统看起来像是在一台计算机上运行。最终,我们希望在容忍网络分区的同时实现一致性和可用性。网络非常不稳定,往往会分裂成多个独立的子网。在这些子网中,节点无法相互通信。在这些分区节点之间发送的某些消息将无法到达目的地。所以总结一下,可用性要求任何无故障的节点都能提供服务,一致性要求结果需要线性一致。EricBrewer提出的CAP理论讨论了一致性、可用性和分区容错性之间的权衡。提到异步系统无法满足可用性需求,在存在网络分区的情况下,我们无法实现既保证可用性又保证一致性的系统。但是,我们可以构建一个保证强一致性的系统,同时尽量保证可用性;或保证可用性同时尽最大努力确保一致性的系统。这里所说的“尽力而为”是指如果一切正常,系统可以提供这个属性的保证,但是在网络分区的情况下,允许削弱和违反这个保证。也就是说,CAP描述的是一种组合选择,即存在取舍。从CAP理论的定义来看,我们可以有如下系统。CP系统:一致且分区容忍的系统。宁愿减少服务时间而不是提供不一致的数据。一些为事务场景构建的NewSQL数据库倾向于采用这种策略,比如TiDB、阿里云PolarDB、AWSAurora等,但是他们自己生成A,也就是高可用。AP系统:可用且分区容忍的系统。它放宽了一致性要求,并允许在请求期间提供可能不一致的值。一般列式存储,NoSQL数据库倾向于AP,比如ApacheCassandra。但是他们会通过不同级别的一致性模式调整来提供高一致性的解决方案。CP系统的场景实现思路是引入共识算法,需要大多数节点的参与来保证一致性。如果要一直保持一致,那么在网络分区的情况下,可能会有一些节点不可用。AP系统只需要一份启动,数据库会一直接受写和读服务。它最终可能会丢失数据或产生不一致的结果。这里可以使用client模式或者Session模式来提供一致的解决方案。使用CAP理论时需要注意一些限制。CAP谈论的是网络分区,而不是节点崩溃或任何其他类型的故障。这意味着网络分区后的节点可能会接受请求,从而导致不一致。但是崩溃的节点将完全没有响应,并且不会出现上述不一致的情况。换句话说,并非所有分区节点都会面临不一致。相反,网络分区不能包含真实场景中的所有故障。CAP意味着即使所有节点都在运行,我们也可能会因为它们之间的连接问题而出现一致性问题。CAP理论经常用一个三角形来表示,好像我们可以任意匹配三个参数。然而,虽然我们可以调整可用性和一致性,但分区容错性是我们实际上不能放弃的。如果我们选择CA而放弃P,那么当分区发生时,为了保证C,系统需要禁止写入。即有写请求时系统不可用。这与A冲突,因为A要求系统可用。所以分布式系统理论上不可能选择CA架构,只能选择CP或者AP架构。如下图所示,其实CA系统是不存在的,这里需要特别注意。图1CAP理论CAP中的可用性也不同于上面提到的高可用性,CAP定义对请求的延迟没有任何限制。此外,与CAP不同,数据库的高可用性不需要每个在线节点都可用于服务。CAP中的C代表线性一致性。除了它之外,还有其他一致的模式。现在让我们详细介绍它们。一致性模型一致性模型是分布式系统的经典内容,也是分布式数据库入门的重要知识点。但是很少有人知道,一致性模型其实来源于单机理论中的共享内存。从用户的角度来看,分布式数据库就像一个共享存储的单机数据库,节点之间的通信和消息传递都隐藏在数据库内部,会给用户一种“分布式数据库是共享内存”的错觉.支持读写操作的单个存储单元通常称为寄存器,我们可以将代表分布式数据库的共享存储看作是一组这样的寄存器。每个读写寄存器的操作被抽象为“调用”和“完成”两个动作。如果操作在“调用”发生之后但在“完成”之前崩溃,我们将操作定义为失败。如果一个操作的调用和完成事件都发生在另一个操作被调用之前,我们说这个操作先于另一个操作,并且这两个操作是顺序的;否则,我们说它们是并发的。如下图所示,a)是顺序操作,b)和c)是并发操作。图2顺序和并发操作多个读取或写入操作可以同时访问一个寄存器。对寄存器的读写操作并不是瞬间完成的,需要一定的时间,即调用和完成两个动作之间的时间。不同进程执行的并发读/写操作不是串行的,并且根据寄存器在操作重叠时的行为方式,它们可能处于不同的顺序并可能产生不同的结果。当我们讨论数据库一致性时,可以区分两个维度。迟滞。这是数据更改的时刻,也是数据被其副本接收的时刻。这就是上一讲介绍的复制延迟场景,一般归类为“客户端一致性”。我们将在“15|重访一致性:除了CAP之外还有哪些一致性模型”中进一步讨论这个问题。顺序。讨论的是在系统的所有副本上执行各种操作的顺序状态。这就是本讲一致性模型的重点。现在让我们仔细看看顺序性。当面对一系列的读写操作时,作为人类,我们对它们的执行顺序有一个主观的判断。甚至,对于一个单机数据,这些操作的顺序是可以确定的。然而,在分布式系统中做出这样的判断并不是那么容易,因为很难知道到底发生了什么,也很难跨集群一次性同步这些操作。为了推理操作顺序并指示真实结果,我们必须定义一个保证顺序的一致性模型。我们如何理解模型中“保证”的含义呢?它将一致性模型视为用户与数据库之间的一种约定。每个数据库副本如何满足此顺序保证?用户在读写数据时期望什么?也就是说,即使同时读取和写入数据,用户也会得到一些可预测的结果。需要注意的是,我们将讨论单对象和单操作的一致性模型,但真正的数据库事务是多步操作,我们将在下面的“事务和一致性”部分进一步讨论。下面我将按照从强到弱的顺序保证来介绍一致性模型。严格一致性严格一致性类似于没有复制:任何节点上的任何写入都立即可用于所有节点上的后续读取。涉及到全局时钟的概念。如果任何节点在时间T1写入新数据A,则所有节点都应在时间T2读取新写入的A(T2满足T2>T1)。不幸的是,这只是一个理论模型,无法在现实中实现。由于各种物理限制,分布式数据不可能在瞬间同步这种变化。线性一致性线性一致性是最严格的、可实现的单对象单操作一致性模型。在这种模型下,写入的值可以在调用和完成之间的某个时刻被其他节点读取。并且所有节点读取的数据都是原子的,即不会读取数据转换的过程和中间未完成的状态。线性一致性需要满足的是,一旦新写入的数据被读取,后续所有的读取操作都应该能够读取到这条数据。也就是说,一旦读操作读取了一个值,所有后续的读操作都将读取这个值或至少是“最近”的值。上述定义来自较早的一篇论文,我将其中的重点提炼一下,如下。需要一个全局时钟来实现所谓的“最近”。因为没有全局一致的时间,所以两个独立的进程没有相同的“最近”概念。任何读取都可以读取这个“最近”值。下面我用一个例子来说明线性一致性。现在有三个节点,其中一个共享变量x进行写操作,第三个节点会读取后面的值。第一次读取可以返回1、2或NULL(初始值,两次写入前的状态),因为两次写入仍在进行中;第一次读可以在两次写之前,第一次在第一次写和第二次写之间,在第二次写之后。由于第一个写操作已经完成,但是第二个写操作还没有完成,所以第二个读操作只能返回1和2。第三个读操作只能返回2,因为第二个写操作是在第一个写操作之后完成的。下图是现象一致性的直观展示。图3线性一致性线性一致性代价高昂,连CPU都不用。有过并发编程经验的朋友一定知道CAS操作。这种运算可以实现运算的线性化,是实现高性能并发编程的关键。就是通过编程的方式来模拟线性一致性。一个常见的误解是线性一致性可以通过共识算法来实现,例如Paxos和Raft。但实际上是行不通的。以Raft为例,该算法只保证了复制Log的线性一致性,并没有描述Log是如何写入到最终的状态机中的,这意味着状态机本身并不是线性一致的。这里推荐大家阅读TiKV关于线性一致性的实现细节。由于线性一致性不划算,这里就不细说了。接下来说一下顺序一致性和因果一致性。顺序一致性由于线性一致性成本高,所以人们认为既然全局时钟使得严格一致性难以实现,那么顺序一致性就是放弃全局时钟的约束,改为分布式逻辑时钟。顺序一致性意味着所有进程都以相同的顺序看到所有修改。读操作可能无法及时获取到其他进程对同一数据的写更新,但是各个进程读取的数据不同值的顺序是一致的。下图是P1和P2写入两个值后读取P3和P4的情况。在实时情况下,1应该写在2之前,但是在顺序一致性下,1可以写在2之后。同时,即使P3已经读到了1的值,P4仍然可以读到2。但是需要注意的是这两个组合:1->2和2->1,P3和P4选择其中一个,保持不变。下图显示了他们阅读顺序的一种可能:2->1。图4顺序一致性我们通过下图进一步区分线性一致性和顺序一致性。图5区分线性一致性和顺序一致性其中,图a满足顺序一致性,但不满足线性一致性。原因是,从全局时钟的角度来看,P2进程对变量x的读操作是在P1进程对变量x的写操作之后,但是读出的是旧数据。但是这个图满足顺序一致性,因为P1和P2这两个进程的一致性是不冲突的。图b满足线性一致性,因为每次读操作读取的是变量最新的写结果,两个进程看到的操作顺序和全局时钟的顺序是一样的。图c不是顺序一致的,因为从进程P1的角度来看,它对变量y的读取操作返回0。也就是说,P1进程对变量y的读操作先于P2进程对变量y的写操作,x变量也是如此。因此,这个顺序不满足顺序一致性。在实践中,可以使用上面提到的一致性算法来实现顺序一致性。这些算法可以保证在每个节点上以相同的顺序执行操作,因此可以保证顺序的一致性。GoogleMegastore等系统使用Paxos算法来实现顺序一致性。也就是说,在Megastore内部,如果有数据更新,所有节点都会同步更新,每个节点上的操作执行顺序是一致的。与顺序一致性相比,因果一致性对因果一致性的要求较低:只要求有因果关系的操作顺序一致,没有因果关系的操作顺序是随机的。因果关系的要求如下。局部顺序:在这个过程中,事件执行的顺序就是局部因果顺序。错位顺序:如果读操作返回写操作的值,那么写操作必须顺序在读操作之前。闭包传递:在时钟向量中定义,如果a->b,b->c,那么必然有a->c。那么为什么需要因果关系,没有因果关系的写作如何传播?下图中,进程P1和P2进行的写操作没有因果关系,即最终一致性。这些操作的结果可能会在不同时间乱序传播给读者。进程P3将在看到2之前看到值1,而P4将看到2然后是1。图6因果一致性和下图显示进程P1和P2进行因果相关的写操作,并按其逻辑顺序将它们传播到P3和P4.因果写除了写数据外,还需要加一个逻辑时钟,通过这个时钟来保证两次写是因果相关的。这样就避免了我们遇到上图的情况。您可以在两个图中比较P3和P4的历史记录。图7逻辑时钟实现该逻辑时钟的一种主要方式是矢量时钟。矢量时钟算法使用矢量数据结构将每个全局进程的逻辑时间戳广播给所有进程。当每个进程发送一个事件时,它会将当前进程已知的所有进程时间写入一个向量,然后传播。因果一致性的一个典型案例是COPS系统,它是一个基于因果+一致性模型的KV数据库。它定义依赖关系并进行操作以实现因果一致性。这对于业务实现分布式数据因果关系非常有帮助。此外,基于AmazonDynamo中的向量时钟,也实现了因果一致性。事务隔离级别和一致性模型既然我们已经讨论了一致性模型,它与数据库世界中的事务有何不同?先说结论吧:有关系没关系。你怎么理解的?让我首先证明它们之间的无关紧要。ACID和CAP中的“C”都是一致的,但是它们的内涵却完全不同。其中ADI是数据库提供的能力保证,而C(一致性)则不是。它是业务级别的逻辑约束。举个最经典的转账例子,A有100元,B有0元,现在A要转30元给B,那么转账前后A有70,B有30,总和还是100.显然,这只是业务层强加的逻辑约束。对于CAP,C上面已经说的很清楚了,就是线性一致性。它代表了副本读取数据的即时性,即保证“何时”可以读取到“正确”的数据。越实时,意味着整个系统读取的数据是一致的。那么它们是如何连接的呢?其实事务的隔离与一致性模型有关。如果你把上面线性一致的例子看成多个并行事务,你会发现它们并不是孤立的。因为这个数据会在开始和结束之间的任何一点被读取,原因是一致性模型关注的是单个操作,而事务是由一组操作组成的。现在我们再看一个例子,这个例子说明了事务不一致导致的问题。图8事务和一致性其中三个事务满足隔离。可以看出T2读取了T1中输入的值。但是这个系统缺乏一致性保证,导致T3读取的值比T2读取的值早,这会导致应用程序出现潜在的bug。现在给出一个结论:事务隔离是描述并行事务之间的行为,一致性是描述非并行事务之间的行为。事实上,广义的事务隔离应该是经典隔离理论和一致性模型的混合体。比如我们会在一些文献中看到“one-copyserializability”、“strongsnapshotisolation”等等。前者其实是可串行化隔离级别加顺序一致性,后者是快照隔离级别加线性一致性。因此,对于分布式数据库,并没有丢弃原有的隔离级别,而是在引入一致性模型后,扩展了数据库隔离级别的内涵。小结今天的内容比较长,但是提炼了很多。我们从高可用入手,介绍了CAP理论对分布式模型评估的影响;然后我们重点介绍了一致性模型,这是核心,帮助大家评估分布式数据库的特性。最后介绍了事务隔离级别和一致性模型的区别和联系,帮助大家理解分布式数据库中事务隔离级别的概念。原文地址:https://www.toutiao.com/article/7174371025906549260/