环境
DBMS:MySQL 8.0.17
工具:Navicat Premium 11.2.16
概述
事务是用户定义的一个数据库操作序列,这些操作要么全做,要么全不做,是一个不可分割的工作单位。
ACID特性
事务具有4个特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持续性(Durability)。
原子性
事务时数据库的逻辑工作单位,事务中的操作要么都做,要么都不做。
一致性
事务必须使数据库从一个一致性状态变到另一个一致性状态。
如果事务提交成功,就说数据库处于一致性状态;如果事务尚未完成就被迫中断,而某些数据操作已经写入数据库,这时数据库就处于不一致状态。
事务的一致性与原子性息息相关。
隔离性
一个事务的执行不能被其他事务干扰,即一个事务的内部操作及使用的数据对其他并发事务是隔离的,并发执行的各个事务之间不能互相干扰。
持续性
事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其执行结果有任何影响。
破坏因素
事务ACID特性可能遭到破坏的因素有:
- 多个事务并行运行时,不同事务的操作交叉执行。在这种情况下,DBMS必须保证多个事务的交叉运行不影响事务的原子性。
- 事务在运行过程中被强行停止。在这种情况下,DBMS必须保证被强行停止的事务对数据库和其他事务没有任何影响。
并发控制
当多个用户并发地存取数据库时就会产生多个事务同时存取同一数据的情况。若不对并发操作加以控制就可能会存取不正确的数据,破坏事务的一致性和数据库的一致性。
并发操作带来的数据不一致性有:
- 脏读:T1读取了T2更新但还没提交的数据之后,T2回滚,T1读取的数据是无效的。
- 不可重复读:T1读取一个数据,T2更新了该数据之后,T1再次读取该数据,发现数据发生了改变。
- 幻读:T1读取表时,T2对表中记录进行插入或删除操作,T1再次读取表时,发现记录数发生了改变。
导致数据不一致性的主要原因是并发操作破坏了事务的隔离性。并发控制机制就是要用正确的方式调度并发操作,使一个用户事务的执行不受其他事务的干扰,从而避免造成数据的不一致性。
事务操作
事务的操作有:开启事务、事务提交、事务回滚。
开启事务
START TRANSACTION;
事务提交
事务提交,即提交事务的所有操作,就是将事务中所有对数据库的数据操作写回到磁盘上的物理数据库中去,事务正常结束。
COMMIT;
开启事务:
查看基本表sc的所有数据:
插入数据:
再次查看基本表sc的所有数据:
可以看到数据还没写入基本表。
事务提交:
再次查看基本表sc的所有数据:
可以看到,事务提交后数据被写入基本表。
事务回滚
事务回滚,即在事务运行过程中发生某种故障,事务不能继续执行,系统将对事务中对数据库的所有已完成的数据操作全部撤销,回滚到事务开始时的状态,事务结束。
ROLLBACK;
开启事务:
查看基本表sc的所有数据:
插入数据:
再次查看基本表sc的所有数据:
可以看到数据还没写入基本表。
事务回滚:
再次查看基本表sc的所有数据:
可以看到数据没有写入基本表。
保存点
在事务中可以设置保存点,事务回滚可以回滚到指定的保存点处,而不至于每次事务回滚都回滚到事务开始前的状态。
设置保存点:
SAVEPOINT <保存点名>;
事务回滚到指定的保存点:
ROLLBACK TO SAVEPOINT <保存点名>;
指定保存点的事务回滚不会结束事务。也就是说,如果事务回滚到指定保存点,则当前事务还没有结束,之后的操作还在同一个事务内。
开启事务:
查看基本表sc的所有数据:
设置保存点p:
插入数据:
事务回滚到保存点p:
插入新的数据:
再次查看基本表sc的所有数据:
可以看到数据还没写入基本表。
如果上一步的事务回滚后事务已经结束,由于MySQL的数据操作默认是自动提交的,那么当前插入的新数据应该会写入基本表。而现在数据没写入基本表,就表明当前操作还在事务内,需要提交才会写入基本表。
事务提交:
再次查看基本表sc的所有数据:
可以看到,第一次插入的数据由于回滚已经撤销,而第二次插入的数据在提交后才写入基本表。
事务隔离级别
事务隔离级别分为读未提交(read uncommitted)、读已提交(read committed)、可重复读(repeatable read)、可序列化(serializable)这4种。
Oracle支持前2种隔离级别,默认为读已提交。
MySQL支持所有隔离级别,默认为可重复读。
MySQL表示事务隔离级别的变量为transaction_isolation。
MySQL设置事务隔离级别:
SET SESSION TRANSACTION ISOLATION LEVEL <隔离级别>;
读未提交
允许事务读取未被其他事务提交的数据操作。
可能导致脏读、不可重复读、幻读。
打开两个客户端,并设置事务隔离级别为读未提交:
客户端1开启事务,并查看表sc的所有记录:
客户端2开启事务,并更新表sc的记录:
客户端1再次查看表sc的所有记录:
最后一条记录的数据已经改变,说明在读未提交的隔离级别下,客户端1会出现“脏读”。
读已提交
允许事务读取已被其他事务提交的数据操作。
避免脏读,可能导致不可重复读、幻读。
打开两个客户端,并设置隔离级别为读已提交:
客户端1开启事务,并查看表sc的所有记录:
客户端2开启事务,并更新表sc的记录:
客户端1再次查看表sc的所有记录:
最后一条记录的数据并没有发生改变,说明在读已提交的隔离级别下,客户端1不会出现“脏读”。
客户端2提交事务:
客户端1再次查看表sc的所有记录:
最后一条记录的数据已经改变,说明在读已提交的隔离级别下,客户端1会出现“不可重复读”。
可重复读
当前事务持续期间,其他事务对当前事务操作的表进行数据操作不会影响当前事务。
避免脏读、不可重复读、幻读。
打开两个客户端,并设置隔离级别为可重复读:
客户端1开启事务,并查看表sc的所有记录:
客户端2开启事务,并更新表sc的记录:
客户端1再次查看表sc的所有记录:
最后一条记录的数据并没有发生改变,说明在可重复读的隔离级别下,客户端1不会出现“脏读”。
客户端2在表sc中插入一条记录,并提交事务:
客户端1再次查看表sc的所有记录:
最后一条记录的数据并没有发生改变,说明在可重复读的隔离级别下,客户端1不会出现“不可重复读”。
表的记录数没有发生改变,说明在可重复读的隔离级别下,客户端1不会出现“幻读”。
可序列化
当前事务持续期间,禁止其他事务对当前事务操作的表进行数据操作。
避免脏读、不可重复读、幻读。
打开两个客户端,并设置隔离级别为可重复读:
客户端1开启事务,并查看表sc的所有记录:
客户端2开启事务:
客户端2更新表sc的记录:
更新失败,说明在可重复读的隔离级别下,客户端1不会出现“脏读”、“不可重复读”。
客户端2在表sc中插入一条记录:
插入失败,说明在可重复读的隔离级别下,客户端1不会出现“幻读”。