数据库事务:ACID特性

文章访问量:

对事务和ACID的具体解释

之前在数据库事务的ACID特性一文中我粗略的记录了“什么是事务”,以及事务要满足的四种特性。本文将更详细地讲解ACID这四种特性。

事务(Transaction)

程序执行单元(包括SQL)

我之前一直以为事务只能由编程语言(Java,GO)这些发起。后面发现SQL也能发起事务。我们来一个例子。

对于表departments(表来自employees sample database,可自行下载)

运行以下SQL语句。如果只看两条UPDATE的话那就是更新表中的数据。但是在这里我们引入了事务,如果你自己跑这段查询的话会发现表中的数据并没有发生改变。接下来我们逐行解释。

BEGIN;
SAVEPOINT point_a;
UPDATE departments
SET dept_name="test" WHERE dept_name="Sales";
ROLLBACK TO point_a;
UPDATE departments
SET dept_name="test_2" WHERE dept_name="Research";
ROLLBACK TO point_a;
COMMIT;
  • BEGIN:写成”START TRANSACTION“也可以。两者都表示“开始事务”。
  • SAVEPOINT/ROLLBACK TO:玩游戏比较多的肯定熟悉这个词,表示“存档点”。和ROLLBACK TO配套使用,表示“回退到该存档点,并撤销存档点之后的事务执行“。
  • COMMIT:提交事务至数据库,所有操作都会生效且无法再回退。提交之后SAVEPOINT就失效了,自然也不能再ROLLBACK TO了。

当然,上面这一段SQL是我们手动回退到某个存档点,如果要回退全部事务的话直接ROLLBACK即可。

ACID特性

原子性,一致性,隔离性,持久性

这四个特性背起来很简单,ACID(酸)嘛,A原子C一致I隔离D持久,都是取各自英文单词的首字母。但是这四个特性要理解起来我个人觉得还是要花点时间的。

接下来我们讲讲为什么事务需要有ACID这四个特性。先给出一个例子:A有1000元,B有900元;现在A要转账50元给B。那么该事务可以被定义为:

read(A);
A:=A-50;
write(A);
read(B);
B:=B+50;
write(B)

其中read表示读取xxx的数据,并将该数据传给一个变量xxx。write则表示把变量xxx的值传回到数据库中。

原子性(Atomicity)

要么全都做,要么全不做

如果事务不具有原子性,那么假设在进行到write(A)时发生了故障,导致read(B)之后的SQL无法执行。那么A的账户少了50元,B却收不到这50元。那这50元就会凭空消失,很明显我们不希望这种情况会发生。

由于故障,导致系统的状态不再反映数据库本应描述的现实世界的真实状态,叫做不一致状态。如上,A+B的总金额应该为1900。但是如果由于故障而导致A+B的总金额变为了1850,此时系统就处于不一致状态。

当然,不一致状态在某个时刻是一定会发生的。因为在write(A)和write(B)之间存在一个时间差,所以重要的是系统最终能恢复到一致性状态。

所以事务的所有操作需要有原子性。要么全不做,要么全做。

一致性(Consistency)

A+B的值不变

在上面这个系统中,只存在A和B两个对象。而我们要保证的是事务执行后A+B的总和不变,金额的凭空增加或者减少都会导致不一致状态。

事务在执行后不能改变数据库系统的一致性状态,虽然在执行过程中会导致数据库出现不一致,但是最终数据库系统仍然会恢复到一致性状态。所以事务需要具有一致性

隔离性(Isolation)

并发事务仍具有原子性和持久性

上面都是针对单个事务而言的。然而在实际生活中,事务常常以并发的形式出现。

比如查询考试成绩,多名学生可能在同一时间向数据库提交了查询成绩的请求,如果这些事务全部串行执行,那效率将会低到难以接受的地步。

而如果并行执行,则需要保证并行执行中的每个事务都能确保原子性和一致性。假设在上面的例子中,此时系统中多出了一个对象C且持有50元,C需要向B转账50。那么该事务可以被定义为

read(C);
C:=C-50;
write(C);
read(B);
B:=B+50;
write(B)

假设两个事务并发执行。当A事务执行到write(B)后,还未将事务提交至数据库时,由于系统故障导致A事务即将被撤销。

那么对于C事务的read(B)而言,将会出现两个可能读到值–A事务撤销前将读到B=950;撤销后将读到B=900。那么对于前者而言,C事务此时读到的B是个无效值,在该无效值上进行的所有操作都会导致系统进入不一致状态。

前者被称为脏读,很明显我们也不希望该情况出现。所以当并发事务执行时,每个事务都需要具有隔离性,即一个事务的故障不会导致其他事务使数据库系统出现不一致状态。

持久性(Durability)

事务提交后,该事务对数据库的所有更新都是持久的

简单来说,一个事务在COMMIT之后,它对数据库的更改就应该是持久的。即使此时数据库故障也不会导致该事务对数据库的更改撤销。

为啥要单独提到这一点?主要是因为–数据库的更新还关系到“写入磁盘”。如果事务执行完后数据库系统断电怎么办?这就需要我们确保事务执行的更新在事务结束前就写入磁盘,而不是保存在主存中。这样即使数据库系统断电重启也能重建这些更新。

具体的就和数据库恢复系统有关了,由于文章篇幅限制,相关知识会后续开另一个文章记录。ACID的介绍就到这里结束了。不得不说和ACID相关的知识点还有很多,要全部理清还是有点难度的。

Subscribe
提醒
0 评论
Inline Feedbacks
View all comments
0
在此留下你的评论x