登录
首页 >  数据库 >  MySQL

面试必问的数据库中的锁

来源:SegmentFault

时间:2023-01-14 17:41:43 234浏览 收藏

亲爱的编程学习爱好者,如果你点开了这篇文章,说明你对《面试必问的数据库中的锁》很感兴趣。本篇文章就来给大家详细解析一下,主要介绍一下MySQL,希望所有认真读完的童鞋们,都有实质性的提高。

IT技术人员面对面试、跳槽、升职等问题,如何快速成长,获得大厂入门资格和升职加薪的筹码?与大厂技术大牛面对面交流,解答你的疑惑。《从职场小白到技术总监成长之路:我的职场焦虑与救赎》活动链接:码客

行锁

前言部分,我们引出了锁。读写和写写之间可能都需要加锁,但是读读之间是不需要的。 有没有什么想法,我们前几天才看了 ReentrantReadWriteLock 的实现,数据库其实跟它差不了太多。

在数据库中,读操作需要获取共享锁(Shared Locks),一般简称为S锁;写操作需要获取独占锁(Exclusive Locks),一般简称为X锁。

而且锁之间的互斥关系跟 ReentrantReadWriteLock 也可以说基本一致,只是这里不存在重入次数,将线程的概念换成了事务。

  • 读取记录时要获取记录的S锁,修改记录时要获取记录的X锁。
  • S锁可以由多个事务共同持有,X锁只能单个事务持有.
  • 事务1持有一条记录的S锁,此时事务2想要持有该记录的X锁的话会被阻塞,直到事务1放弃S锁
  • 事务1持有一条记录的S锁时,此时别的事务不管是S锁还是X锁都不可以获得
兼容性 X S
X 不兼容 不兼容
S 不兼容 兼容

锁定读

上边介绍完了锁,我们其实可以发现如果按上边所说,仍然不能解决银行存钱场景中的,事务1获取S锁读取了数据,此时事务2仍然可以获取事务的S锁继续读取,那么,我们就在读取记录时获取该记录的X锁。上述两种不同的读取加锁方式的实现分别如下

  SELECT ... LOCK IN SHARE MODE;

这是读取时获取S锁

   SELECT ... FOR UPDATE

这样读取后会为读取到的记录加X锁

写操作

DELETE

