目录
MySQL 事务主要用于处理操作量大,复杂度高的数据。比如说,在人员管理系统中,你删除一个人员,你既需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样这些数据库操作语句就构成一个事务!
- 在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务。
- 事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行。
- 事务用来管理 insert,update,delete 语句。
事务的四大特性
- 原子性:一个事务(transaction)中的所有操作, 要么全部完成,要么全部不完成 ,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
- 一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
- 隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
- 持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
在 MySQL 命令行的默认设置下,事务都是自动提交的,即执行 SQL 语句后就会马上执行 COMMIT 操作。因此要显式地开启一个事务务须使用命令 BEGIN 或 START TRANSACTION,或者执行命令 SET AUTOCOMMIT=0,用来禁止使用当前会话的自动提交。
事务的状态
根据事务所处的不同阶段,事务大致可以分为以下5个状态:
- 活动的(active) 当事务对应的数据库操作正在执行过程中,则该事务处于
活动
状态。 - 部分提交的(partially committed) 当事务中的最后一个操作执行完成,但还未将变更刷新到磁盘时,则该事务处于
部分提交
状态。 - 失败的(failed) 当事务处于
活动
或者部分提交
状态时,由于某些错误导致事务无法继续执行,则事务处于失败
状态。 - 中止的(aborted) 当事务处于
失败
状态,且回滚操作执行完毕,数据恢复到事务执行之前的状态时,则该事务处于中止
状态。 - 提交的(committed) 当事务处于
部分提交
状态,并且将修改过的数据都同步到磁盘之后,此时该事务处于提交
状态。
事务的分类
事务分为隐式事务和显式事务两种。
隐式事务:事务没有明显的开启或者结束的标志,在mysql中,默认是开启自动提交的.我们的DML语句(insert、update、delete)就是隐式事务。
select @@autocommit
显示事务:该事务具有明显的开启和结束标记,使用显式事务的前提是你得先把自动提交事务的功能给禁用。禁用自动提交功能就是设置
autocommit
变量值为0(0:禁用 1:开启)
开启事务
存在表t3
select * from t3;
-- 步骤一:开启事务(可选)
start transaction;
-- 步骤二:编写事务中的sql语句(insert、update、delete)
-- 这里实现一下"张三给李四转账"的事务过程
update t3 set balance = 150 where vname = "张三";
update t3 set balance = 150 where vname = "李四";
-- 步骤三:结束事务
commit; #提交事务 (我们已经把自动提交关闭)
-- rollback; #回滚事务:就是事务不提交(commit),回滚到事务开始前的状态,用于commit之前
回滚点的使用(setpoint)
SET AUTOCOMMIT=0;
START TRANSACTION;
[DELETE | UPDATE | INSERT | SELECT]; #回滚时要执行提交的部分
SAVEPOINT a; # 设置回滚点,且变量名为a
[DELETE | UPDATE | INSERT | SELECT]; #回滚时不执行提交的部分
ROLLBACK TO a; # 回滚时与ROLLBACK TO搭配使用
存在t4
-- 禁用自动提交事务
set autocommit = 0;
-- 开启事务
start transaction;
-- 删除id为2的记录
delete from t4 where id = 2;
-- 设置保存点名为m1
savepoint m1;
-- 删除id为3的记录
delete from t4 where id = 3;
-- 回滚到AA保存点处
rollback to m1;
可以看到id为2的人皇被删除了,而文王却还在,就是因为事务回滚到了m1处,所以id为3的那条记录被回滚掉了。
事务并发时出现的问题
在mysql下事务的隔离级别有四种且由低到高依次为Read uncommitted 、Read committed 、Repeatable read (默认)、Serializable ,这四个级别中的后三个级别可以逐个解决脏读 、不可重复读 、幻读这几类问题
1.脏读的情况:对于两个事务T1与T2,T1读取了已经被T2更新但是还没有提交的字段之后,若此时T2回滚,T1读取的内容就是临时并且无效的
开启两个mysql客户端,并创建一张测试表transaction
更改默认隔离级别REPEATABLE READ为READ UNCOMMITTED
SELECT @@tx_isolation; #查询隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; # 更改隔离级别
两个客户端同时开启事务,其中一个事务做UPDATE操作,另一个事务做SELECT
若此时黑色背景的客户端进行回滚,则白色背景的客户端读取的数据就是临时并且无效的。即脏读。
2. 不可重复读: 对于两个事务T1和T2,T1读取了一个字段,然后T2更新了该字段并提交之后,T1再次提取同一个字段,值便不相等了。不可重复读的重点是修改。
重复读取的结果不一致的情况发生。
3. 幻读: 对于两个事务T1、T2,T1从表中读取数据,然后T2进行了INSERT操作并提交,当T1'再次读取的时候,结果不一致的情况发生。幻读的重点在于新增或者删除。
不同隔离级别所解决的事务并发问题
脏读 | 不可重复读 | 幻读 | |
READ UNCOMMITTED | 1 | 1 | 1 |
READ COMMITTED | 0 | 1 | 1 |
REPEATABLE READ | 0 | 0 | 1 |
SERIALIZABLE | 0 | 0 | 0 |
READ UNCOMMITTED级别不做演示,其隔离性最低,会出现脏读、不可重复读、幻读等所有情况。
READ COMMITTED级别能够避免脏读,下面来进行演示:
1.避免脏读(一个事务读取到另一个事务未提交的数据)
2. 无法避免重复读(一个事务读取到另一个事务已经提交的数据)
REPEATABLE READ避免不可重复读的情况发生,下面来看演示:
1. 避免不可重复读(一个事务读取到另一个事务已经提交的数据)
2. 无法避免幻读(一个事务多次查询整表数据,由于其他事务新增(删除)记录造成多次查询的记录条数不同(一个事务读取到另一个事务已经提交的数据))
SERIALIZABLE避免幻读情况,阻塞方式
可以看出,serializable级别就类似加锁的方式,同一时刻支持多个事务并发,但是针对DML(UPDATE\INSERT\DELETE)操作时,当前发起操作的事务会被阻塞,直到其他事务commit或者rollback才会继续执行事务语句。可见效率十分低下。