1. 数据库事务概述
1.1 存储引擎支持情况

1.2 基本概念
1.3 事务的ACID特性
- 原子性(atomicity)
- 一致性(consistency)
- 隔离型(isolation)
UPDATE accounts SET money = money - 50 WHERE NAME = 'A';
UPDATE accounts SET money = money + 50 WHERE NAME = 'B';
- 持久性(durability)
1.4 事务的状态
- 活动的(active)
- 部分提交的(partially committed)
- 失败的(failed)
- 中止的(aborted)
UPDATE accounts SET money = money - 50 WHERE NAME = 'A';
UPDATE accounts SET money = money + 50 WHERE NAME = 'B';
- 提交的(committed)
- 总结

2. 如何使用事务
使用事务有两种方式,分别为 显式事务 和 隐式事务 。
2.1 显式事务
步骤2:一系列事务中的操作(主要是DML,不含DDL)
# 显式开启事务。
begin;
# 执行MDL语句。
update `user` set name = 'dzw' where id = 2;
# 提交事务。当提交事务后,对数据库的修改是永久性的。
commit;
# 回滚事务。即撤销正在进行的所有没有提交的修改
rollback;
# 将事务回滚到某个保存点。
rollback to [SAVEPOINT];
2.2 隐式事务
SHOW VARIABLES LIKE 'autocommit';
- 显式的的使用 START TRANSACTION 或者 BEGIN 语句开启一个事务。这样在本次事务提交或者回滚前会暂时关闭掉自动提交的功能。
- 把系统变量 autocommit 的值设置为 OFF ,就像这样:
SET autocommit = OFF;
#或
SET autocommit = 0;
# 相反的是ON 1
2.3 隐式提交数据的情况
- 数据定义语言(Data definition language,缩写为:DDL)
- 隐式使用或修改mysql数据库中的表
- 事务控制或关于锁定的语句
- 加载数据的语句
- 关于MySQL复制的一些语句
- 其它的一些语句
2.4 使用举例:提交与回滚
# 开启事务举例
CREATE TABLE t_user(name varchar(20), PRIMARY KEY (name)) ENGINE=InnoDB;
BEGIN;
INSERT INTO t_user SELECT '张三';
COMMIT;
BEGIN;
INSERT INTO t_user SELECT '李四';
INSERT INTO t_user SELECT '李四';
ROLLBACK;
SELECT * FROM t_user;
# 最后只查到张三一条数据
# 默认开启隐式提交事务
CREATE TABLE t_user1 (name varchar(20), PRIMARY KEY (name)) ENGINE=InnoDB;
BEGIN;
INSERT INTO t_user1 SELECT '张三';
COMMIT;
INSERT INTO t_user1 SELECT '李四';
INSERT INTO t_user1 SELECT '李四';
ROLLBACK;
SELECT * FROM t_user1;
#查到张三、李四2条数据。
# 一个事务里操作
CREATE TABLE t_user2(name varchar(255), PRIMARY KEY (name)) ENGINE=InnoDB;
# 自动开启一个相同隔离级别的事务
SET @@completion_type = 1;
BEGIN;
INSERT INTO t_user2 SELECT '张三';
COMMIT;
INSERT INTO t_user2 SELECT '李四';
INSERT INTO t_user2 SELECT '李四';
ROLLBACK;
SELECT * FROM t_user2;
# 只查到张三一条数据。
# SAVEPOINT
# 最初id为1的记录,age为20。
begin;
update user set age = age + 10 where id = 1;
update user set age = age + 10 where id = 1;
savepoint s1;
update user set age = age + 10 where id = 1;
rollback to s1;
commit;
select * from user where id = 1;
# 查到 age=40;
# 注意,rollback to s1;只是在事务内回滚到某个保存点,对其他事务不可见,需要commit。
3. 事务隔离级别
MySQL是一个 客户端/服务器 架构的软件,对于同一个服务器来说,可以有若干个客户端与之连接,每个客户端与服务器连接上之后,就可以称为一个会话( Session )。每个客户端都可以在自己的会话中向服务器发出请求语句,一个请求语句可能是某个事务的一部分,也就是对于服务器来说可能同时处理多个事务。
事务有 隔离性 的特性,理论上在某个事务 对某个数据进行访问 时,其他事务应该进行 排队 ,当该事务提交之后,其他事务才可以继续访问这个数据。但是这样对 性能影响太大 ,我们既想保持事务的隔离性,又想让服务器在处理访问同一数据的多个事务时 性能尽量高些 ,那就看二者如何权衡取舍了。
3.1 数据准备
CREATE TABLE student (
studentno INT,
name VARCHAR(20),
class varchar(20),
PRIMARY KEY (studentno)
) Engine=InnoDB CHARSET=utf8;
INSERT INTO student VALUES(1, '小谷', '1班');
3.2 数据并发问题
针对事务的隔离性和并发性,我们怎么做取舍呢?先看一下访问相同数据的事务在 不保证串行执行 (也就是执行完一个再执行另一个)的情况下可能会出现哪些问题:
1.脏写( Dirty Write )
对于两个事务 Session A、Session B,如果事务Session A 修改了 另一个 未提交 事务Session B 修改过 的数据,那就意味着发生了 脏写。
2.脏读( Dirty Read )
3.3 SQL中的四种隔离级别
上面介绍了几种并发事务执行过程中可能遇到的一些问题,这些问题有轻重缓急之分,我们给这些问题按照严重性来排一下序:
脏写 > 脏读 > 不可重复读 > 幻读
- READ UNCOMMITTED :读未提交,在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。不能避免脏读、不可重复读、幻读。
- READ COMMITTED :读已提交,它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。可以避免脏读,但不可重复读、幻读问题仍然存在。
- REPEATABLE READ :可重复读,事务A在读到一条数据之后,此时事务B对该数据进行了修改并提交,那么事务A再读该数据,读到的还是原来的内容。可以避免脏读、不可重复读,但幻读问题仍然存在。这是MySQL的默认隔离级别。
- SERIALIZABLE :可串行化,确保事务可以从一个表中读取相同的行。在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作。所有的并发问题都可以避免,但性能十分低下。能避免脏读、不可重复读和幻读。

