说到关系型数据库的事务我们一定会想到ACID特性,在实际开发发现ACID特性的约束太大了。例如在分布式情景下,C 一致性(Consistent)往往要牺牲性能为代价,同时又有CAP的约束(CAP可参看谈谈CAP定理)。实际的业务可能对性能要求很高或者业务对数据不需要强一致性。这样,我们可以考虑系统的BASE特性。今天就细聊ACID、BASE及其对比。

ACID概念

事务是一个由一系列数据库操作构成的完成的、不可分割的逻辑过程。事务具有ACID特性。下面详细说明ACID的特点。

  • A 原子性(Atomic)
    原子性指事务是一个不可分割的整体,它对数据库的操作要么全做,要么全不做,即不允许事务部分地完成。如果因服务器故障导致事务未完成可以通过日志功能恢复事务执行前的状态。
  • C 一致性(Consistent)
    一致性指事务对数据库的作用使数据库从一个一致状态装换到另外一个一致状态。一致状态指数据库中的数据所必须满足的完整性约束完整性约束数据的正确性相容性。一个明显的例子:账户A转账到账户B的前后两个账户的金额总数不变(满足完整性约束)保持一致性由RMDBS负责,但完整性约束由用户给出约束条件。也就是说,用户给出数据完整性约束的约束条件,有RMDBS负责保证该条件的成立。
  • I 隔离性(Isolated)
    隔离性指在并发环境下,不同事务操纵相同的数据时,每个事务都有各自完整的数据空间。并发事务间对数据所做的修改必须是互相隔离。当一个事务要查看数据时,数据所处的状态不是另外一个事务修改前的状态就是另外一个事务修改后的状态,不可能是中间状态。隔离性使得每个事务在更新(commit)前对数据所做的修改在其他事务看来是不可见的。
  • D 持久性(Durable)
    持久性指事务一旦提交(commit)成功,它对数据所做的更新都是持久的。这样保证DBRMS运行故障时也能恢复数据。

相关概念

针对ACID概念的相关概念的解析

  • 完整性约束
    完整性约束数据的正确性相容性
    正确性指数据符合现实世界语义、反映当前实际状况。
    相容性指数据库同一对象在不同关系表中的数据是符合逻辑的。

  • 完整性约束的内容
    实体完整性是在CREATE TABLE中用PRIMARY KEY定义。包括两种情况。

1
2
3
4
CREATE TABLE Student (
Sno CHAR(9) PRIMARY KEY, /*列级定义*/
Sname CHAR(9) NOT NULL
);
1
2
3
4
5
CREATE TABLE Student (
Sno CHAR(9),
Sname CHAR(9) NOT NULL,
PRIMARY KEY (Sno) /*表级定义*/
);

参照完整性是在CREATE TABLE中用FOREIGN KEY短语中定义哪些列为外码,用REFERENCES短语指明这些外码参照哪些表的主码。

1
2
3
4
5
6
CREATE TABLE SC(
Sno CHAR(9) NOT NULL,
Cno CHAR(9) NOT NULL,
PRIMARY KEY (Sno, Cno),
FOREIGN KEY (Sno) REFERENCES Student(Sno) /*表级定义参照完整性*/
);


用户定义完整性是针对某一具体应用的数据必须满足的语义要求。
例如:
1.列值非空 NOT NULL
2.列值唯一 UNIQUE
3.列表检测是否满足一CHECK表达式

事务的隔离级别

  • 未提交读(READ UNCOMMITED)

一个事务修改的数据,但没有提交,其他事务也可以读到。这时就会出现脏读

  • 提交读(READ COMMITED)

提交读满足了事务的最基本定义:一个事务可以读到其他事务已经提交了的修改。但这个隔离级别会出现不可重复读的情况。例如:两个事务,事务A读取以数据data=1。这时事务B修改了data=2并提交。事务A重新读取data发现data=2,和第一次读取的数据不一样。事务A在运行期间读到data不唯一,出现了不可复现的情况。

  • 可重复读(REPEATABLE READ)

可重复读是MySQL的默认隔离级别。可重复读允许读取事务已经提交的数据,但一个事务两次读取一个数据期间,其他事务不能更新该数据。可以理解为读取某数据的事务已经对该数据加锁了,其他数据可以读,但不能修改(读写锁)。该隔离级别尽管避免不可重复读的出现(加锁),但会出现幻读。幻读指一个事务在读取某个范围内的数据时,另外一个事务在该范围内插入新数据,那么这个事务再次读取该范围的记录时就会读到多出来的行(幻行)。解决这个问题可以把整个表锁定。

  • 可串行化(SERIALIZABLE)