先获取指定记录的X锁,再进行删除标记(应该了解不会删除记录而只是打标记吧~

UPDATE

更新如果更新了记录的键值对需要先获取X锁删掉记录再进行Insert新纪录;没变键值对的话直接获取X锁进行原地修改

Insert

隐式锁,既不是S锁也不是X锁。

表锁

上边说了针对记录的行锁,同样还有表锁,其实相对于行锁,无非就是作用范围变大了,由单条记录变为了整个表,除此之外的兼容性规则完全一样。不过注意表锁和行锁间的互斥,比如一个事务获得整个表的S锁,别的事务既可以获得这个表的S锁,也可以获得表中某条记录的S锁,至于S锁,就别想了,我们说了,规则跟上边一样,不再赘述。

表锁种需要注意的只有一个意向锁,即如果我们想给表加X锁,此时表任意一条记录都能有S锁或者X锁,如果我们想给表加S锁,表中任意一条记录都不能有X锁。那么,问题来了,我们怎么直到表中任意一条记录有没有锁呢?

不可能遍历吧?当然不可能,实际上是当事务准备在某条记录加X锁时,就会在表上加一个IX锁;当事务准备在某条记录加S锁时,就会在表上加一个IS锁.这样当别的事务来,一看表有IX或者IS就知道这个表中的记录有没有X锁或者S锁了。IX和IS只是代表表中有记录持有X锁或者S锁,所以他们俩并不互斥,当然了IX和IX也不互斥。

说到表锁,不得不提下MyISAM、MEMORY这些引擎,他们都采用表锁,而且不支持事务,是基于会话的,这种大粒度的锁效率是非常慢的,所以这些存贮引擎一般只适用于只读的场景中

InnoDb中的行级锁

Record Locks

记录锁,其实就是S锁和X锁

Gap Locks

解决幻读就在于此。这个锁可以锁住某条记录和它前边记录之间的间隙,使得该间隙不能插入记录。

Next-Key Locks

不翻译了就,作用是锁住指定记录及其前边的间隙,即前两种锁的合体

Insert Intention Locks

插入意向锁,很无聊的一个锁。当事务被间隙锁阻塞不能成功插入时,会在该间隙生成一个插入意向锁,当可以插入时便获得插入意向锁

隐式锁

这个挺有意思的。试想这么一个场景,我们刚插入一个记录,此时别的事务就过来要这个记录的S锁进行读取或X锁进行修改,很明显这会造成脏读或者脏写问题。隐式锁由此而来。

对于聚簇索引记录,有一个隐藏列trx_id,记录着最后改动这条记录的事务id(MVCC才说过的),很自然的,新插入的记录的trx_id就是他自己的事务id,如果其他事务此时想获得该记录的S锁或者X锁时,会检查trx_id代表的事务是否是当前活跃事务,如果是的话,帮助该事务创建一个X锁(也就是为当前事务创建⼀个锁结构,is_waiting属性是false),然后自己进入等待状态(也就是为⾃⼰也创 建⼀个锁结构,is_waiting属性是true)。

对于二级索引,没有trx_id属性,但是在⼆级索引页面的Page Header部分有⼀ 个PAGE_MAX_TRX_ID属性,该属性代表对该⻚⾯做改动 的最大的事务id,如果PAGE_MAX_TRX_ID属性值⼩于 当前最小的活跃事务id,那么说明对该⻚⾯做修改的事 务都已经提交了,否则就需要在⻚⾯中定位到对应的二级 索引记录,然后回表找到它对应的聚簇索引记录,然后再重复情景聚簇索引的做法。

事务id相当于加了⼀个隐式锁。别的事务在对这条记录加 S锁或者X锁时,由于隐式锁的存在,会先帮助当前事务生成一 个锁结构,然后⾃⼰再⽣成⼀个锁结构后进⼊等待状态

InnoDb中的表锁

IS和IX以及S和X前边说过。

数据库中我们常用的一个主键自增,其实就是表锁的一个体现。 执行每条插入语句时都会在表上加一个AUTO-INC锁,然后为每条待插⼊记录的 AUTO_INCREMENT修饰的列分配递增的值,在该语句执 ⾏结束后,再把AUTO-INC锁释放掉。这样⼀个事务在持 有AUTO-INC锁的过程中,其他事务的插⼊语句都要被阻 塞,可以保证⼀个语句中分配的递增值是连续的。 该锁尤其适用于无法确定插入记录的条数的插入

轻量级锁

在为插⼊语句⽣ 成AUTO_INCREMENT修饰的列的值时获取⼀下这个轻量 级锁,然后⽣成本次插⼊语句需要⽤到的 AUTO_INCREMENT列的值之后,就把该轻量级锁释放 掉,并不需要等到整个插⼊语句执⾏完才释放锁。 即先通过轻量级锁对AUTO_INCREMENT修饰的列进⾏赋值。 该锁适用于确定要插入的记录条数

总结

其实很多东西都是相通的,尤其是我们前边说过类似共享锁和独占锁的概念,这里就很好理解~其他的,也没什么好说的,无非就是锁的作用范围和时机的一些问题,多看几遍自然就熟了~

(想自学习编程的小伙伴请搜索圈T社区,更多行业相关资讯更有行业相关免费视频教程。完全免费哦!)

到这里,我们也就讲完了《面试必问的数据库中的锁》的内容了。个人认为,基础知识的学习和巩固,是为了更好的将其运用到项目中,欢迎关注golang学习网公众号,带你了解更多关于mysql的知识点!

声明:本文转载于:SegmentFault 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>
评论列表