vlambda博客
学习文章列表

数据库事务与四种事务隔离级别详解及对比

事务的四大特性:ACID


原子性(Atomicity):事务中的操作要么都发生,要么都不发生

一致性(Consistency):事务必须使数据库从一个一致性状态变换到另外一个一致性状态

隔离性(Isolation):一个事务内部的操作及使用的数据对并发的其他事务是隔离的不同的事务之间彼此没有任何干扰

事务隔离分为不同级别,包括读未提交(Read Uncommitted)、读提交(Read Committed)、可重复读(Repeatable Read)和串行化(Serializable)。

持久性(Durability):事务对数据库的所有更新是永久保存到数据库,即使系统故障也不会丢失。


至于ACID实现原理,像 undo log redo log mvcc 快照读当前读还未出现在笔者的笔记本里(小编是小白) 这里就不展开说了后续看看要不要单独出一篇。

 

事务并发带来的问题


脏读: 事务A读取到事务B未提交的数据

不可重复读:同一个事务中多次读取的数据不一致

幻读:同一个事务内多次查询返回的行数不同(一般针对插入和删除操作

 

不同隔离级别的并发事务问题


隔离级别

脏读

不可重复读

幻读

读未提交(Read Uncommitted

读提交(Read Committed

可重复读(Repeatable Read

串行化(Serializable

 

 

虽然串行化可以解决事务并发带来的问题,但是效率却是最低的

一般互联网项目不会采用串行化作为隔离级别


mysql 默认 可重复读

oracle 默认 读提交

 


查看与配置隔离级别


MySQL为例(MySql存引擎是InnoDB 才支持事务)

查看事务隔离级别:SELECT @@tx_isolation;

设置隔离级别set session transaction isolation level ISOLATION NAME


//设置read uncommitted级别:

set session transaction isolation level read uncommitted;

 

//设置read committed级别:

set session transaction isolation level read committed;

 

//设置repeatable read级别:

set session transaction isolation level repeatable read;

 

//设置serializable级别:

set session transaction isolation level serializable;

 


脏读、不可重复读、幻读的实践


准备工作:

Test 表数据

Id

Age

Name

1

20

张三

2

25

李四

3

30

隔壁老王

将事务隔离级别设置为:读未提交

set session transaction isolation level read uncommitted;



脏读:

步骤

事务A

事务B

1

start transaction;


2

SELECT * FROM test WHERE `name`='张三';


3


start transaction;

4


UPDATE `test` SET `age`=25  WHERE `name`='张三';

5

SELECT * FROM `test` WHERE `name`='张三';


6


COMMIT;


步骤2 结果:

Id

Age

Name

1

20

张三

步骤5 结果:

Id

Age

Name

1

25

张三



不可重复读:

步骤

事务A

事务B

1

start transaction;


2

SELECT * FROM `test` WHERE `name`='李四';


3


start transaction;

UPDATE `test` SET `age`=24  WHERE `name`='李四'

COMMIT;

 

4

SELECT * FROM `test` WHERE `name`='李四';


 

步骤2 结果:

Id

Age

Name

2

25

李四

步骤4 结果:

Id

Age

Name

1

24

李四



幻读:

步骤

事务A

事务B

1

start transaction;


2

SELECT * FROM `test` WHERE `age`=24;


3


start transaction;

INSERT INTO test (`age`,`name`) VALUES(24,"靠收租维持生活的小姐姐") ;

COMMIT;

 

4

SELECT * FROM `test` WHERE `age`=24;


 

步骤2 结果:

Id

Age

Name

1

24

李四

步骤4 结果:

Id

Age

Name

1

24

李四

4

24

靠收租维持生活的小姐姐