ACID 、CAP、BASE

955 阅读12分钟

[TOC]

ACID

事务是指一组操作,满足业务场景和并发控制的基本单位。目的是为了简化业务编程。

目的:ACID是传统单机关系型数据库的事务模型

A:Atomic,原子性

解释:原子性是一组操作要么全都执行,要么都不执行。

保障:事务中间发生异常时,需要对之前的操作进行回滚(回滚日志等实现方式)

C:Consistency,一致性

Consistency ensures that a transaction can only bring the database from one valid state to another, maintaining database invariants: any data written to the database must be valid according to all defined rules, including constraints,cascades,triggers, and any combination thereof.

解释:事物的执行是从一种有效状态转换为另一种状态,不能存在中间状态。这种状态由业务或者开发人员去决定,是一种对数据库的约束

只要事务执行前后满足约束,不管数据是不是合理(比如要求金钱总数不变,并没有要求金钱必须大于0),就满足一致性

一致性是由数据库本身和开发人员控制,是应用层面的,AID则是数据库保证的,AID最终满足应用程序的一致性

I:Isolation,隔离性

解释:为了保证并发事务处理时互不干扰(因为多个事务的操作数据可能存在交叉)

并发事务出现的问题

  1. 脏读(读未提交)

事务B读到了A修改但并未提交的数据

  1. 不可重复度(读已提交)

事务B有两次读取操作,第二次读取到了A提交后的数据,导致两次读操作数据不一致

解决方案:加锁、MVCC

  1. 幻读

事务B有两次读取操作,第二次读取到了A提交后的新增数据,导致两次读操作数据量不一致

解决方案:事务串行

事务隔离级别

根据上面的问题,数据库事务的隔离级别有以下四种(级别从低到高):

  1. 读未提交,不做处理,并发事务发生时会出问题;
  2. 读已提交,对数据的修改只有在提交后才能被其他事务读取,为了解决脏读问题;
  3. 可重复度,事务内多次读取的数据是相同的,其他事务的修改并不影响当前事务的数据,解决脏读和不可重复读问题,innodb的默认隔离级别;
  4. 串行化,多个事务之间之间只能串行执行,牺牲了系统的并发性,会影响性能。

事务隔离的实现

事务隔离其实就是为了实现并发控制

  1. 锁,锁时解决并发最常用的手段,如行锁、表锁等,同时读锁和写锁又用来区分读写操作,保证性能的同时满足隔离性;
  2. 时间戳,乐观锁,事务修改数据后给数据盖一个时间戳,其他事务修改时需要重新执行事务操作;
  3. MVCC,多版本并发控制,乐观锁,读取数据的时候只能读取最新版本的数据

D:Durability,持久性

解释:事务结束后对数据的修改要能够持久存储,不能丢失数据

保障:落地到磁盘,mysql事务是先对内存中的数据进行修改,并添加事务日志缓存,事务完成后将两个缓存数据持久化到磁盘

CAP

目的:为了解决分布式环境下各个节点状态的同步,是实现分布式系统的基本定理。

C:Consistency,一致性

分布式系统的数据是否在同一时刻都是一样的。也就是数据修改后,从所有的节点读到的数据都是最新的

分布式系统下,为了保证服务的可用性(数据拷贝)和高性能(读写分离),数据的可靠性(宕机后数据不会丢失),一份数据会在多个节点存在,所以数据修改时就需要考虑一致性的问题。

分布式数据一致性级别

  1. 强一致性:任何时候在任何节点读到的数据都是最新的,要求数据的修改能实时同步到各个节点;
  2. 弱一致性:数据更新后,会在一定的时间后达到数据一致,但是时间是不可控的;
  3. 最终一致性:是弱一致性的特例,也是目前大部分分布式系统采用的一个模型。

同时最终一致性又可以分为:

  • 因果一致性。如果进程A通知进程B它已更新了一个数据项,那么进程B的后续访问将返回更新后的值,且一次写入将保证取代前一次写入。与进程A无因果关系的进程C的访问遵守一般的最终一致性规则。
  • 读己之所写(read-your-writes)一致性。当进程A自己更新一个数据项之后,它总是访问到更新过的值,绝不会看到旧值。这是因果一致性模型的一个特例。
  • 会话(Session)一致性。这是上一个模型的实用版本,从线程上升到会话级别,它把访问存储系统的进程放到会话的上下文中。只要会话还存在,系统就保证“读己之所写”一致性。如果由于某些失败情形令会话终止,就要建立新的会话,而且系统的保证不会延续到新的会话。
  • 单调(Monotonic)读一致性。如果进程已经看到过数据对象的某个值,那么任何后续访问都不会返回在那个值之前的值。
  • 单调写一致性。系统保证来自同一个进程的写操作顺序执行。要是系统不能保证这种程度的一致性,就非常难以编程了。

上述最终一致性的不同方式可以进行组合

A:Availability,可用性

是指整个系统能够在一定时间内响应客户的请求。

P:Partition tolerance,分区容错

分布式系统下,各个机器是运行在多个子网络中,每个子网络称为区,网络分区就是子网络之间无法通信。

分区容错是指集群中某些节点无法通信的时候,其他节点仍能工作

因为分布式系统的网络分区是无法避免的(机器部署在不同的机房、不同的机架、不同的局域网下,之间的网络通信出现延迟或者丢包是正常的),所以只能实现CP和AP,一般业务都是保证可用性,舍弃一致性,但是都能达到最终一致性

BASE

