【MySQL】如何理解MySQL的锁(图文并茂,一网打尽)

一、锁的介绍

     锁是计算机协调多个进程或者线程并发访问某一资源的机制。那么如何保证数据并发访问的一致性、有效性是数据库必须解决的一个问题,锁的冲突也是影响数据库并发访问性能的一个重要因素,所以数据库中锁的应用极为重要,其复杂度也更高。

锁的分类,以锁的颗粒度为三类:

全局锁:锁定数据库中的所有表。

表级锁:每次操作锁住整张表。

行级锁:每次操作都锁到对应行的数据。

接下来就分别对这几种锁做一个解释。

二、全局锁

1、概念

对整个数据库实例加锁,加锁后整个实例就处于只读状态,对于后面的DML语句,DDL语句,已经更新操作的事务提交语句都会被阻塞。

典型的场景应用:数据备份。对所有的表进行锁定,从而获取数据的一致性,保证数据的完整性。

6c735dd4683e406ca1f40eeb45527dda.png

如图所示,我们加了全局锁之后,使用mysqldump取备份数据的时候,其他客户端的事务是不能执行DML操作、DDL操作的,但可以执行DQL操作,当我们备份好数据后,导出xxx.sql文件后,再解开锁,此时DML操作、DDL操作才生效。

(其中DML操作是指:对数据进行增加、删除、修改操作。DDL操作是指:主要是进行定义/改变表的结构、数据类型、表之间的链接等操作。DQL操作是指:对数据进行查询操作。还有DCL操作是指:主要是用来设置/更改数据库用户权限。)

2、语句

加锁:flush tables with read lock;

解锁:unlock table

3、不足

①:如果在主库上备份,那么备份期间都不能执行更新,业务基本上就停摆了 。

②:如果在从库上备份,那么在备份期间不能执行同步过来的二进制日志洗,会导致延迟。

二、表级锁

对于表级锁,锁定粒度答,发生锁冲突的概率 最高,并发度低。应用在MyISAM、InnoDB、BDB等存储引擎中。

表级锁分为表锁元数据锁意向锁三类。

1、表锁

(1)表共享读锁(read lock)

01966238a8c14762be6c719370d9f554.png

如图所示有两个客户端Client1、Client2当Client1执行加锁操作后,Client1、Client2都只能执行DQL操作不能执行DDL/DML操作(进行堵塞)。只有当Client1解锁后,才能恢复DDL/DML的操作。

(2)表独占写锁(write lock)

666debd437934a0b93bf08291e3fb9ff.png

如图所示有两个客户端Client1、Client2当Client1执行加锁操作后,Client1对该表能读能写,Client2不能执行DQL操作和不能执行DDL/DML操作(进行堵塞)。只有当Client1解锁后,才能恢复Client2的DQL/DDL/DML的操作。

(3)语法

   加锁:lock tables 表名 read/write。

   释放锁:unlock tables / 客户端断开。

2、元数据锁(MDL)

 元数据锁:加锁过程是系统自动控制的,无需显示使用,主要作用是为了维护表元数据的一致性。 在表中若有未提交的事务,不可以对元数据进行写入操作。 对表进行增删查改的时候加入MDL读锁(共享), 对表结构进行变更操作的时候加入MDL写锁(排他)。

MDL的读锁分为类型分为shared_read(执行select、select xxx lock in share mode 触发)、shared_write(执行insert、update、delete、select...for update 触发)。

①:读锁之间是兼容的

35c0e4488e654a3fafcf0180d62f7952.png

如图所示,两个客户端Client1、Client2,当这两个客户端执行CRUD操作的时候,系统就自动添加了MDL的读锁,而读锁之间是不排斥的,所以Client1与Client2对User表的操作不会造成堵塞。

②:读锁与写锁排斥、写锁与写锁排斥

33589e7d7a0d4e92a706df352e803ac9.png如图所示,两个客户端Client1、Client2,假设Client1执行了DQL操作,那么此时系统产生了MDL读锁,如果这时我们的Client2执行DDL操作,就会被堵塞,因为DDL操作产生的是MDL的写锁,会与MDL的写锁排斥。此时只有Client1commit提交后,Client2的语句才会生效。

3、意向锁

为了避免DML在执行时,加的行锁与表锁的冲突,在InnoDB中引入了意向锁,使得表锁不用检查 每行数据是否加锁,使得意向锁来减少表锁的检查。

83e37b51ffc543f5b7ae63888b563be8.png如图所示,当我们Client1执行一个update语句的时候,我们的mysql 会默认对更新的行加上行锁,

若此时没有意向锁,且Client2想对整张表进行锁表操作,那么Client2就会从第一条记录开始一条条查找对应行有没有行锁,找到行锁后在判断该行锁类型与添加的表锁是否冲突,再决定是否加锁,现实这个过程效率很低。

ea99d156fcf84165be200078cd385fa4.png假如我们加了意向锁,如图所示,此时Client1执行上诉同样的操作,而Client2在执行使先 判断当前的意向锁 与表锁是否兼容:若兼容直接加锁,若不兼容则Client2的操作会阻塞,直到Client1进行了commit操作,且释放意向锁和行锁后,才生效。此时不用逐行检查行锁的情况,效率增加。

