对于大部分程序员来说,他们的任务就是把现实世界的业务场景映射到数据库世界中。
银行为了存储人们的账户信息而建立一个 account 表:
CREATE TABLE account (
id INT NOT NULL AUTO_INCREMENT COMMENT '自增id',
name VARCHAR(100) COMMENT '客户名称',
balance INT COMMENT '余额',
PRIMARY KEY (id)
) Engine=InnoDB CHARSET=utf8;
kelvin 和 bob 在银行各自开设了账户,俩人在现实世界的资产就会体现在数据库世界的 account 表中。
假设现在 kelvin 有15元,bob 只有5元。
+----+--------+---------+
| id | name | balance |
+----+--------+---------+
| 1 | kelvin | 15 |
| 2 | bob | 5 |
+----+--------+---------+
随着时间的流逝,kelvin 和 bob 可能陆续向银行账户中存钱、取钱或者向别人转账,他们账户中的余额也因此发生变动。
比如,有一次 bob 着急用钱,急忙打电话向 kelvin 借了10块钱。现实世界中的 kelvin 走向银行 ATM机,输入 bob 的账号以及10元转账金额,然后按下确认键之后就拔卡走人了。
对于数据库世界来说,这相当于执行了下面这两条语句:
UPDATE account SET balance = balance - 10 WHERE id = 1;
UPDATE account SET balance = balance + 10 WHERE id = 2;
但是这里有个问题:如果上述两条语句只执行了一条时,服务器忽然断电了,这该咋办?把 kelvin 的钱扣了,但是没给 bob 转过去,这问题就大了。
为了解决这个问题,数据库设计者引入了事务的概念。
我们把需要保证原子性、隔离性、一致性和持久性的一个或多个数据库操作称为事务。
在现实世界中,转账操作是一个不可分割的操作。也就是说,要么压根就没转,要么转账成功,不能存在中间状态,也就是转了一半的这种情况。我们把这种“要么全做,要么全不做”的规则称为原子性。
在现实世界中,两次转账状态应该是互不影响的。比如 kelvin 向 bob 同时转了两次5块钱(假设同时操作)。那么最后 bob 的账户里肯定多10块钱,kelvin 账户里肯定少了10块钱。
但是对应到数据库世界中,事情又变得复杂了一些。为了简化问题,我们粗略假设 kelvin 向 bob 转账5块钱的过程如下:
A 中:简写为 read(A)。A = A - 5。write(A)。B 中:简写为 read(B)。B = B + 5。write(B)。我们将 kelvin 向 bob 同时进行的两次转账操作分别称为 T1 和 T2。
在现实世界中 T1 和 T2 应该是没有关系的,可以先执行完 T1,再执行 T2;或者先执行完 T2,再执行 T1。如下图所示。

但是在真实的数据库中,T1 和 T2 的操作可能交替执行,如下图所示。

所以对于现实世界中状态转换对应的某些数据库操作来说,不仅要保证这些操作以原子性的方式执行完成,而且要保证其他的状态转换不会影响到本次状态转换,这个规则称为隔离性。
我们生活的现实世界中存在形形色色的约束,比如身份证号不能重复、性别只能是男或者女、人民币的最大面值只能是100(现在是2022年)、红绿灯只能有3种颜色等。只有符合这些约束的数据才是有效的。
数据库世界只是现实世界的一个映射,现实世界中存在的约束当然也要在数据库世界中有所体现。如果数据库中的数据全部符合现实世界中的约束,我们就说这些数据就是一致的,或者说符合一致性的。
如何保证数据库中数据的一致性呢(就是符合所有现实世界的约束)?主要靠下面两方面:
当现实世界中的一个状态转换后,这个转换的结果将永久保留,这个规则被叫做持久性。
当把现实世界的状态转换映射到数据库世界时,持久性意外着该次转换对应的数据库操作所修改的数据都应该在磁盘中保留下来,无论之后发生什么事故,本次转换造成的影响都不应该丢失。
我们现在知道,事务是一个抽象的概念,它其实对应着一个或多个数据库操作。我们根据这些操作所执行的不同阶段把事务大致划分成了下面几个状态。
事务的状态转换图,如下图所示:

从图中可以看出,只有当事务处于提交的或者中止的状态时,一个事务的生命周期才算是结束了。对于已经提交的事务来说,该事务对数据库所做的修改将永久生效;对于处于中止状态的事务来说,该事务对数据库所做的所有修改都会被回滚到没执行该事务之前的状态。
写在最后:在 MySQL 中,并不是所有的存储引擎都支持事务的功能,目前只有 InnoDB 和 NDB 存储引擎支持。
redo log 保证事务的持久性,使用 undo log 来保证事务的原子性。REPEATABLE-READ)。本文参考 《MySQL是怎样运行的》 小孩子4919 著