数据库各隔离级别下的存在的并发访问问题:更新丢失、脏读、不可重复读、幻读

注意这里讨论的是抽象意义上的数据库,mysql数据库有自己特有的实现。

事务隔离级别更新丢失脏读不可重复读幻读
未提交读RU(Read Uncommited)避免发生发生发生
已提交读RC(Read Commited)避免避免发生发生
可重复读RR(Repeatable Read)避免避免避免发生
串行化(Serializable)避免避免避免避免

事务并发访问引起的问题以及如何避免

mysql会利用锁机制创建出来不同的事务隔离级别,将按照事务隔离级别从低到高进行讲解

1. 更新丢失问题

事务并发带来的一个问题:更新丢失,即一个事务的更新覆盖了另一个事务的更新

由于目前我们主流的数据库都会为我们自动加锁来避免这种更新丢失的情况,所以在数据库层面是不好进行模拟的

在这里插入图片描述
mysql的所有事务隔离级别在数据库层面上均可以避免更新丢失的问题

2. 脏读问题

dirty read,一个事务读取到了另一个事务还未提交的数据

该问题可以在Read-Committed事务隔离级别以上可避免

select @@tx_isolation

现将事务隔离级别调至最低,就是未提交读 Read Uncommitted

set session transaction isolation level read uncommitted;
-- 或者
set tx_isolation = 'read-uncommitted';

在autocommit=1 即自动提交开启的情况下执行

步骤1

session1执行

START TRANSACTION;

步骤2

session2执行

START TRANSACTION;

步骤3

session1执行

UPDATE account_innodb SET balance = 1000-100 WHERE id = 1;
SELECT * FROM account_innodb WHERE id = 1;

此时session1未提交

步骤4

session2查询

SELECT * FROM account_innodb WHERE id = 1;

居然是900了

步骤5

session1 rollback

步骤6

session2并不知道session1 rollback,其继续进行操作

UPDATE account_innodb SET balance = balance + 200 WHERE id  = 1

结果就是1100块(应该是1200块),这就是脏读带来的问题

将隔离级别改为Read Committed之后,重复上面的过程

会发现在步骤4,session2查询的时候,会发现其值为1000,而不是900,即未读到session1未提交的事务

3. 不可重复读问题

REPEATABLE READ事务隔离级别以上可以避免

含义是事务A多次读取同一数据,事务B在事务A多次读取的过程中,对数据做了更新并提交,导致事务A多次读取时数据不一致

在RC隔离级别下

步骤1

session1

start transaction;

步骤2

start transaction;

步骤3

session1先读取一次,是1200

session2加了300,之后commit

session1再读取一次,是1500

如果session1基于1200进行了操作,就可能造成数据紊乱的结果

将隔离级别改为REPEATABLE READ

再进行一次,会发现session1读取的结果一致都是第一次start transaction之前数据的值,在整个session过程中不变

那如果session1就在这个基础上做修改,那还是会存在问题吗?

session2 1500

session1 read 仍是1200,但其执行

UPDATE account_innodb SET balance = balance - 100 WHERE id = 1;
commit;

再查询,结果是1400,是正确的,而不是我们之前预想的1100,原因是RR下是第一次select的时候生成的一个read view,之后都是读取这个read view,但是最后执行update的时候,是会加X锁的

4. 幻读问题

phantom read,事务A读取与搜索条件相匹配的若干行,事务B已插入或删除操作修改数据A操作的结果集,导致事务A看起来像出现幻觉一样

SERIALIZABLE事务隔离级别可避免

步骤1

session1

START TRANSACTION;

之后

SELECT * FROM account_innodb LOCK IN SHARE MODE;

为表里的三条记录加行级锁

步骤2

session2

START TRANSACTION;
INSERT INTO account_innodb VALUES(4,"newman",750);

步骤3

session1

UPDATE account_innodb SET balance = 1000;

对session1来说,它本想更新自己的三条记录,结果却更新了4条,好像出现的幻觉一样

以上是我们想象中的幻读的情况,但实际上innodb在session2 insert的时候,就会block住,也就是说innodb在REPEATABLE READ级别实现了对幻读问题的处理

为了模拟上面幻读的情况,可以将事务隔离级别降级到Read Committed

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值