数据库事务之隔离级别

Posted 李星星5488

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据库事务之隔离级别相关的知识,希望对你有一定的参考价值。

数据库事务四大属性(ACID特性):

  • 原子性(atomicity) 一个事务是不可分割的一部分,要么都做,要么都不做;

  • 一致性(consistency)一个事务必须是从一个一致性状态到另一个一致性状态;

  • 隔离性(isolation) 一个事务的内部操作不能被另一个事务干扰;

  • 持久性(durability) 一个事务一旦提交,它对数据库的更改是永久性的(我的理解应该指落到了磁盘上而不是还在内存) 。

隔离性的级别

隔离性的几个级别:0 未提交读(read uncommitted) : 未提交读会产生脏读,即一个事务可以读取另一个未提交的事务的数据(更改);

1 提交读(read committed):会产生不可重复读和幻读,即一个事务中两次读,第二次读到别的事务的数据(更改,提交后的);

2 可重复读(repeatable read):避免脏读,不可重复读,允许幻像读;

3 串行化读(serializable),事务只能一个一个执行,避免了脏读、不可重复读、幻读;

实验数据的环境:

windows10,mysql8.0.13,datagrip2019.2

脏读示例

开启三个连接:

A连接使用read uncommitted;

 
   
   
 
  1. #脏读演示 A

  2. # step0

  3. set session transaction isolation level read uncommitted;

  4. # step1.1 A读数据 balance = 0;

  5. select * from bank_account where id = 1;

  6. # step2.1 A开启事务(不开亦可);

  7. start transaction;

  8. # step5.1 A读数据 balance = 1;

  9. select * from bank_account where id = 1;

  10. # step6.1 A提交(同step2.1);

  11. commit ;

B连接使用read uncommitted;

 
   
   
 
  1. # 脏读演示 B

  2. # step0

  3. set session transaction isolation level read uncommitted;

  4. # step0 设置不自动提交事务(这里是session级别的)

  5. set autocommit=0;

  6. #step3 B开启事务

  7. start transaction ;

  8. #step4 B更新数据更改 balance = 1

  9. update bank_account set balance = 1 where id = 1;

  10. #step7 B提交事务

  11. commit ;

C连接使用repeatable read(mysql默认级别);

 
   
   
 
  1. #脏读演示 C

  2. # step0

  3. set session transaction isolation level REPEATABLE READ;

  4. # step1.2 C读数据 balance = 0;

  5. select * from bank_account where id = 1;

  6. # step2.2 C开启事务(不开亦可);

  7. #start transaction;

  8. # step5.2 C读数据 balance = 0;

  9. select * from bank_account where id = 1;

  10. # step6.2 C提交(同step2.2);

  11. commit ;

  • 首先设置三个连接的session隔离级别;可以使用 以下语句查看是否设置正确:

 
   
   
 
  1. show session variables like '%isolation%';

衍生一个题外话:MySQL隔离级别分为session和global两种

设置global的方式只需要把session替换成global即可

  • 按照上述语句依次运行即可(语句上方为说明)

按照顺序从上往下依次执行

总结:
  1. 可以发现read uncommitted 级别,可以随意读取别人事务中更改的数据(别人未提交事务),这个级别相当于根本没有事务的四大属性之—>隔离性(isolation)了,别人只是声明了要更改数据,你便相信了,别人要是反悔了呢(rollback);

  2. 必须要两个连接都是设置的可以发生脏读的级别才可以看到数据,演示C是repeatable 便看不到事务B中的数据。

不可重复读示例

 
   
   
 
  1. #不可重复读演示 A

  2. # step0

  3. set session transaction isolation level read committed;

  4. # step1.1 A读数据 balance = 0;

  5. select * from bank_account where id = 1;

  6. # step2.1 A开启事务;

  7. start transaction;

  8. # step3.1 A读数据 balance = 0;

  9. select * from bank_account where id = 1;

  10. # todo something

  11. # step 6.1 A读数据 balance = 0 这里说明已经避免了脏读;

  12. select * from bank_account where id = 1;

  13. # step 8.1 A读数据 balance = 1 这里出现了不可重复读;

  14. select * from bank_account where id = 1;

  15. # step9.1 A提交(同step2.1);

  16. commit ;

  17. # step 10.1 A读数据 balance = 1 ;

  18. select * from bank_account where id = 1;

 
   
   
 
  1. # 不可重复读演示 B

  2. # step0

  3. set session transaction isolation level read committed;

  4. #step4 B开启事务

  5. start transaction ;

  6. #step5 B更新数据

  7. update bank_account set balance = 1 where id = 1;

  8. #step7 B提交事务

  9. commit ;

 
   
   
 
  1. #不可重复读演示 C

  2. # step0

  3. set session transaction isolation level REPEATABLE READ;

  4. # step1.2 C读数据 balance = 0;

  5. select * from bank_account where id = 1;

  6. # step2.2 C开启事务;

  7. # start transaction;

  8. # step3.2 C读数据 balance = 0;

  9. select * from bank_account where id = 1;

  10. # step6.2 C读数据 balance = 0 这里说明已经避免了脏读;

  11. select * from bank_account where id = 1;

  12. # step8.2 C读数据 balance = 0 这里避免了不可重复读 ;

  13. select * from bank_account where id = 1;

  14. # step9.2 C提交(同step2.2);

  15. commit ;

  16. #step 10.2 C读数据 balance = 1

  17. select * from bank_account where id = 1;

  • 按照上述语句依次运行即可(语句上方为说明)

按照顺序从上往下依次执行


总结:

可以发现read commited 级别,可以在自己的事务中读取别人事务已经提交的数据(自己事务未提交),会出现两次读取数据记录不一样的结果。

幻读

这里我觉得有些许争议:关于不可重复读和幻读的

  • 解释一(百度百科):隔离级别

不可重复读:一事务对数据进行了更新或删除操作,另一事务两次查询的数据不一致幻读:一事务对数据进行了新增操作,另一事务两次查询的数据不一致

  • 解释二(CSDN):littlexiaoshuishui对于这篇文章的回答

不知道是谁发明了不可重复读和幻读问题,这本质都是一样的东西啊,就是一个事务内两次读取同一些数据,结果不一样。这就是统一的幻读问题啊。行级锁可以解决更新和删除导致的幻读,间隙锁可以解决插入导致的幻读。为啥要分开呢,导致很多人看的时候云里雾里,而且很多人也是跟着人云亦云。

总结:

幻读和不可重复读非常类似,这里留给读者自己思考...

隔离级别 脏读 不可重复读 幻读
读未提交
读已提交 不会
可重复读 不会 不会
串行 不会 不会 不会

演示数据脚本:

 
   
   
 
  1. create table bank_account

  2. (

  3. id int auto_increment,

  4. consumer varchar(20) not null,

  5. balance int default 0 not null,

  6. constraint bank_account_consumer_uindex

  7. unique (consumer),

  8. constraint bank_account_id_uindex

  9. unique (id)

  10. );


  11. alter table bank_account

  12. add primary key (id);

到此,数据库隔离级别介绍完毕



以上是关于数据库事务之隔离级别的主要内容,如果未能解决你的问题,请参考以下文章

精通Java事务编程-弱隔离级别之已提交读

事务并发之隔离级别

MYSQL数据库之事务隔离级别详解

软件开发之数据库事务四大特性及隔离级别

数据库 之 事务控制和隔离级别

精通Java事务编程-可串行化隔离级别之真串行