BASE是指基本可用(Basically Available)、软状态( Soft State)、最终一致性( Eventual Consistency),是基于CAP理论的演化,其核心思想就是即使无法做到强一致性,但每个应用可以根据自身业务特点,通过合适的方式达到最终一致性

基本可用(Basically Available)

分布式系统出现故障时,损失部分可用性,来保障核心功能,如服务降级、增加响应时间

软状态( Soft State)

允许系统存在中间状态,保障服务的整体的可用性,如节点间副本同步的延迟就是中间状态

最终一致性( Eventual Consistency)

一段时间后,所有数据副本达到一致状态。

ACID注重数据的一致性,适用于单机传统数据库,BASE注重系统可用性,适用于大部分的分布式系统

分布式一致性

大部分业务场景都是选择满足AP,所以我们需要一些算法来保证最终一致性

2PC(tow phase commit),两阶段提交协议

2pc是协调所有分布式原子事务参与者,并决定提交或取消(回滚)的分布式算法

系统包含两类节点,一类是协调者协调者(coordinator)只有一个,一类是参与者(participants),一般是数据副本的个数。

  1. 阶段1,请求阶段

协调者向所有参与者发起提议,询问是否可以执行,同时添加本地日志用作事务回滚(watchlog);参与者执行本机的原子事务,但不会提交,同时会向协调者反馈执行结果(成功或者执行异常),进行状态记录(logging)

  1. 阶段2,提交阶段

协调者根据参与者反馈,如果都同意则发送提交命令,否则发送终止命令

缺点:

  1. 同步阻塞。执行过程之中,所有参与节点都是事务阻塞的,独占公共资源;
  2. 单节点故障。如果协调者发生故障,则所有参与者会一直处于锁定资源的状态,无法继续完成事务操作;
  3. 数据不一致。二阶段中,如果commit请求发生故障,会导致部分机器无法执行事务,产生数据不一致

3PC(three phase commit),三阶段提交协议

  1. 阶段1,CanCommit

协调者发送canCommit请求,等待参与者响应;

这时如果参与者宕机或者未收到协调者未收到响应,则终止事务;宕机的参与者恢复后,读取logging,发现未发出响应,也会自行终止事务,释放资源。

本阶段是协调者确认每个参与者的状态

  1. 阶段2,PreCommit

如果参与者都响应yes,则发送预提交(preCommit)请求,参与者收到预提交请求后会执行事务,并将undo或者redo记录到事务日志中,发送ack进行反馈。

如果参与者有一个响应了no请求或者等待超时,则终止事务,发送abort请求,参与者收到abort请求或者等待超时会终止事务。

本阶段是协调者将最终商议结果同步给参与者

  1. 阶段3,doCommit

协调者收到所有的ack请求后,发送doCommit请求,参与者接收到请求后则正式提交事务,事务提交完成后释放资源,并发送hasCommited响应。协调者收到响应ack后,标识事务完成

协调者没收到ack或者接受超时,则终止事务

本阶段是协调者通知准备好的参与者执行事务

3PC 对协调者和参与者都设置了超时机制(2PC只有协调者有超时机制)

优点: 减少了阻塞范围,超时后协调者或者参与者都会终止事务,释放资源

缺点: 参与者收到preCommit请求后,会等待最终指令。如果协调者发出doCommit请求,但是参与者没收到,会导致部分参与者继续提交事务,造成数据不一致

TCC(Try-Confirm-Cancel)事务补偿

事务补偿的想法类似于回滚日志,让所有参与方都执行,谁成功就提交本地事务。但是每个参与方的每个操作,都要注册(注意是注册,不是自动生成)一个对应的补偿操作,这个补偿操作由人为定义,用于撤销已执行事务带来的影响。

  1. Try 资源预留&锁定。事务发起方将调用服务提供方的Try方法来锁定业务所需要的所有资源;
  2. Confirm 确认执行业务逻辑操作。这里使用的资源一定都是在Try中预留的资源,Try + Confirm 组合起来是一次完整的业务逻辑;try阶段成功,默认confirm一定成功。
  3. Cancel阶段取消执行业务逻辑,主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源得到释放。

例子:A给B转账100元

Try阶段:尝试执行。

业务检查(一致性):A账户不能少于100元。 预留资源(准隔离性):冻结A账户100元,并保证其他事务的操作不会使A账户为负的;

Confirm阶段:确认执行。

真正执行事务:A账户减少100元,B 账户增加100元;

Cancel阶段:执行失败后,释放A账户冻结的100元

优点:实现和流程简单,但一致性比2PC差; 缺点:第2,3步失败后需要各种补偿,可能无法回滚

本地消息表(异步确保)

消息生产方:额外新建一个消息表,并记录消息发送的状态。消息表和业务数据要在一个事务中提交(就是说消息表和事务在同一个数据库中),然后消息经过MQ之后发送到消息的消费方。若消息发送失败,会进行重新发送。

消息消费方:处理该消息,并完成自己的业务逻辑。如果本地事务处理成功,表明已经处理成功,若处理失败,则重新执行。若是业务上的失败,可以给生产者发送一个业务补偿消息,通知生产方进行回滚等操作。

生产方和消费方定时扫描本地消息表,把还没处理完成的消息 or 失败的消息再发送一遍。

优点:避免了分布式事务,实现了最终一致性; 缺点:消息会和业务耦合

参考:

  1. 如何理解数据库事务中的一致性的概念?
  2. 浅谈事务与一致性问题
  3. CAP 定理的含义
  4. 分布式理论(CAP BASE TCC 2PC 3PC)