3.4 MySQL支持的四种隔离级别
MySQL的默认隔离级别为REPEATABLE READ,我们可以手动修改一下事务的隔离级别。
查看隔离级别:
# 查看隔离级别,MySQL 5.7.20的版本之前:
SHOW VARIABLES LIKE 'tx_isolation';
# 查看隔离级别,MySQL 5.7.20的版本及之后:
SHOW VARIABLES LIKE 'transaction_isolation';
# 或者不同MySQL版本中都可以使用的:
SELECT @@transaction_isolation;
3.5 如何设置事务的隔离级别
通过下面的语句修改事务的隔离级别:
SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL 隔离级别;
# 其中,隔离级别格式:> READ UNCOMMITTED> READ COMMITTED> REPEATABLE READ> SERIALIZABLE
SET [GLOBAL|SESSION] TRANSACTION_ISOLATION = '隔离级别'
# 其中,隔离级别格式:> READ - UNCOMMITTED> READ - COMMITTED> REPEATABLE - READ> SERIALIZABLE
关于设置时使用GLOBAL或SESSION的影响:
- 使用 GLOBAL 关键字(在全局范围影响):
SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;
#或
SET GLOBAL TRANSACTION_ISOLATION = 'SERIALIZABLE';
- 使用 SESSION 关键字(在会话范围影响):
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
#或
SET SESSION TRANSACTION_ISOLATION = 'SERIALIZABLE';
小结:数据库规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性越弱。
3.6 不同隔离级别举例
数据准备:
# 新建account表,并插入2条记录
CREATE TABLE account (
id INT,
name VARCHAR(20),
balance INT,
PRIMARY KEY (id)
) Engine=InnoDB CHARSET=utf8;
INSERT INTO account VALUES(1, '张三', 100);
INSERT INTO account VALUES(2, '李四', 0);
- 演示1. 读未提交之脏读
只有事务1修改数据,事务2查询
- 演示2:读已提交之不可重复读
- 演示3:可重复读之避免不可重复读
- 演示4:幻读
4. 事务的常见分类
从事务理论的角度来看,可以把事务分为以下几种类型:
- 扁平事务(Flat Transactions)
- 带有保存点的扁平事务(Flat Transactions with Savepoints)
- 链事务(Chained Transactions)
- 嵌套事务(Nested Transactions)
- 分布式事务(Distributed Transactions)