数据库17mysql锁机制

锁是计算机协调多个进程或线程并发访问某一资源的机制。锁保证数据并发访问的一致性、有效性;锁冲突也是影响数据库并发访问性能的一个重要因素。锁是Mysql在服务器层和存储引擎层的的并发控制

InnoDB锁的类型(表级锁,行级锁,页面锁)

从锁定资源的角度看, MySQL 中的锁分类:表级锁,行级锁,页面锁

表级锁

特点是每次都整张表加锁,加锁速度快,但是锁的粒度太大,并发性就低,发生锁冲突的概率大。
表锁的种类主要包含两种:
读锁 (共享锁):同一份数据多个读操作同时进行不会互相影响,但是读操作会阻塞写操作。
写锁(排他锁):当前写操作没有完成之前会阻塞其他读和写操作。

行级锁

特点是对一行数据加锁,加锁的开销会大,但是锁粒度小,发生锁冲突的概率就低并发度提高了。
行锁的种类包含:
读锁(S 共享锁):允许一个事务读取某一行,其他事务在读取期间无法修改该行数据但可以读。
写锁(X 排他锁):允许当前获得排它锁的事务操作数据,其他事务在操作期间无法更改或者读取。
意向排它锁(IX):一个事务给该数据行加排它锁之前,必须先获得 IX 锁。
意向共享锁(IS):一个事务给该数据行加共享锁之前必须先获得 IS 锁。

页面锁

因为MySQL 数据文件存储是按照页去划分的,所以这个锁是 MySQL 特有的。开销和加锁时间界于表锁和行锁之间,锁定粒度界于表锁和行锁之间,并发度一般。
在 InnoDB 引擎中默认使用行级锁,我们重点就行级锁的加锁、解锁来做一些说明。

行级锁上锁分为 隐式上锁 和 显式上锁

隐式上锁是默认的上锁方式,select不会自动上锁,insert、update、delete 都会自动加排它锁。在语句执行完毕会释放。
显式上锁即通过手动的方式给 sql 语句加锁,比如:
共享锁:select * from tableName lock in share mode;
排他锁:select * from tableName for update;

行级锁的实现方式

在 InnoDB 中行级锁的具体实现分为三种类型:
锁定单个行记录:Record Lock。
锁定一个范围,不包含记录本身:Gap Lock。
同时锁住一行数据 + 该数据上下浮动的间隙 :next-Key Lock。
注意:InnoDB默认级别是repeatable-read级别,所以下面说的都是在RR级别中的。

之前一直搞不懂Gap Lock和Next-key Lock的区别,直到在网上看到一句话豁然开朗,希望对各位有帮助。
Next-Key Lock是行锁与间隙锁的组合,这样,当InnoDB扫描索引记录的时候,会首先对选中的索引记录加上行锁(Record Lock),再对索引记录两边的间隙加上间隙锁(Gap Lock)。如果一个间隙被事务T1加了锁,其它事务是不能在这个间隙插入记录的。

锁兼容关系和理解

首先把四个名字的含义理解了:
X理解为独占一个表(exclusive)
S理解为共享一个表(整个表共享读)
IX理解为有意向写这张表的某一行(有意向独占某一行、先占一个坑)
IS理解为有意向读这张表的某一行

一开始学这个知识,很多小伙伴傻傻分不清兼容关系
不过找到技巧还是很容易掌握的!
1.X是独占整个表,当表中已经存在了X、S、IX、IS任意一种锁,X锁一定加不上了,因为无法满足独占。同理,表中已经存在X,其他锁都加不上了(必须独占)
2.IX和S锁不兼容,IX是独占某一行,既然IX占领了某一行,那么就不能S整个表共享读了
3.IX和IS兼容,某一行有意向独占和另一行有意向共享读是可以成立的
4.IX和IX之间兼容,可以存在IX想要独占row_a,另一个IX想要独占row_b,只要a≠b即可成立
5.整个兼容表其实是对称的 (行列顺序都按照 X、S、IX、IS)

意向锁是InnoDB自动加的,不需要用户干预。
对于insert、update、delete,InnoDB会自动给涉及的数据加排他锁(X);对于一般的Select语句,InnoDB不会加任何锁,事务可以通过以下语句给显示加共享锁或排他锁。

1
2
共享锁:select * from table_name where .....lock in share mode  
排他锁:select * from table_name where .....for update

间隙锁目的

会封锁区间,以阻止其他事务id=10的记录插入。
画外音:
为什么要阻止id=10的记录插入?
如果能够插入成功,头一个事务执行相同的SQL语句,会发现结果集多出了一条记录,即幻影数据。
间隙锁的主要目的,就是为了防止其他事务在间隔中插入数据,以导致“不可重复读”。
如果把事务的隔离级别降级为读提交(Read Committed, RC),间隙锁则会自动失效。

何时在InnoDB中使用表锁

InnoDB在绝大部分情况会使用行级锁,因为事务和行锁往往是我们选择InnoDB的原因,但是有些情况我们也考虑使用表级锁。
1、当事务需要更新大部分数据时,表又比较大,如果使用默认的行锁,不仅效率低,而且还容易造成其他事务长时间等待和锁冲突。
2、事务比较复杂,很可能引起死锁导致回滚。

死锁和避免

我们说过MyISAM中是不会产生死锁的,因为MyISAM总是一次性获得所需的全部锁,要么全部满足,要么全部等待。而在InnoDB中,锁是逐步获得的,就造成了死锁的可能
发生死锁后,InnoDB一般都可以检测到,并使一个事务释放锁回退,另一个获取锁完成事务。

避免死锁
有多种方法可以避免死锁,这里只介绍常见的三种:
1、如果不同程序会并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低死锁机会
2、在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率;
3、对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率

参考

InnoDB锁定模式和兼容性理解:https://blog.csdn.net/weixin_39666736/article/details/105242584
mysql锁机制之共享锁,排它锁:https://blog.csdn.net/sugartang23/article/details/79214710
MySql - innodb不可重复读下的一些锁:https://blog.csdn.net/b9x__/article/details/83994129
mysql的mvcc(多版本并发控制)https://my.oschina.net/u/4409676/blog/4109538
浅谈Innodb中的幻读以及对行级锁的实现:https://blog.csdn.net/weixin_44339331/article/details/108183619#t5

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×