MySql之事务详解

1.什么是事务

  • 事务是一个原子操作。是一个最小执行单元。可以甶一个或多个SQL语句组成
  • 在同一个事务当中,所有的SQL语句都成功执行时,整个事务成功,有一个SQL语句执行失败,整个事务都执行失败。

2.事务特性

  • 原子性(Atomicity):事务是一个原子性质的操作单元,事务里面的对数据库的操作要么都执行,要么都不执行。
  • 一致性(Consistency):在事务开始之前和完成之后,数据都必须保持一致状态,必须保证数据库的完整性。也就是说,数据必须符合数据库的规则。
  • 隔离性(Isolation):一个事务的执行不能被其他事务干扰。即一个事务的内部操作及使用的数据对其他并发事务是隔离的,并发执行的各个事务之间不能互相干扰。
  • 持久性(Durability):持久性也成为永久性,指一个事务一旦提交,它对数据库中数据的改变就应该是永久的。

3.MySQL事务操作

事务分为隐式事务显式事务

查看是否开启自动提交

show variables like 'autocommit';

手动控制事务

方式一:

示例一:提交事务操作

set autocommit=0;
insert into t_test values(1);
commit;

示例二:回滚事务操作

set autocommit=0;
insert into t_test values(2);
rollback;
方式二:

autocommit还原回去:

set autocommit=1;

示例一:提交事务操作

start transaction;
insert into t_test values (2);
insert into t_test values (3);
commit;

最终结果:成功插入2条数据记录。

示例二:回滚事务操作

start transaction;
delete from t_test;
rollback;

最终结果:由于执行了rollback操作,导致删除操作回滚。

3.3.savepoint关键字

可以实现回滚事务中部分数据,相当于一个书签,放在哪回滚到哪

savepoint效果演示:

start transaction;
insert into t_test values (1);
savepoint point1;
insert into t_test values (2);
rollback to point1;
commit;

最终结果:保存点point1rollback to point1之间的操作将被回滚。

3.4.只读事务

表示在事务中执行的是一些只读操作,如查询,但是不会做insert、update、delete操作。

start transaction read only;
select * from t_test;
delete from t_test;
commit;

最终结果:只允许查询操作,编辑操作无效报错。

4.并发事务中会出现的问题

  • **更新丢失:**两个不同的事务(或者Java程序线程)在某一时刻对同一数据进行读取后,先后进行修改。导致第一次操作数据丢失。

  • **读已提交:**即一个事务操作过程中可以读取到其他事务已经提交的数据。

  • **脏读:**是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。

  • **不可重复读:**什么意思呢,就是在一个事务内,多次读取同一个数据,却返回了不同的结果。实际上,这是因为在该事务间隔读取数据的期间,有其他事务对这段数据进行了修改,并且已经提交,就会发生不可重复读事故;相反,“可重复读” 在同一事务中多次读取数据时, 能够保证所读数据一样, 也就是后续读取不能读到另一事务已提交的更新数据。

  • **可重复读:**一个事务操作中对于一个读取操作不管多少次,读取到的结果都是一样的。

  • **幻读:**是指当事务不独立执行时,插入或者删除另一个事务当前影响的数据而发生的一种类似幻觉的现象。举个例子,某事务在检查表中的数据数count时,是10,过一段时间之后再查是11,这就发生了幻读,之前的检测获取到的数据如同幻觉一样。

    出现幻读和不可重复读的原因很像,都是在多次操作数据的时候发现结果和原来的不一样了,出现了其他事务干扰的现象。但是,幻读的偏重点是添加和删除数据,多次操作数据得到的记录数不一样;不可重复读的偏重点是修改数据,多次读取数据发现数据的值不一样了。

5.隔离级别

如果没有指定隔离级别,数据库就会使用默认的隔离级别。在MySQL中,如果使用 InnoDB,默认的隔离级别是Repeatable Read

