一文了解Innodb中的锁

对于大部分的后端开发来说,数据库尤其是MySQL是一个离不开的知识点,那么今天就分享一下最近学习的数据库中的锁相关知识,并以此解释事务隔离性问题。
如下是整理的Mysql中锁的相关知识点

什么是锁

锁是数据库系统区别于文件系统的一个关键性区别。锁机制用于管理数据库共享资源的并发访问。也是实现事务的关键。

锁的级别

在MySQL中根据锁的粒度分为全局锁,表锁和行锁,锁的粒度从大到小。

  1. 全局锁:锁住的是数据库实例,被锁住时只读不可写。需要显示的调用锁库语句:Flush table with lock。 使用场景一般是全库备份。

  2. 表锁 :分为表锁和元数据锁(MDL)

    表锁类似全局锁,只是锁住对象时表,分读锁和写锁。
    元数据锁(MDL),不需要显示的调用,当对一个表进行增删改查时会加MDL读锁 ,当对表结构进行修改时(如加减字段)那么此时会对表加上写锁。这里的读锁之间是不会互相排斥的,但是写锁是互斥的

  3. 行锁:对表中的具体记录进行加锁,是项目中最常见的一种锁。行锁是对索引加锁。之前说过,每一张表都是有索引的,如果有主键,主键会键索引,如果没有主键,Innodb会为每行记录加一个默认列row_index 在这个列上面会建立索引。

锁的分类

InnoDB实现了如下两种标准的行级锁:

  • 共享锁 (S Lock),允许事务读取一行数据。
  • 排他锁(X Lock),允许事务删除或者更新一行数据。

如下图显示了共享锁和排他锁的兼容性

只有同时是S 共享锁时才能在不同的事务中进行读取。

锁的算法

在对锁有了简单的了解后,下面就来说说锁的算法。

  1. Record Lock: 行记录锁,属于X 排他锁。
  2. Gap Lock:间隙锁,也叫范围锁,对索引之间的范围加锁。
  3. Next Key Lock:行记录锁+间隙锁的组合。

大家可能对前面两个比较好理解,对于第三个Next Key Lock会略感陌生,那么下面就是对该算法的解释。

假设表上面id列有索引4,5,7,10)这四个值,那么加锁的情况就是(-无穷大,4],(4,5],(5,7],(7,10],(10,+无穷大),如果id不是聚簇索引或者唯一索引,那么执行select * from t where id=5 for update ,加锁的情况就是 (4,5)和(5,7)那么 insert into t select 6 ;会被锁住无法插入,但是insert into t select 8,或者insert into t select 2都是可以插入。

在可重复读的隔离级别下默认使用的是Next Key Lock,通过这个锁算法可以解决幻读问题。使用读提交的隔离级别会关闭Gap Lock和Next Key Lock

锁带来的问题

正是因为有了锁我们可以对共享资源进行并发控制,但带来便利的同时,也带来了其他问题。

两阶段协议

在继续往下讲之前,我们需要了解什么是锁的两阶段协议

1 . 锁在需要的时候才会加上去;
2. 事务结束才会释放锁。

怎么理解呢?
通俗的讲就是,事务开启时不会立马加锁,只有在执行需要加锁的sql时才会加上锁比如insert ,delete等;但是锁的释放是等事务提交时才会释放,而不是执行完sql就释放锁。也正是由于这个原因才出现了阻塞和死锁的现象。

阻塞

由于不同锁之间的兼容问题,在有些时刻,一个事务中的锁需要等待另一个事务中的锁的释放它锁占用的资源,这个就是阻塞
在Innodb中,有一个参数innodb_lock_wait_timeout 是用来控制等待时间的,默认是50秒,当一个事务等待超时时会出现1205异常,在默认情况下Innodb不会回滚超时引发的错误异常(在Innodb中大部分异常不会有回滚操作除了死锁检测)。如果想回滚可以业务自己手动调用,或者将参数innodb_rollback_on_timeout设置为on,默认是off。

死锁

和java中死锁的概念差不多,具体是指:两个或者两个以上的事务在执行过程中,因为争夺锁资源而造成的一种互相等待的现象。

死锁原因

在上面有提到两阶段的协议,也正是其中提到的事务提交时才会释放锁,那么就会在事务结束时出现AB和BA的互相等待锁的情况。这个就是导致死锁的原因。

如何解决

对于死锁问题的解决一般有三种方向

  1. 数据库默认的一个参数是innodb_lock_wait_timeout ,这个是事务等待超时,也是用来做锁等待超时的
  2. 打开死锁检测,innodb_deadlock_detect=ON
  3. 在业务端控制并发,比如对热点的数据做缓存或者使用队列进行数据库操作

解释一下什么死锁检测

为了能够主动进行死锁检测,数据库中保存了2种信息 “锁的链表信息”和“事务等待链表”,通过这两个链表可以构造出一张以事务为节点的图(wait-for graph),图中事务节点可以互相指向,当T1等待T2锁占用的资源时,会有一个T1指向T2的定义,当事务请求锁时就会检测这张图,如果出现循环指向,那么就会报1213异常,这时会将undo log量最少的事务进行回滚。

死锁的检测会导致机器的cpu利用率高,尤其是在并发度较高时,因此可以通过innodb_deadlock_detect开关来关闭死锁检测。
复制代码

总结

今天主要分析了MySQL中的锁的相关知识,只有认识了锁那么在解决数据库方面的问题时才会更加得心应手。大家如果有问题可以一起讨论。

未完待续!!!!

转载于:https://juejin.im/post/5d073830f265da1b8608875f

猜你喜欢

转载自blog.csdn.net/weixin_33701564/article/details/93166435