通过强制事务串行执行,避免幻读的问题。但会因为多个事务进行锁竞争而大大影响性能。

查询、修改MySQL的默认隔离级别

  • 查询
1
select @@global.tx_isolation

结果

1
2
3
4
5
6
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| REPEATABLE-READ |
+-----------------------+
1 row in set (0.00 sec)
  • 修改
1
set tx_isolation='read-committed'

再次查询

1
2
3
4
5
6
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| REPEATABLE-READ |
+-----------------------+
1 row in set (0.00 sec)

事务的实现

实现事务的四个难点:(1)原子性(2)隔离性(3)一致性(4)持久性
其次就是实现ACID特性。下面我们来一一讲解

原子性的实现

实现原子性可以通过打log的方式。所有数据的更改操作都写入日志,如果一个事务在完成一些列操作中的一部分后就出现故障而无法继续,可以通过回溯日志,将已经执行成功的操作撤销,从而达到撤销操作。具体过程如下:

因为系统崩溃使数据库处于不一致状态,执行如下操作

  1. 读取日志,把已经执行成功但没有写入磁盘的操作进行持久化(REDO)
  2. 对没有提交成功的事务进行撤销,保证原子性(UNDO)

一致性的实现

原子性并不能保证单个数据的一致性。例如:事务A对a进行加1,但在事务A提交前,事务B对a加2且提交。随后,事务A才提交,结果覆盖了事务B提交的结构,a的值只加了1。因此我们需要引入隔离性。

隔离性的实现

隔离性可以保证每个事务都有独立一致的数据空间,在事务期间它读到的数据总是处于一致的状态。事实上,隔离性的实现关键是并发控制。如果事务都是串行执行,那么久没有必要提供隔离性,因为任何一个事务在运行期间都不会有其他事务干扰。但,当多个事务进行非串行化执行时,需要控制它们的并发执行过程以保证数据的一致。参考操作系统的竞争条件

常见的并发控制方式是。锁无非就两种类型:

(1)悲观锁 事务期间对操作的对象加锁,直到事务结束而释放
(2)乐观锁 事务期间不对操作对象加锁而是记录它们的初始状态,但在事务提交前会检测操作对象的状态,如果和初始状态不等,就不提交回滚。(对比CAS)

然后在这基础上,分不同粒度的锁。

关于锁的粒度

封锁的对象可以是逻辑单元和物理单元。逻辑单元包括:行、表、数据库等;物理单元包括:页、物理记录等。

数据一致性模型和BASE特性

BASE, Basically Available, Soft-State, Eventual consistency.BASE特性是在分布式环境下数据库遵循的特性。对比与关系型数据库中的ACID特性。

解释

  1. Basically Available,基本可用性:系统能够基本运行,一直提供服务
  2. Soft-State,软状态:系统不要求一直保持强一致状态
  3. consistency,最终一致性:系统需要在某个时刻后达到一致性要求。对于很多应用来说,完全牺牲一致性是不可取的,只会导致数据混乱,即使系统的性能再高也没有实际用处。这里牺牲一致性,只是放弃了关系型数据库的强一致性要求。从用户的角度看,即便没有强一致性,只要在一定时间窗口内保持一致,用户就感觉数据是一致的。例如微信头像的更改。

BASE采用的是弱一致性或最终一致性。

三类一致性模型

  • 强一致性

无论更新操作在哪个副本完成,之后的所有读操作都能获得最新的数据。对于单副本来说,读写操作都在同一数据库上,可以轻易实现强一致性。对于多副本数据来说,需要使用分布式事务协议,包括Paxos算法、两阶段提交。

  • 弱一致性

在数据副本更新后,允许读操作在一段时间内读到的数据不是最新的。

  • 最终一致性

最终一致性是弱一致性的特例。系统能保证用户最终能够读到操作对系统特定数据的更新。DNS就是最终一致性的经典例子。DNS是一个分布式系统,在CAP理论中满足AP特性,在一致性上是最终一致性,当更新一个域名的IP以后,根据配置策略以及缓存控制的策略,最终所有客户端都能看到最新的值。

ACID vs BASE

用一张表格来对比ACID特性和BASE特性

ACID BASE
强一致性 弱一致性
隔离性 可用性优先
采用悲观、保守的方法 采用乐观方法
难以变化 适应变化、更简单、更快

分布式系统中,由于CAP的约束,最终会表现出BASE特性。

转载请包括本文地址:https://allenwind.github.io/blog/4315
更多文章请参考:https://allenwind.github.io/blog/archives/