Innodb引擎里面的锁

Innodb引擎里面的锁

Lock和Latch

lock和latch是两种不一样的东西,在mysql里面都有使用,lock锁的是事务,latch锁的是线程间的共享数据。

锁的分类

锁最基础的分类就是共享锁和排它锁了

  • 共享锁:S锁,可理解为读锁
  • 排它锁:X锁,可理解为死锁

他们互相间的阻塞和读写锁是一样的。只有SS是不阻塞的。

表级锁

意向锁

意向锁是什么

意向锁表达的是,对这个表有锁定的意向,并没有实际的上锁,它只对全表扫描的操作有阻塞作用。
意向锁也分为IX-IS,也就是共享锁和排它锁

意向锁有什么用

意向锁的用途就在于全表扫描的时候。
假设有如下场景:

  • 我需要去全表扫描select * from t1
  • 但是我不知道表里面有没有S锁锁住了记录怎么办
  • 方案1:我一个个去查,有锁我就等等他
  • 方案2:我对一个行加行X锁的时候,顺手给表加了个IX锁,这样当要全表扫描的时候,人家就知道现在不能扫描了,乖乖阻塞住。

很明显方案2听起来很靠谱,方案1太鸡肋了,这也就是意向锁的用途。

自增锁

自增锁是什么

在给主键赋予了自增属性(假设为id)的时候,那对于这个id的获取,那肯定是不能乱来的。所以派生出了AUTOINC_LOCK

自增锁有什么用

在insert的时候会去获取这么一个表级的锁,别人等我插入完了你再插入咯。这样可以保证id正确的被获取

自增锁太慢了!我想快点

innodb里面提供了一个参数innodb_autoinc_lock_mode,参数值有:

  • 0:代表现在使用的是AUTOINC锁
  • 1:代表这是自适应的方式,如果是simple insert那就用快一点的方式,如果是bulk insert,一堆给我干过来,那就使用AUTOINC锁。
  • 2:代表现在使用的是一个“快一点的方式"。

什么是快一点的方式

innodb自己也觉得AUTOINC有时候太慢了,所以它提供了一个获取完id就可以释放锁了的操作,只对id这么一个共享变量进行一个互斥的读取。

出现问题了-binlog-主从复制

当然欲速则不达,这种方式就出问题了,当出现并发insert的时候,就可能会出现问题。
场景如下:

  • A事务下想对表t插入一条数据,它获取了id为2. insert into t values(2, "我是A")
  • B事务下想对表t插入一条数据,它获取了id为3. insert into t values(3, "我是B")
  • 但是但是但是,他们真实插入的时候,可能B比A更快。
  • 如果此时好巧不巧,我就使用了statement的binlog_format,那么在复制的时候,到从表就变成了(3, "我是A"),(2,"我是B")这么两条数据了。出现了主从不一致的可怕问题。

所以说这种方式有局限性-那么就是只能使用raw的binlog_format.

行级锁

Record Lock

记录锁,也就是正经的对某一行上锁

Gap Lock

Gap Lock是什么

间隙锁,它是加在一个范围上的锁。

Gap Lock有什么用

GapLock听得最多的就是防止幻读。
场景如下:

  • 假设现在是select * from t1 where id > 3
  • 那么现在另一个事务里插入了一条 insert into t1 values(4, "幻读来咯")
  • 我再次执行select * from t1 where id > 3,会发现我眼花了,多读出一条记录了。
  • 如果我现在给我此时的事务中加一个GapLock,那另一个事务里的插入就会被阻塞,那么幻读就会被拒绝。

GapLock出现在RR下,RC没有

那么这就也可以说明,RR下是会出现幻读的。
GapLock之间是不会被阻塞的,它的作用只是让别人别过来再写了

Next-Key Lock

Next-Key Lock是什么

他是GapLock+Record的组合,可以说是把当前记录和间隙都锁住了。

Next-Key Lock有什么用

它的作用就是支持不需要使用不可重复读和幻读的场景
当你查询的where的东西是一个唯一的值,那就会被降级成record lock
Next-Key Lock之间是会相互阻塞的。

多粒度锁

Innodb中是支持多粒度锁的,也就是说行锁和表锁同时存在,其实也就是意向锁干的,这里只是单独列出来说明。

MVCC-多版本并行控制

MVCC是什么

MVCC相关的就是一致性非锁定读的操作。

MVCC干了什么

它做的就是如果有写锁占住了一个record,这时候如果我想要去读它就要一直等,那么我现在想不让他等,就直接去读快照咯。

一致性非锁定读的原理-undo log

原理就是在于undo log,我们读取的时候,去读已经生成的之前版本的快照是肯定不用上锁的,毕竟已经没人会去再修改它了。

不同事务隔离级别的MVCC

RR

RR做的就是事务开始的时候的版本的快照。

RC

RC做的就是读最新的快照,那他就会出现幻读和不可重复读咯。

一致性非锁定读其实不保证数据一致性

它只是为了让读的时候更快的一种操作,如果你不想出现幻读和不可重复读,那就乖乖上锁,上锁以后就关乎于之前提到的行级锁啦。

  • SELECT ... FOR UPDATE-加X锁
  • SELECT ... LOCK IN SHARE MODE-加S锁

脏读、幻读、不可重复读

脏读-问题!

脏读就是读到了还没有提交的数据,这也叫脏数据。

解决办法

脏读的解决办法比较简单,将事务隔离级别提到RC就行了,脏读就是出现在Read UnCommited

幻读

幻读其实不是一个问题,而是一个现象。在一些数据库默认级别为RC的厂商眼里,他们都是允许这个的发送的。
幻读就是一个事务内,两次读出来的数量不同

解决办法

幻读只出现在RC下,提升到RR就可以了

不可重复读

不可重复读和幻读类似,它是出现在读的本条数据上,它被人修改掉了,两次读的数据的内容不一致。
解决办法同幻读。

死锁

既然上锁,那肯定会出现死锁,很正常,一是要正确操作避免死锁,二是要死锁发送破坏死锁。

为什么会死锁

当A事务已经给data1上锁,B事务已经给data2上锁,此时A事务想要操作data2,而B事务想要操作data1,完蛋咯,出现死锁

怎么破坏死锁

innodb下是使用了wait-for-graph对死锁的检查。他会去用dfs检查有无环存在(个人想到了spfa哈哈哈哈),如果有死锁就会选择回滚undo log最少的那个事务去破坏死锁。

posted @ 2022-10-27 22:05  azxx  阅读(31)  评论(0编辑  收藏  举报