MySQL事务隔离级别

本文详细介绍了MySQL的四种事务隔离级别:未提交读、提交读、可重复读和可串行化,通过实例展示了它们的区别。在可重复读级别,InnoDB通过MVCC解决幻读问题,而在可串行化级别,事务执行将被串行化以防止幻读,但可能导致锁竞争。文章还讨论了快照读和当前读的概念,并分析了并发更新时可能出现的情况。
摘要由CSDN通过智能技术生成
背景

试想一下,当我们购买商品的时候,一般会有一下几个流程:

  1. 扣减商品数量
  2. 记录购买信息

如果商品扣减了,结果记录用户购买信息的时候失败了,那么数据就会出现不一致的问题。这种场景就可以使用事务。因为事务的一个特性就是原子性:要么不做,要么全做(事务的ACID的特性最终目的是为了保证数据的一致性)

上述问题解决了。再试想一下,在购买前,先查询商品剩余数量,如果商品<1,则商品售罄;如果事务A扣减商品数量但未提交,事务B查询剩余商品的数量,此时应该是多少呢?这就和事务的隔离级别有关系了。

实践

在讨论隔离级别之前,我们先做一些数据库初始化工作

CREATE TABLE `goods` (
  `id` int(11) NOT NULL,
  `count` int(11) NOT NULL DEFALUT '0' COMMENT '商品数量',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

初始化一个商品

insert into goods (id,count) values (1,1);

定义

在SQL标准中定义了四种隔离级别,每一种级别都规定了一个事务中所做的修改,哪些在事物内和事务间是可见的,哪些是不可见的。较低级别的隔离通常可以执行更高的并发,系统开销也更低。

1.未提交读 READ UNCOMMITTED

事务中的修改,即使没有提交,对其他事务也是可见的。事务可以读取未提交的数据,也称为脏读。(实际应用中很少使用)

mysql演示:
将隔离级别设置为未提交读
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

执行过程事务A事务B
1start transcation;start transcation;
2select count from goods where id = 1;(count = 1)
3update goods set count = count - 1 where id = 1;
4select count from goods where id = 1;(count = 0)select count from goods where id = 1;(count = 0)
5rollbak;
6commit;

可以看到事务B读取到了事务A未提交的数据,它商品售卖已结束。但如果此时事务A回滚,但售卖实际是未结束的。这就是脏读

2.提交读 READ COMMITTED

提交读可以满足上面提到的隔离性的简单定义:一个事务开始时,只能“看见”已经提交的事务所做的修改。换句话说一个事务开始直到提交之前,所做的任何修改都不可见。
这个隔离级别也叫不可重复读(nonrepeatable read),因为执行两次查询,可能会得到不同的结果(事务开始前和事务结束后)

mysql演示:
将隔离级别设置为提交读
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;

执行过程事务A事务B
1start transcation;start transcation;
2select count from goods where id = 1;(count = 1)
3update goods set count = count - 1 where id = 1;
4select count from goods where id = 1;(count = 0)select count from goods where id = 1;(count = 1)
5commit;select count from goods where id = 1;(count = 0)
6commit;

可以看到事务A提交之前的改动事务B是读取不到的。只有事务A提交之后事务B才能读到事务A的改动。在事务B中先后两次读取,count的值是不一样的,这就叫做不可重复读。

3.可重复读 REPEATABLE READ

REPEATABLE READ 可以解决脏读的问题。该级别保证了再同一个事务中多次读取同样的记录结果都是一致的。
但理论上,可重复读隔离级别还是无法解决另一个幻读的问题。所谓幻读,指的是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行。
InnoDB和XtraDB存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)解决了幻读的问题。
可重复读是MYSQL的默认事务隔离级别

mysql演示:
将隔离级别设置为可重复读
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;

执行过程事务A事务B
1start transcation;start transcation;
2select count from goods where id = 1;(count = 1)
3update goods set count = count - 1 where id = 1;
4select count from goods where id = 1;(count = 0)select count from goods where id = 1;(count = 1)
5commit;select count from goods where id = 1;(count = 1)
6commit;

可以看到,无论事务A是否提交,事务B读到的结果都是一致的。

思考

下面默认都是讨论MYSQL RR 隔离级别的情况。

如果用户1和用户2同时购买一件商品,并且都进入到了购买的事务中。A事务扣减了商品数量,B也扣减了商品数量。假设商品总数是N,那么两个事务并发执行,那么不论A有没有提交,B得到的数量都是N,B事务执行后为N-1,而事务A也是N-1,这样就有问题了,期望的是N-2。

快照读、当前读

在事务中,执行普通select查询后,会创建快照,后面再执行相同的select查询语句时,查询的其实是前面生成的快照。这就是为什么会有可重复读。

哪些读操作是快照读?哪些是当前读?

  • 快照读:简单的select操作,属于快照读,不加锁
    select * from table where ?
  • 当前读:特殊的读操作,插入/更新/删除操作,属于当前读,需要加锁。
    select * from table where ? lock in share mode;   
    select * from table where ? for update;
    insert into table values;
    update table set ? where ?;
    delete from table where ?;
    

所有以上的语句,都属于当前读。当前读读取的记录都是最新版本,并且读取之后还需要保证其他并发事务不能修改当前记录,对读取记录加锁。除了第一条语句外,对读取记录加S锁(共享锁)外,其他操作,加的都是X锁(排他锁)。

sql语句执行过程
以update为例。当update sql发给mysql之后,mysql会根据where条件读取第一天满足条件的记录,然后InnoDB引擎会将第一条记录返回,并加锁(当前读)。待mysql收到这条加锁的记录之后,会再发起一个update请求,更新这条记录。
一条记录完成之后,在读取下一条,直至没有满足条件的记录为止。delete的操作也一样。insert会有稍许不同,简单来说,就是insert操作会触发unique key的冲突检查,也会进行一个当前读。

InnoDB与mysql的交互,是一条条进行的,因此加锁也是一条条进行的。

回到刚才的问题。当事务B执行N-1更新操作的时候,会触发当前读,读取事务A提交后的数据,也就是事务A的N-1,再次基础上执行-1操作,最终变成N-2。

并发更新
上面的问题解决了事务A已经提交的情况。但如果事务A更新产品数量没有提交呢?此时事务B执行当前读拿到的也是N。
了解数据库锁机制。
事务A提交前,会一直持有排它锁(具体行锁还是表锁,要看查询有没有走索引),此时事务B是会阻塞的。也就是说只有事务A提交或者回滚之后,事务B才能获得排它锁,从而进行更新操作

4.可串行化(SERIALIZABLE)

SERIALIZABLE是最高的隔离级别。它通过强制事务串行执行,避免了幻读的问题,简单的来说SERIALIZABLE会在读取的每一行数据都加锁,所以可能导致大量的锁超时和锁争用的问题。
MVCC并发控制退化为基于锁的并发控制,不区别快照读与当前读,所有操作均为当前读。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值