MySQL 事务,事务隔离

事务:一个或一组sql语句组成的一个执行单元,在这个单元中。每条sql语句是相互依赖的。这个执行单元要么全部执行,要么全部失败。(最典型的事务,转账)
事务作为不可分割的整体,如果某条语句执行失败,那么整个单元就会回滚,如果所有sql语句执行成功,则事务顺利执行。

MySQL中innodb支持事务。

事务的属性特点 (ACID)(Atomicity, Consistency, Isolation, Durability)

  • A 原子性 事务是不可分割的工作单位。事务中操作要么全部发生要么全部不发生
  • C 一致性 事务必须使数据库从一个一致性状态变换到另一个一致性状态。
  • I 隔离性,事务执行不能被其他事务干扰,即一个事务内部操作即使用数据对其他事务是隔离的,并发执行各个事务之间不能相互干扰
  • D 持久性 事务一旦提交,它对数据库中数据改变是永久性的。

隐式事务:事务没有明显的开始结束标志,如insert update delete.
显式事务:事务具有明显的开启结束标志,前提是设置自动提交功能关闭,set autocommit=0

显式事务的常规写法。

set autocommit=0;   # 设置自动提交关闭
start transaction;	#开启事务(可选,即使不写默认开启)
sql语句1;
......
commit; |rollback; # 提交事务|回滚事务(放弃执行)

事务的使用是比较简单的 主要是掌握使用时机。以及事务使用中可能遇到的问题。

当事务访问相同数据且没有采取必要隔离时,会导致各种并发问题。
如 脏读 不可重复读 幻读

  • 脏读 对于两个事物T1,T2,T1读取了已经被T2更新单还未提交的字段之后,若T2回滚,T1读取的内容就是临时且无效的。
  • 不可重复读 对于两个事务T1,T2 ,T1读取了一个字段,然后T2更新了该字段之后,T1再次读取同一个字段,值就不同了。
  • 幻读对于两个事务T1,T2,T1从一个表中读取了一个字段,然后T2在这张表中插入一些新的行,如果T1再次读取同一个表,就会多出几行。

MySQL 中支持四种隔离级别,从低到高依次是

  • read uncomitted 不可避免脏读 不可重复读 幻读
  • read commited 可避免脏读,不可避免 不可重复读与幻读
  • repeatable read 可避免脏读与不可重复读,但不可避免幻读。MySQL默认的隔离级别
  • serializable 最高的隔离级别,可避免脏读,不可重复读与幻读。

查看当前隔离级别 默认 repeatable read

select @@tx_isolation;

MySQL 事务,事务隔离_第1张图片
修改当前会话隔离级别 (如修改为 read committed),若修改全局则将session换为global, session 默认值,可省。

 set session transaction isolation level read committed;

MySQL 事务,事务隔离_第2张图片

测试隔离级别 对 事务的影响。

创建表

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);

当前数据 如下
MySQL 事务,事务隔离_第3张图片
开启两个会话来展示,不同隔离级别对事物影响。

测试脏读。 两个会话修改隔离级别为(read uncomitted),只要一个修改为read uncomitted就可以用来测试。。。为了统一采用相同隔离级别。 尽量使用session使用global需重新打开一个会话才会生效

设置隔离级别

set session transaction isolation level read uncommitted;

两个会话关闭自动提交,开始事务。

set autocommit=0;
start transaction;

session1查看数据。
MySQL 事务,事务隔离_第4张图片
session2 修改数据。将胡斐年龄改为25,但不提交。

update hero set age=25 where id=1;

session1再次查看数据。读取到了session2未提交的数据。
MySQL 事务,事务隔离_第5张图片

session2 回滚,即放弃提交。

rollback; 

session1 再次读取发现回到了起点。
MySQL 事务,事务隔离_第6张图片
session1 也rollback;

修改两个会话隔离级别为read committed 解决脏读问题,测试不可重复读。

 set session transaction isolation level read committed;

开启事务:

set autocommit=0;
start transaction;

session1读取原始数据:
MySQL 事务,事务隔离_第7张图片
session2 修改数据但未提交:

update hero set age=25 where id=1;

session1 读取数据未受影响,解决了脏读:
MySQL 事务,事务隔离_第8张图片
session2 提交数据。这次试用commit提交数据

 commit;

session1查看数据 发生改变,不可重复读。
MySQL 事务,事务隔离_第9张图片
将session1 事务回滚 rollback;

修改 隔离级别 为 repeatable read; 就是磨人的隔离级别,可避免脏读和不可重复读,但可能出现幻读问题。

 set session transaction isolation level repeatable read;

开启事务:

set autocommit=0;
start transaction;

session1读取原始数据。MySQL 事务,事务隔离_第10张图片
session2 修改数据未提交。将李寻欢年龄改为30

update hero set age=30 where id=3;

session1 再次读取数据,没有脏读问题。这里就不截图了。。。
session2 提交数据。

commit;

MySQL 事务,事务隔离_第11张图片
session1 读取数据,数据没有改变。解决了不可重复读问题。
MySQL 事务,事务隔离_第12张图片

session1 事务结束。查看数据,这时才看到session2修改的数据。
MySQL 事务,事务隔离_第13张图片

幻读展示以及修改隔离级别最高解决幻读(效率太低,一般不会使用最高隔离级别的)

开启事务。

set autocommit=0;
start transaction;

session1 查看原始数据。
MySQL 事务,事务隔离_第14张图片
session2 新增数据,并提交。

insert into hero values(0, "张无忌", 20);
commit;

查看session1中数据
MySQL 事务,事务隔离_第15张图片
好像没什么变化,接下来session 修改数据。

 update hero set age=10;

可以看到 有6行数据受影响。6行数据
再次查看 不止有 session1修改的age,还多了一个张无忌。。。
MySQL 事务,事务隔离_第16张图片
session1 放弃提交rollback;

解决幻读问题,设置隔离级别serializable****

set session transaction isolation level serializable;

开启事务。

set autocommit=0;
start transaction;

session2 新增数据时会阻塞,等待session1提交事务或者回滚:
阻塞
可以看到session2阻塞了很长时间。
MySQL 事务,事务隔离_第17张图片
session1 查看数据也会阻塞,需要等到session2 提交或回滚:
MySQL 事务,事务隔离_第18张图片
尽管解决的幻读问题,但是代价有点太大了。。。
两个事务读和写都要阻塞,这样的效率太低了,所以一般不会使用serializable隔离级别,使用默认的隔离级别就好。

你可能感兴趣的:(MySQL事务,事务隔离级别,数据库,MySQL)