事务ACID、隔离级别及CAP、BASE理论

相信不少读者容易搞混事务的ACID属性、事务隔离级别、CAP理论以及BASE理论都是什么,它们之间有什么关系,本文将对这些看起来有一定关联的概念做一个探讨。

事务的ACID属性

谈到数据库事务,ACID属性总是无法绕过,先了解下什么是ACID,再跟后面内容做一个对比,加深理解。
事务本质上是由一组关联SQL组成的执行任务单元,事务必须符合4个基本属性才能称之为一个有效的事务:

  • A: Atomic, 原子性,一组执行任务单元要么一起成功,要么一起失败撤销,不能存在部分成功的情况。
  • C: Consistency,一致性,事务执行结束后,逻辑上数据的完整性是不变的。
  • I : Isolation, 隔离性,事务之间彼此独立,不会相互影响,不允许事务A读取到事务B的中间状态的情况。
  • D: Durability,持久性,事务执行完成后,数据就能持久存储,即便系统崩溃,数据也能正常恢复。
    ACID的关注点主要集中在一次事务的基本能力诉求上,而怎么达到事务的这些属性要求,需要交由各个系统去考虑及实现。

事务的隔离级别

事务有4种隔离级别,从隔离程度的低到高分别是:

  • Read uncommitted:读未提交
    一个事务尚未提交,其他事务就可以读到它的中间状态数据。
    读未提交的实现方式是,在事务过程中不做任何控制,一遍执行事务一遍实时生效到数据库中。
    这个过程中如果其他事务在读写同一个数据,那么最终结果是会发生错误的,也就是说存在“脏读”的可能性。

  • Read committed:读已提交
    事务只有在提交以后,其他事务才能读到它的更新数据。
    读已提交的实现方式是,同样不做事务过程控制,只关注单个事务执行过程的中间数据不直接入库,而是在执行完毕后一次性把缓存副本全部刷到数据库中。
    上述实现避免中间态外泄的问题,避免了“脏读”,但无法避免一个事务在两次读取A数据的过程中数据被其他事务更新,导致两次读出来数据不一致的问题,也就是“不可重复读”。


    不可重复读
  • Repeatable read:可重复读
    对事务过程的并发进行控制,事务启动后会锁着相关的数据,禁止其他事务进行读写。
    这样又避免了“不可重复读”的问题,然而,如果事务过程包含count操作,在两次count之间,有一个插入数据的事务执行了,那么两次count的内容又会不一致,这就是“幻读”。

  • Serializable:序列化
    所有事务进入执行队列串行执行,把并发事务disable掉,除了性能问题,序列化方案能避免所有问题。

CAP理论

不同类型的分布式存储系统,对于数据可靠的要求程度是不同的,也就是说,不同人对于怎样的一个系统算是可靠的定义是不一样的。那么通过怎样的标准来衡量系统的可靠程度呢?
Eric Brewer提出了分布式系统的三个衡量系统可靠性的指标,来论证分布式系统的可靠能力边界,它们统称CAP理论:


CAP
  • Consitency: 一致性
    分布式系统各节点之间的数据,是否能保证严格一致,即每次的数据变更,要么同时在全部节点生效,要么全部不生效,不会出现只有部分节点生效的情况,即强一致性。
    举例:
    把分布式系统的数据A改成B,当系统返回成功的时刻起,任何查询该数据的请求都能正确得到B,期间不存在部分查询得到A,部分得到B的中间状态。
    再稍微扩展下,对于一致性的等级,可以进一步分成三种:
1、强一致性
指更新操作完成的时刻,对所有节点的读操作都返回更新后的内容(基于同一个数据副本)。
2、弱一致性
指更新以后,允许部分或全部读取的结果不能返回更新后的内容。
3、最终一致性
指更新完成一段时间以后,系统最终达成数据的一致性。
  • Availability: 可用性
    可用性指的是每一个向服务器发起的请求,都能在可容忍的一定时间内得到符合业务预期的响应结果。
  • Partition tolerance: 分区容忍性
    分区容忍性指的是系统存在多个节点的情况下,不会因为某部分节点故障导致服务完全不可用。
    存在分布式存储系统的原因,除了高可用高性能的需求外,最基础的就是数据有备份机制,不能存在由于某个节点的问题导致全部数据丢失。基于这个原因,数据存储系统分区是必须的。
    但是分区又会带入另一个问题,就是分区系统间的网络可能会抖动、某些服务器可能会崩溃,也就是说分区出现数据状态不一致等的情况几乎是必然发生的,分布式存储服务的设计必须容忍这种情况,并以某种方式让数据最终能够达成一致。如果不允许此情况发生,唯一的解决方案就是没有分区,也就是单节点运行,而这又是服务器设计的兵家大忌。