隔离级别分为4种:

  • 读未提交(Read Uncommitted):在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)

  • 读已提交(Read Committed):这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别也支持所谓的不可重复读(NonrepeatableRead),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果

  • 可重复读(Repeatable Read):这是MySQL的默认事务隔离级别,同一事务的多个实例在并发读取数据时,会看到同样的数据。不过理论上,这会导致另一个棘手的问题:幻读(Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。

  • 可串行化(Serializable):这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争

隔离级别脏读不可重复读幻读
Read Uncommitted
Read Committed不会
Repeatable Read不会不会
Serializable不会不会不会

上面4中隔离级别越来越强,会导致数据库的并发性也越来越低。

查看隔离级别:

show variables like 'transaction_isolation';

隔离级别的设置:

修改MySQL中的my.ini文件,设置隔离级别,如下:

# 创建新表时将使用的默认存储引擎
default-storage-engine=INNODB
# 隔离级别设置,READ-UNCOMMITTED读未提交,READ-COMMITTED读已提交,REPEATABLE-READ可重复读,SERIALIZABLE串行
transaction-isolation=REPEATABLE-READ

以管理员身份打开cmd窗口,重启MySQL,如下(或在服务中重启动MySql服务):

C:\Windows\system32>net stop mysql
mysql 服务正在停止..
mysql 服务已成功停止。

C:\Windows\system32>net start mysql
mysql 服务正在启动 .
mysql 服务已经启动成功。

5.1.READ-UNCOMMITTED(读未提交)

请将MySQL的隔离级别设置为:READ-UNCOMMITTED(读未提交)。(注意:一定要重启MySQL服务)

在这里插入图片描述

请先清空t_test表中的所有数据,再进行后续操作。

会话1窗口,操作如下:

start transaction;
select * from t_test;
select * from t_test;
commit;

会话2窗口,操作如下:

start transaction;
insert into t_test values (1);
select * from t_test;
commit;

最终结果:

  1. T2-会话1:无数据,T6-会话1:有数据,T6时刻会话2还未提交,此时会话1已经看到了会话2插入的数据,说明出现了脏读
  2. T2-会话1:无数据,T6-会话1:有数据,查询到的结果不一样,说明不可重复读

结论:读未提交情况下,可以读取到其他事务还未提交的数据,多次读取结果不一样,出现了脏读、不可重复读、幻读

5.2.READ-COMMITTED(读已提交)

请将MySQL的隔离级别设置为:READ-COMMITTED(读已提交)。(注意:一定要重启MySQL服务)

在这里插入图片描述

请先清空t_test表中的所有数据,再进行后续操作。

会话1窗口,操作如下:

start transaction;
select * from t_test;
select * from t_test;
select * from t_test;
commit;

会话2窗口,操作如下:

start transaction;
insert into t_test values (1);
select * from t_test;
commit;

最终结果:

  1. T5-会话2:有数据,T6-会话1:无数据,会话1看不到会话2的数据,说明没有脏读
  2. T6-会话1:无数据,T8-会话1:看到了会话2插入的数据,此时会话2已经提交了,会话1看到了会话2已提交的数据,说明可以读取到已提交的数据
  3. T2-会话1T6-会话1:无数据,T8-会话1:有数据,多次读取结果不一样,说明不可重复读。

结论:读已提交情况下,无法读取到其他事务还未提交的数据,可以读取到其他事务已经提交的数据,多次读取结果不一样,未出现脏读,出现了读已提交、不可重复读、幻读

5.3.REPEATABLE-READ(可重复读)

5.3.1.可重复读

请将MySQL的隔离级别设置为:REPEATABLE-READ(可重复读)。(注意:一定要重启MySQL服务)

在这里插入图片描述

请先清空t_test表中的所有数据,再进行后续操作。

会话1窗口,操作如下:

start transaction;
select * from t_test;
select * from t_test;
select * from t_test;
commit;
select * from t_test;

会话2窗口,操作如下:

start transaction;
insert into t_test values (1);
select * from t_test;
commit;

最终结果:

  1. T2-会话1T6-会话1:无数据,T5-会话2:有数据,会话1看不到会话2的数据,说明没有脏读
  2. T8-会话1:无数据,此时会话2已经提交了,会话1看不到会话2已提交的数据,会话1中3次读的结果一样都是没有数据的,说明可重复读

结论:可重复读情况下,未出现脏读,未读取到其他事务已提交的数据,多次读取结果一致,即可重复读。

5.3.2.幻读

请将MySQL的隔离级别设置为:REPEATABLE-READ(可重复读)。(注意:一定要重启MySQL服务)

准备演示数据,创建数据库:

create table t_user(id int primary key,name varchar(16) unique key);
insert into t_user values (1,'路人甲Java'),(2,'路人甲Java');
ERROR 1062 (23000): Duplicate entry '路人甲Java' ***\*for\**** key 'name'

select * from t_user;

注意:这里特地给t_user中的name字段添加了唯一约束,表示name不能重复,否则报错。
在这里插入图片描述

会话1窗口,操作如下:

start transaction;
select * from t_user where name='张三';
insert into t_user values (2,'张三');
ERROR 1062 (23000): Duplicate entry '张三' for key 'name'
select * from t_user where name='张三';
commit;

会话2窗口,操作如下:

start transaction;
insert into t_user values (1,'张三');
select * from t_user;
commit;

最终结果:
会话1想插入数据“张三”,插入之前先查询了一下(T5-会话1时刻)该用户是否存在,发现不存在,然后在T7-会话1时刻执行插入,报错了,报数据已经存在了,因为T6-会话2时刻已经插入了“张三”。

然后会话1有点郁闷,刚才查的时候不存在的,然后会话1不相信自己的眼睛,又去查一次(T8-会话1时刻),发现“张三”还是不存在的。

总结:数据明明不存在,但是就是插入不了,如同发生了幻觉一样。

5.4.SERIALIZABLE(串行)

SERIALIZABLE会让并发的事务串行执行。多个事务之间读写、写读、写写会产生互斥,效果就是串行执行,多个事务之间的读读不会产生互斥。

请将MySQL的隔离级别设置为:SERIALIZABLE(串行)。(注意:一定要重启MySQL服务)

在这里插入图片描述

总结:按时间顺序运行上面的命令,会发现T4-会话2这样会被阻塞,直到T5-会话1执行完毕。

6.总结

  1. 需要对各种隔离级别产生的现象非常了解,然后选择的时候才能游刃有余
  2. 隔离级别越高,并发性也低,比如最高级别SERIALIZABLE会让事务串行执行,并发操作变成串行了,会导致系统性能直接降低。
  3. 具体选择哪种需要结合具体的业务来选择。
  4. 读已提交(READ-COMMITTED)通常用的比较多

置为:SERIALIZABLE(串行)。(注意:一定要重启MySQL服务)

[外链图片转存中…(img-hmPxMDWv-1710209600697)]

总结:按时间顺序运行上面的命令,会发现T4-会话2这样会被阻塞,直到T5-会话1执行完毕。

6.总结

  1. 需要对各种隔离级别产生的现象非常了解,然后选择的时候才能游刃有余
  2. 隔离级别越高,并发性也低,比如最高级别SERIALIZABLE会让事务串行执行,并发操作变成串行了,会导致系统性能直接降低。
  3. 具体选择哪种需要结合具体的业务来选择。
  4. 读已提交(READ-COMMITTED)通常用的比较多
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值