意向锁分为两类意向共享锁(IS)和意向排他锁(IX)两类,接下来做以下介绍。

(1)意向共享锁(IS)

意向共享锁,与表锁的表共享读锁(read)兼容,与表独占写锁(write)排斥。

语句:select ... (select语句)lock in share mode(触发)

699c266066d04de9bac0a17b271ead45.png

如图所示,Client1先通过select ... (select语句)lock in share mode触发意向共享锁(IS),此时Client2执行锁表的read lock操作可以直接锁表,因为二者兼容,当 执行锁表的wirte lock此操作,则会被堵塞因为二者不兼容。

(2)意向排他锁(IX)

意向排他锁,与表共享读锁(write)、表独占写锁(read)都互斥。意向锁之间不会互斥。

语句:insert 、update、delete、select ... for update(触发)

12a44507c9354ce08f9669a1c980187f.png

如图所示,Client1通过insert 、update、delete、select ... for update语句 触发意向排它锁(IX),此时Client2执行表共享读锁(write)、表独占写锁(read)都会被互斥,因为不兼容,只有等Client1执行完毕提交后,Client2的指令才会触发。

三、行级锁

   行级锁:是MySQL 中锁定粒度最小的一种锁,是针对索引字段加的锁,只针对当前操作的行记录进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。应用在Innodb储存引擎中。

行级锁分为行锁(Recode Lock)、间隙锁 (Gap lock)、临键锁 (Next-key Lock:行锁与间隙锁的组合)三类,接下来分别介绍。

1、行锁

行锁分为共享锁排他锁两类。

(1)共享锁(s)

只允许一个事务去读一行,阻止其他事务获得相同的数据集的排它锁。(共享锁之间兼容,与排它锁之间排斥)

语句:select ... (select语句)lock in share mode 触发(select 不加锁)

(2)排他锁(x)

允许获取排它锁的事务更新数据,阻止其他事务获得相同数据集的共享锁和排他锁(排它锁与排它锁、共享锁之间都冲突)(阻止其他事务的读锁和写锁 )

语句:insert 、update、delete、select ... for update 触发

2、间隙锁

间隙锁唯一的目的就是防止其他事务插入间隙。造成幻读现象。

61a035ae987745638a8c58408e1fa402.png

如图所示,Client1执行了一个update Student set age = 10 where id=5操作,此时因为表中没有id=5的字段,此时就会在id=7和id=3之间加一个间隙锁(不包含id=3和id=7的情况),此时,Client2又执行了一个insert Student values(6,'李蛋',6)指令,在id=3和id=7之间插入一条指令,因为加了间隙锁,所以插入操作会被阻塞,直到Client1完成commit提交后,Client2插入语句才会生效。

3、临键锁

临键锁,是记录锁与间隙锁的组合,它的封锁范围,既包含索引记录,又包含索引区间。默认情况下InnoDB在Repeatable Read事务隔离级别运行,InnoDB使用临键锁进行搜索和索引扫 描,以防止幻读。(临键锁与间隙锁相比,除了锁住当前记录也会锁定当前记录之前的一部分间隙)

四、再谈乐观锁与悲观锁

我们从mysql模式上分类来谈谈乐观锁与悲观锁这两种思想。

1、乐观锁

(1)概念

乐观锁是一种在数据库操作中用于处理并发问题的技术。它的思想是认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返 回错误信息,让用户决定如何去做。

(2)实现

     利用数据版本号(version)机制是乐观锁最常用的一种实现方式。一般通过为数据库表增加一个数字类型的 “version” 字段,当读取数据时,将version字段的值一同读出, 数据每更新一次,对此version值+1。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第 一次取出来的version值相等,则予以更新,否则认为是过期数据,返回更新失败。

(3)应用场景

①:低冲突环境。②:读多写少的场景。③ :短事务场景。④:互联网应用。⑤:分布式应用。

(4)缺点

①:冲突检测:在高并发的环境中,乐观锁可能会导致大量的冲突 。

②:处理开销:在处理冲突时,需要执行回滚和重试,这可能会增加系统的开销。

③:版本管理:乐观锁通常通过版本号(或时间戳)来检测冲突。这要求系统能正确的控制版本号,否则可能导致错误的版本冲突。

2、悲观锁

(1)概念

悲观锁是指数据在操作数据的时候比较悲观,每次去拿数据的时候认为别的线程也会同时修改数据,所以在每次拿数据的时候都会加锁,这样别的线程想要拿数据就会被阻塞直到它解开锁。

(2)实现

①:使用select...for update是MySQL提供的实现悲观锁的方式。

②:Java中使用synchronized和ReentrantLock等独占锁方式实现。

(3)应用场景

①:写操作比较多的场景。②:并发冲突高的场景。③:业务需要强一致性的的场景。

(4)缺点

①:需要实施阻塞,导致效率降低下。

②:可能造成某个线程永久等待,即发生死锁的可能性较大。

③:锁超时:如果一个事务长时间有锁而不被释放,可能导致其他等待锁的事务超时。

五、总结

  这里博主整理我们常见的MySQL中锁的概念,还介绍了面试中 常问的关于乐观锁、悲观锁的 一些特点。相信一定对屏幕前正在阅读的小伙伴有所帮助,大家不要忘记点赞、关注,支持博主一波哦!后续还有更多内容与大家分享~

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值