事物
事务执行是一个整体,所有的 SQL 语句都必须执行成功。如果其中有 1 条 SQL 语句出现异常,则所有的SQL 语句都要回滚,整个业务执行失败。
MySQL有两种方式进行事物操作
- 手动提交事物【Oracle默认】
- 自动提交事物【MySQL默认】
一、手动提交事物
- 开启事物:
start transaction; - 提交事物:
commit; - 回滚事物:
rollback;
1.手动提交事物的使用过程
- 成功执行的情况:开启事物--执行多条SQL语句--成功提交事物
start transaction;
update account set balance = balance - 500 where name = '张三';
update account set balance = balance + 500 where name = '李四';
commit;
- 执行失败的情况:开启事物--执行多条SQL语句--事物的回滚【清空日志,数据没有被改!】
start transaction;
update account set balance = balance - 500 where name = '张三';
update account set balance = balance + 500 where name = '李四';
rollback ;
总结: 如果事务中 SQL 语句没有问题,commit 提交事务,会对数据库数据的数据进行改变。 如果事务中 SQL语句有问题,rollback 回滚事务,会回退到开启事务时的状态。
二、自动提交事物的过程
MySQL默认每一条DML(增删改)语句都是一个单独的事物,每条语句都会自动开启一个事物,语句执行完毕自动提交事物,MySQL默认开始自动提交事物
1. 取消自动提交事物
- 查看MySQL是否开启自动提交事物
select @@autocommit;
@@var 表示全局变量,@@autocommit=1表示开启自动提交事务,0表示关闭
- 取消自动提交事务【只针对于当前连接--日志!】【需要手动commit;才能提交申请】
set @@autocommit = 0;
当前连接查询会经过当前连接日志处理,并不一定代表数据库中数据的真正改变!
三、事务原理
事务开启之后, 所有的操作都会临时保存到事务日志中, 事务日志只有在得到 commit 命令才会同步到数据表中,其他任何情况都会清空事务日志(rollback,断开连接)
1.事务原理图

2.事务的步骤
- 客户端连接数据库服务器,创建连接是创建此用户临时日志文件
- 开启事物以后,所有的操作都会先写入到临时日志文件中
- 所有的查询操作从表中查询,但会经过日志文件加工后才返回
- 如果事物提交则将日志文件中的数据写到表中,否则清空日志文件。
四、回滚点
在某些成功的操作完成之后,后续的操作有可能成功也有可能失败,但是不管成功还是失败,前面的操作都已经成功,可以在当前成功的位置设置一个回滚点。可以供后续失败操作返回到该位置,而不是返回所有操作,这个点称之为回滚点。
1. 回滚点操作语句
| 回滚点的操作语句 | 语句 |
|---|---|
| 设置回滚点 | savepoint 名字 |
| 回到回滚点 | rollback to 名字 |
- 操作流程【在@@autocommit=0的情况下操作,非自动提交事务的情况下】
# 开启事务
start transaction;
# 执行更新操作
...
# 设置回滚点
savepoint three_times;
# 继续操作
...
# 回退到回滚点
rollback to three_times;
# 提交事务
commit;
总结:设置回滚点可以让我们在失败的时候回到回滚点,而不是回到事务开启的时候。
五、事务的隔离级别
1.事务的四大特性
| 事务特性 | 含义 |
|---|---|
| 原子性(Atomicity ) | 每个事务都是一个整体,不可再拆分,事务中所有的 SQL 语句要么都执行成功,要么都失败。 |
| 一致性(Consistency ) | 事务在执行前数据库的状态与执行后数据库的状态保持一致。如:转账前2 个人的总金额是 2000 ,转账后 2 个人总金额也是 2000 |
| 隔离性(Isolation ) | 事务与事务之间不应该相互影响,执行时保持隔离的状态。 |
| 持久性(Durability ) | 一旦事务执行成功,对数据库的修改是持久的。就算关机,也是保存下来的。 |
2.事务的隔离级别
事务在操作时的理想状态: 所有的事务之间保持隔离,互不影响。因为并发操作,多个用户同时访问同一个数据。可能引发并发访问的问题:
| 并发访问的问题 | 含义 |
|---|---|
| 脏读 | 一个事务读取到了另一个事务中尚未提交的数据 |
| 不可重复读 | 事务update时引发的问题:一个事务中两次读取的数据内容不一致,要求的是一个事务中多次读取时数据是一致的 |
| 幻读 | insert或delete时引发的问题,一个事务中两次读取的数据的数量不一致,要求在一个事务多次读取的数据的数量是一致的 |
3.MySQL数据可以四种隔离级别
隔离级别越高,性能越差,安全性越高。
上面的级别最低,下面的级别最高。“是”表示会出现这种问题,“否”表示不会出现这种问题。
| 级别 | 名字 | 隔离级别 | 脏读 | 不可重复读 | 幻读 | 数据库默认隔离级别 |
|---|---|---|---|---|---|---|
| 1 | 读未提交 | read uncommitted | 是 | 是 | 是 | |
| 2 | 读已提交 | read committed | 否 | 是 | 是 | Oracle和SQL Server |
| 3 | 可重复读 | repeatable read | 否 | 否 | 是(MySQL InnoDB引擎已经解决此问题-间歇锁)) | MySQL【查询select @@tx_isolation】 |
| 4 | 串行化 | serializable | 否 | 否 | 否 |
4.MySQL事务隔离级别相关的命令
- 查询全局事务隔离级别
select @@tx_isolation
- 设置数据隔离级别,需要退出登录才能开到隔离级别的变化
set global transaction isolation level 级别字符串;
六、并发问题演示!
1.脏读演示
【级别最低时,commit前,数据已经写入数据库!】read uncommitted 脏读!
《后期更新...》
【事务开始时,commit前,先将未提交的数据放在日志中,commit后才将数据放入库中】结论:read committed 的方式可以避免脏读的发生
2.不可重读演示
《后期更新...》
【事务开始时,commit前,第一次查询的结果会保留在日志中,后续查询会使用这个值,直到commit后才会再次访问数据库】结论:同一个事务中为了保证多次查询数据一致,必须使用 repeatable read 隔离级别
3.幻读演示
呵呵,MySQL中无法看到幻读的效果!
《后期更新...》
【事务开始时,commit前,如果当前没有commit操作,访问本次涉及到的数据的其他线程或进程会被阻塞】结论:使用 serializable 隔离级别,一个事务没有执行完,其他事务的 SQL 执行不了,可以挡住幻读