暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MySQL事务详解(三):脏写与幻读的绝路—锁

IT的世界 2024-06-08
29

在上次MySQL事务详解(二):隔离级别的实现--MVCC的学习中,我们认识到了并发事务访问相同的数据时, 在读—写
情况下带来的脏读、不可重复读
问题,可以通过MySQL的MVCC机制
解决。
而对于读—写
情况下的幻读
问题和写—写
情况下的脏写
问题,我们提到可以通过MySQL的
机制解决。

锁的概念

锁的作用于现实生活中的锁的作用类似,都是控制对资源的访问。
而MySQL的锁更侧重于对于资源的并发访问
,来保证访问数据的完整性与一致性

锁,其实就是与访问数据关联的一种数据结构
。如下图:

当访问(多为修改)一条数据时,首先查询
是否有与之关联的数据结构,没有就生成
一个,并占有
(即加锁成功),如果没有也生成一个并等待
(加锁失败)。
当占有锁的事务释放
锁之后,等待的事务就会被唤醒
执行。

锁的分类

锁的提出是为了解决并发访问带来的问题,但是如果一味的对访问的数据进行加锁,将访问串行化,使其排队等待执行,同时又会降低并发量,降低性能。
因此对锁进行了不同级别的分类,来应对不同的操作场景,如下图:

操作类型

读锁(S锁、共享锁)读取
数据用到的锁。给数据加S锁后,其他事务依然可以加S锁
(即共享),但是不可以加X锁。
写锁(X锁、排他锁)更改
数据用到的锁。给数据加X锁后,其他事务不可以加S锁和X锁
(即排他)。

态度

乐观锁假设
事务之间很少发生冲突,不主动
对数据进行加锁
。通常使用版本号
时间戳
在事务提交时检测是否冲突。
悲观锁与乐观锁相反
,假设事务之间经常发生冲突,主动给数据加锁,获取事务所需资源。

粒度

全局锁:对整个数据库实例进行加锁。Flush tables with read lock
 ,常用于对整个库进行逻辑备份。全局锁对数据库影响较大,锁主库会导致不能更新,业务暂停,锁从库导致主从延迟。
表级锁:对整张表进行锁定,开销小,粒度大,发生冲突的概率高,并发低,可以避免死锁。
行级锁:对某一行记录进行锁定,开销大,粒度小,发生冲突概率低,并发高,不能避免死锁。
页级锁:对数据库的最小存储单元——页进行锁定,粒度介于表和行之间。应用在BDB存储引擎中,了解即可。

表级锁

表级别的S锁、X锁和元数据锁

# 上锁
lock table tableName read; # 读锁/共享锁
lock table tableName write;# 写锁/排他锁
#解锁
unlock tables; # 客户端断开的时候也会自动释放锁。

使用上面的语言可以为表加上S锁和X锁,以及解锁。

但是如果仅仅是对表进行select、insert、update、delete操作,存储引擎是不会默认为表加上表级锁

而实现这些操作与其他事务中的DDL语句冲突的是表级锁中的元数据锁(MDL)

元数据锁是系统自动控制
的,每一条DML、DDL语句执行时都会申请MDL。
DML申请元数据读锁,DDL申请元数据写锁
。且DML与DML之间并不相互阻塞,因此DML可以并发执行。
所以表级别的S锁、X锁很少用到。

表级别的意向锁

意向读锁(IS):当我们想向表中的记录加上S锁时,首先给表加上IS锁。
意向写锁(IX):当我们想向表中的记录加上X锁时,首先给表加上IX锁。
IS和IX作用是为了后续给表加上S锁和X锁做的准备工作
,避免加上表级别S锁和X锁时,需要遍历所有的数据来判断是否有记录被锁上。

表级别自增锁

自增锁主要是用来给表中声明为AUTO_INCREMENT
的字段,在插入记录是递增而用。

  • • 在我们不知道要插入的数据量时
    ,例如使用 insert...select;replace...select或者load data时,通常先给表加上一个自增锁,然后生成对应的值,插入完成之后,删除自增锁;

  • • 在我们知道具体的插入数据量时
    ,使用轻量级锁
    ,为插入语句生成所需的值后即可释放锁,提高性能。

行级锁

行级锁才是使用最频繁的锁,在开发中遇到的死锁问题一般都是行级锁引起的。
根据不同的情况, 行级锁被分为不同的类型,解决不同的问题,幻读就是其中之一。

行级S锁、X锁

为我们要访问的某一条记录加上读锁或者写锁。S锁与X锁的关系之前已经学习过。

间隙锁

用来解决幻读的锁
。因为幻读是在其他事务插入数据后产生,当前事务在第一次select时,数据还不存在。如果使用行级的X锁,找不到上锁的对象。因此提出间隙锁,如下图:

给no=7的数据加上gap锁后,表明在这条数据之前(no为(3,7)间)不能插入新的数据。以此来解决幻读。

临键锁

当我们想锁住访问数据本身以及这条数据之前的间隙时,使用临建锁。即行级SX锁与gap锁的结合
。如下图:

插入意向锁

当我们想插入一条数据时,首先要判断插入的位置是有存在gap锁
,如果存在就首先生成一个插入意向锁,向系统表明有事务在等待插入当前位置。

两种数据读

快照读

MVCC使用的就是快照读,在读取事务是好像拍了一张照片,当前事务读取的是照片快照下的数据。

普通的select 语句都是快照读,不会给记录有加锁操作。

当前读

读取到的数据为最新的的数据

select ..lock in share mode;
对记录加上S锁;
selelct ... for update;
对记录加上X锁;
以上两种select语句给记录加上了锁,并且读取到数据是最新的数据。



文章转载自IT的世界,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论