事务:一个或一组sql语句组成的一个执行单元,在这个单元中。每条sql语句是相互依赖的。这个执行单元要么全部执行,要么全部失败。(最典型的事务,转账)
事务作为不可分割的整体,如果某条语句执行失败,那么整个单元就会回滚,如果所有sql语句执行成功,则事务顺利执行。
MySQL中innodb支持事务。
事务的属性特点 (ACID)(Atomicity, Consistency, Isolation, Durability)
隐式事务:事务没有明显的开始结束标志,如insert update delete.
显式事务:事务具有明显的开启结束标志,前提是设置自动提交功能关闭,set autocommit=0
显式事务的常规写法。
set autocommit=0; # 设置自动提交关闭
start transaction; #开启事务(可选,即使不写默认开启)
sql语句1;
......
commit; |rollback; # 提交事务|回滚事务(放弃执行)
事务的使用是比较简单的 主要是掌握使用时机。以及事务使用中可能遇到的问题。
当事务访问相同数据且没有采取必要隔离时,会导致各种并发问题。
如 脏读 不可重复读 幻读
MySQL 中支持四种隔离级别,从低到高依次是
查看当前隔离级别 默认 repeatable read
select @@tx_isolation;
修改当前会话隔离级别 (如修改为 read committed),若修改全局则将session换为global, session 默认值,可省。
set session transaction isolation level read committed;
测试隔离级别 对 事务的影响。
创建表
create table hero(id int primary key auto_increment, heroName char(20), age int);
添加数据;
insert into hero values(0, "胡斐", 20);
insert into hero values(0, "苗人凤", 40);
insert into hero values(0, "李寻欢", 34);
insert into hero values(0, "乔峰", 40);
insert into hero values(0, "郭靖", 20);
当前数据 如下
开启两个会话来展示,不同隔离级别对事物影响。
测试脏读。 两个会话修改隔离级别为(read uncomitted),只要一个修改为read uncomitted就可以用来测试。。。为了统一采用相同隔离级别。 尽量使用session使用global需重新打开一个会话才会生效
设置隔离级别
set session transaction isolation level read uncommitted;
两个会话关闭自动提交,开始事务。
set autocommit=0;
start transaction;
session1查看数据。
session2 修改数据。将胡斐年龄改为25,但不提交。
update hero set age=25 where id=1;
session1再次查看数据。读取到了session2未提交的数据。
session2 回滚,即放弃提交。
rollback;
session1 再次读取发现回到了起点。
session1 也rollback;
修改两个会话隔离级别为read committed 解决脏读问题,测试不可重复读。
set session transaction isolation level read committed;
开启事务:
set autocommit=0;
start transaction;
session1读取原始数据:
session2 修改数据但未提交:
update hero set age=25 where id=1;
session1 读取数据未受影响,解决了脏读:
session2 提交数据。这次试用commit提交数据
commit;
session1查看数据 发生改变,不可重复读。
将session1 事务回滚 rollback;
修改 隔离级别 为 repeatable read; 就是磨人的隔离级别,可避免脏读和不可重复读,但可能出现幻读问题。
set session transaction isolation level repeatable read;
开启事务:
set autocommit=0;
start transaction;
session1读取原始数据。
session2 修改数据未提交。将李寻欢年龄改为30
update hero set age=30 where id=3;
session1 再次读取数据,没有脏读问题。这里就不截图了。。。
session2 提交数据。
commit;
session1 读取数据,数据没有改变。解决了不可重复读问题。
session1 事务结束。查看数据,这时才看到session2修改的数据。
幻读展示以及修改隔离级别最高解决幻读(效率太低,一般不会使用最高隔离级别的)
开启事务。
set autocommit=0;
start transaction;
session1 查看原始数据。
session2 新增数据,并提交。
insert into hero values(0, "张无忌", 20);
commit;
查看session1中数据
好像没什么变化,接下来session 修改数据。
update hero set age=10;
可以看到 有6行数据受影响。
再次查看 不止有 session1修改的age,还多了一个张无忌。。。
session1 放弃提交rollback;
解决幻读问题,设置隔离级别serializable****
set session transaction isolation level serializable;
开启事务。
set autocommit=0;
start transaction;
session2 新增数据时会阻塞,等待session1提交事务或者回滚:
可以看到session2阻塞了很长时间。
session1 查看数据也会阻塞,需要等到session2 提交或回滚:
尽管解决的幻读问题,但是代价有点太大了。。。
两个事务读和写都要阻塞,这样的效率太低了,所以一般不会使用serializable隔离级别,使用默认的隔离级别就好。