1. 事务概念

事务处理(transaction processing)可以用来维护数据库的完整性,它保证成批的 MySQL 操作要么完全执行,要么完全不执行。

下面是关于事务处理需要知道的几个术语:

  • 事务(transaction )指一组 SQL 语句;
  • 回退(rollback)指撤销指定 SQL 语句的过程;
  • 提交(commit)指将未存储的 SQL 语句结果写入数据库表;
  • 保留点(savepoint )指事务处理中设置的临时占位符(placeholder),

你可以对它发布回退(与回退整个事务处理不同)。

mysql事务和存储过程 mysql存储过程事务控制_mysql事务和存储过程


事务仅仅适应于 INSERTUPDATEDELETE,对 SELECTCREATEDROP不生效。

2. 事务控制语句

SQL 定义了用于管理数据库事务的事务控制语句(Transaction Control Language)。MySQL 实现了以下语句:

  • BEGIN 或者 START TRANSACTION,开始一个事务;
  • COMMIT,提交事务;
  • ROLLBACK,撤销事务;
  • SAVEPOINT,事务保存点,用于撤销部分事务;
  • SET autocommit = {0 | 1},设置事务是否自动提交。

实际上,由于 MySQL 默认启用了自动提交(autocommit),任何数据操作都会自动提交:

show variables like 'autocommit';
Variable_name|Value|
-------------|-----|
autocommit   |ON   |

INSERT INTO bank_card VALUES ('62220803', 'C', 2000);

接下来我们看一下 ROLLBACK 命令的作用:

BEGIN;
INSERT INTO bank_card VALUES ('62220804', 'D', 1000);
ROLLBACK;

其中,BEGIN 开始一个新的事务;然后插入一条记录;最后使用 ROLLBACK 撤销该事务。因此,最终不会创建卡号为 “62220804” 的记录。

Oracle 支持事务管理的 COMMITROLLBACK 以及 SAVEPOINT 语句。Oracle 中不需要手动开始事务,一个事务的结束意味着另一个事务的开始。
SQL Server 支持事务管理的 BEGIN TRANSACTIONCOMMITROLLBACK 以及 SAVE TRANSACTION 语句。
PostgreSQL 支持事务管理的 BEGINCOMMITROLLBACK 以及 SAVEPOINT语句。

3. 事务的 ACID 属性

SQL 标准定义了数据库事务的四种特性:ACID

3.1 原子性

原子性(Atomic)是指一个事务包含的所有 SQL 语句要么全部成功,要么全部失败。

例如,某个事务需要更新 100 条记录;但是在更新到一半时系统出现故障,数据库必须保证能够回滚已经修改过的数据,就像没有执行过任何修改一样。

3.2 一致性

一致性(Consistency)意味着事务开始之前,数据库位于一致性的状态;事务完成之后,数据库仍然位于一致性的状态。

例如,银行转账事务中;如果一个账户扣款成功,但是另一个账户加钱失败,就会出现数据不一致(此时需要回滚已经执行的扣款操作)。另外,数据库还必须保证满足完整性约束,比如账户扣款之后不能出现余额为负数(在余额字段上添加检查约束)。

3.3 隔离性

隔离性(Isolation)与并发事务有关,一个事务的影响在提交之前对其他事务不可见,多个并发的事务之间相互隔离。

例如,账户 A 向账户 B 转账的过程中,账户 B 查询的余额应该是转账之前的数目;如果多人同时向账户 B 转账,结果也应该保持一致性,和依次进行转账的结果一样。SQL 标准定义了 4 种不同的事务隔离级别,我们将会在下文进行介绍。

3.3 持久性

持久性(Durability)表示已经提交的事务必须永久生效,即使发生断电、系统崩溃等故障,数据库都不会丢失数据。

数据库系统通常使用重做日志(REDO)或者预写式日志(WAL)实现事务的持久性。简单来说,它们都是在提交之前将数据的修改操作记录到日志文件中;当数据库出现崩溃时,可以利用这些日志重做之前的修改,从而避免数据的丢失。

对于我们开发者而言,重点需要注意的是隔离级别,而隔离级别又与并发访问有关。

4. 并发和隔离级别

数据库的并发意味着多个用户同时访问相同的数据,例如 A 和 C 同时给 B 转账。数据库的并发访问可能带来以下问题:

4.1 脏读(Dirty Read)

当一个事务允许读取另一个事务修改但未提交的数据时,就可能发生脏读。

例如,B 的初始余额为 0;A 向 B 转账 1000 元但没有提交;此时 B 能够看到 A 转过来的 1000 元,并且成功取款 1000 元;然后 A 取消了转账;银行损失了 1000 元。很显然,银行不会允许这种事情发生。

4.2 不可重复读(Nonrepeatable Read)

一个事务读取某一记录后,该数据被另一个事务修改提交,再次读取该记录时结果发生了改变。

例如,B 查询初始余额为 0;此时 A 向 B 转账 1000 元并且提交;B 再次查询发现余额变成了 1000 元,以为天上掉馅饼了。

4.3 幻读(Phantom Read)

一个事务第一次读取数据后,另一个事务增加或者删除了某些数据,再次读取时结果的数量发生了变化。幻读和非重复读有点类似,都是由于其他事务修改数据导致的结果变化。

4.4 更新丢失(Lost Update)

第一类:当两个事务更新相同的数据时,如果第一个事务被提交,然后第二个事务被撤销;那么第一个事务的更新也会被撤销。第二类:当两个事务同时读取某一记录,然后分别进行修改提交;就会造成先提交的事务的修改丢失。

5. 隔离级别

为了解决并发访问可能导致的各种问题,SQL 标准定义了 4 种不同的事务隔离级别(从低到高):

隔离级别

脏读

更新丢失

不可重复读

幻读

读未提交(Read Uncommitted)

可能

可能

可能

可能

读已提交(Read Committed)


可能

可能

可能

可重复读(Repeatable Read)




可能

序列化(Serializable)





  • 读未提交隔离级别最低,一个事务可以看到其他事务未提交的修改。该级别可能产生各种并发异常;如果一个事务已经修改某个数据,则另一个事务不允许同时修改该数据,写操作一定是按照顺序执行。PostgreSQL 消除了读未提交级别时的脏读。
  • 读已提交只能看到其他事务已经提交的数据,不会出现脏读。
  • 可重复读可能出现幻读。MySQL 中的 Innodb 存储引擎和 PostgreSQL 在可重复读级别消除了幻读。
  • 序列化提供最高级别的事务隔离。它要求事务只能一个接着一个地执行,不支持并发访问。

事务的隔离级别越高,越能保证数据的一致性;但同时会对并发带来更大的影响。不同数据库默认使用的隔离级别如下:

  • OracleSQL Server 以及 PostgreSQL 默认使用读已提交隔离级别;
  • MySQL InnoDB 存储引擎默认使用可重复读隔离级别。

一般情况下,大多数数据库的默认隔离级别为读已提交;此时,可以避免脏读,同时拥有不错的并发性能。尽管可能会导致不可重复度、幻读以及丢失更新,但是可以通过应用程序加锁进行处理。