选择AP还是CP的现实问题

在理解了CAP理论的定义之后,我们知道了分区容忍性是必须满足的,那么让我们探讨下CAP能否同时保障。
假设我们有一个由2台服务器组成的分布式数据库系统,现在服务器之间的网络断开了,然后存储系统收到了一个更新操作,把数据A更新为B。

  • 如果我们必须保证强一致性(C)。

1)我们可以让一台服务器接管所有更新跟后续的查询请求,也就是说我们必须放弃另一台机器,单机运行,即放弃分区容忍性,保障系统的强一致性以及可用性(CA)。
2)我们也可以选择更新失败,等待两台服务器之间的网络连接恢复,然后再通知用户重新发起请求,放弃可用性(CP)。

  • 如果我们必须保证可用性(A)。

这意味着更新操作必须得到成功的响应结果。
3)我们可以选择两台服务器一起工作,每台完成自己的独立读写,实现分区容忍性,等网络恢复了再手工进行服务器之间的数据同步,放弃强一致性(AP)。
4)我们也可以选择只让一台服务器工作,回到方案1,放弃分区容忍性(AC)。

  • 如果我们必须保证分区容忍性(P)。

也就是说我们要让两台服务器能够同时处理请求。
我们可以选择回到假设2的方案1,放弃强一致性(PC)。
或者可以选择回到假设3的方案2,放弃可用性(PA)。

基于上述的假设,我们能够清楚地知道,CAP中的三者不可兼得,我们往往只能选择两个。

  • 1、保障CA,舍弃P:
    舍弃分区容忍性,意味着我们只能让一台服务器接管所有的请求,放弃高可用。那么就有一个单点故障的问题存在,一旦单机挂了,那么整个系统就不可用了。
    因此,舍弃分区容忍性基本是不可接受的。
    大多数的关系型数据库默认都是CA模型,并不考虑分区容忍性,有时候我们会简单地采用master-slave的方式来做数据冷备,这种方式实际上也是CA,因为master挂了服务就不可用了。

  • 2、保障AP,舍弃C:
    舍弃强一致性,意味着当故障发生,有些用户进行写操作以后,可能出现某些请求出现结果,某些请求又返回修改前的数据的情况,只要保障最终一致性就可以。
    在大多数对数据一致性不具有很强要求的应用场景,可以采用这种方式,比如各类缓存服务模块,目标是服务稳定,至于数据是否完全一致,并不那么重要。
    再接着第一点的master-slave方案中,有读者又会提出问题,如果master-slave是一主多从,主写从读呢?-- 这种就没法保证一致性了,实际上是AP模式。

  • 3、保障CP,舍弃A:
    放弃可用性,意味着当故障发生的时候,我们要让整个系统处于不可用的状态,直到故障恢复,系统才能继续响应用户的请求。
    这样的设计在应用系统中大多在跟钱打交道的应用中使用,当故障发生的时候,我们让写的请求操作不可用,只读不写,放弃写的可用性。
    在关系型数据库的分布式事务,或者像上文的master-slave的架构升级版,主写从读,二阶段提交,从挂了就舍弃,主挂了就重新选举呢?
    实际上这就是Zookeeper这种数据治理系统常用的解决方案,保障了CP,但当系统分裂程度达到分区的容忍范围外的情况下(一般是1/2及以上,避免脑裂发生),系统会不可用,从而放弃可用性。

BASE理论

BASE理论,即Basically Available, Soft state, Eventually consistency(基本可用,软(过渡)状态,并达到最终一致)。
BASE理论是在CAP理论的三种刚性要求基础上提出的折中方案,中心思想是根据业务场景,在可容忍范围内,尽可能保障用户体验及服务可用。

  • Basically Available:基本可用
    系统即便在发生部分故障的情况下,也能保持基本可用的状态。
  • Soft state:软(过渡)状态
    软状态指的是,允许存在中间存在数据不一致的情况,参考关于上文Consistency一节提到的三种一致性等级。
  • Eventually consistency:达到最终一致
    能通过自动、手动同步等方式,让数据达到最终一致性。

总结

最后,我们来把事务ACID/隔离级别,CAP及BASE理论串一下。

  • 一个生产可用的存储系统要求至少得是一个分布式系统,而通过CAP理论,我们得知分布式存储系统不是万能的,它具备能力边界。
  • CAP理论要求的三种指标都太刚性,我们可以使用BASE理论来设计一个存在中间过渡状态的实现方案。
  • 如果存储系统需要满足事务需求,需要达到ACID的4种属性要求。
  • 在具体实现事务的设计方案中,存在4种相互隔离的级别可供选择,业务需根据自己的使用场景进行选择。

你可能感兴趣的:(事务ACID、隔离级别及CAP、BASE理论)