Mysql—Innodb锁介绍

一、锁简介

锁这个词是比较常见的,生活中我们使用锁来保证一个房间或者一个资源的安全,因为开锁需要钥匙,而钥匙保存在我们手里,其他人是无法正常获取到的。程序中,当我们的程序需要多线程去访问操作共享资源时,为了保证一致性,我们需要使用锁机制来防止并发原因出现的问题,同样,数据库会使用这种锁机制来保证资源的共享安全性,比如当两个事务都需要更改同一条记录时,就需要锁机制来保证一致安全性。

下面根据加锁的范围来看下innodb支持的锁类别:

1.记录锁(行锁)

行级锁是特定引擎所支持的,比如Innodb是支持行锁的,这也是它的一个特性,可以在数据记录级别上锁,粒度是比较细的,记录锁在加锁类别可以分成两种锁:

共享锁(s锁):事务可以读取数据,如果数据加了记录共享锁,可以继续加共享锁;

排他锁(x锁):事务可以更改数据,如果事务加了记录排他锁,则不可以在家共享锁和排他锁;

两种锁的排斥关系如下:

共享锁(s) 排他锁(x)

共享锁(s)

兼容 不兼容

排他锁(x)

不兼容 不兼容

2.范围锁

范围锁是指加在区间上的锁,其锁定的是一个空间,比如锁定范围为(-∞,1】或(10,30)这种方式,Innodb中这种锁叫做gap锁。

间隙锁(gap lock):工作在事务隔离级别中的RR级别,在索引之间加入锁,它使得RR级别避免了幻读;

next key lock:这种锁描述的在Innodb中会出现行锁和gap lock结合使用的场景,把这种加锁方式叫做next key;

3.粗粒度锁

Innodb还支持粗粒度的锁,可以建立在表以及段、区、页上的。其中意向锁是表级别的锁,当想要在数据记录上加x锁时,需要先在表加上IX锁。意向锁之间是兼容的,当我们给表加表锁的时候,如果没有意向锁的话,需要遍历每一条数据是否有排他锁,如果没有才能加表锁,但是有了意向锁,直接判断表上是否有意向排他锁就可以了。意向锁是Innodb引擎自我管理的,无需DBA去操作或者管理配置,是一种自我属性。类别上也是分为两种:

意向共享锁(IS):在数据记录加排他锁之前,需要在上级也就是表上加意向排他锁;

意向排他锁(IX):在数据记录加共享锁之前,需要在上级也就是表上加意向共享锁;

意向锁和记录锁之间的排斥关系如下:

锁类别 共享锁 排他锁 意向共享锁 意向排他锁
共享锁 兼容 不兼容 兼容 不兼容
排他锁 不兼容 不兼容 不兼容 不兼容
意向共享锁 兼容 不兼容 兼容 兼容
意向排他锁 不兼容 不兼容 兼容 兼容

                                               注:IS或者IX锁此时是与表级的X锁或S锁比较排斥关系

二、加锁情况

在Innodb引擎中,使用多版本并发控制来处理并发的问题,也就是MVCC(Multi-version Concurrency Control),这个控制方法不仅仅使用在Innodb中,其实已经很多数据库,以及其他很多的引擎也支持使用了这中控制协议,其最大的有点是读不加锁,读写不冲突,因此减少了锁的开销,提高了读写性能,可以理解他是行级锁的一个变种。

MVCC工作在Innodb的RC和RR事务隔离级别下,因为RU每次都读取最新的行,Seriallizable是单线程串行,无需加锁操作。

在Innodb的MVCC中读操作分为两种:快照读和当前读,快照读读取的数据可见版本或者历史版本,无需加锁操作,当前读需要读取数据的最新数据行,并且这些返回的数据行都会加锁,防止其他事物更改,两种读操作包含的sql语句结构:

快照读:select * from table where ***;简单的读取操作;

当前读:

select * from table where ? lock in share mode;//加S锁,其他事物可以读取;

select * from table where ? for update;

insert into table values(...);

update table set ...where ?;

delete from table where ?;

除了第一条加的是S锁,其他语句都是读取数据的最新行,并且需要加X锁,以防止其他事物更改或者读取。

通过一条update语句,分析sql执行的简单过程:

1.首先客户端收到一条sql,客户端将这个sql发到server层,server经过权限验证、查询缓存(开启)、sql优化、指定执行计划等步骤之后,会把该sql发送到引擎层;

2.引擎层接收到该sql后,会查找符合该条件的记录,先去内存查找,如果没有,则到磁盘读取放入内存,找到符合条件的记录返回到server层执行器,并给该记录执行加锁操作;

3.执行器根据该返回记录,生成新的行,调用引擎的写入接口,将该记录写入;

4.引擎层接收到该更新插入请求,更新内存,添加日志,将更新成功结果返回到server层,一条记录更新完成;

5.引擎继续返回满足条件的行,直到所有满足条件更新完毕,就会告诉执行器所有记录更新完成,可以提交;

6.server层发起提交请求,引擎层执行提交操作,完成;

可以看出更新的过程是有一个当前读加锁操作,插入记录如果需要检查unique key冲突,也需要当前读的验证;

三、锁情况分析

锁的定义了解之后,我们需要知道我们应用中sql执行的内部流程包括加锁条件 ,这样才能有方向的进行优化,提升速度。假如我们分析一条语句的加锁条件,例如:delete from T1 where id = 10;执行的过程受很多因素参数影响,比如当前引擎的事务隔离级别、id是否有索引,如果有索引,索引的类别是什么?是主键?是唯一索引?下面列出几种情况,讲述锁的使用方式,可以通过阅读文章《Mysql—Innodb引擎 索引》了解innodb索引信息:

假设有表T1,有字段id,name:

RC级别下:

1.RC级别、id主键

id是主键,直接在主键上加x锁就可以

2.RC级别、id辅助唯一索引

id为唯一辅助索引,则需要在辅助索引x锁,在通过主键x锁,便可确定

3.RC级别、id辅助非唯一索引

将符合条件的非唯一辅助索引上加x锁、通过辅助索引找到对应的主键并加x锁

4.RC级别、id无索引

id列无索引,则通过全表扫描找到对应的数据记录,这个过程会使得全部记录加x锁。因为全部加x锁会严重影响性能,后期优化后,在寻找过程中,不符合记录的记录会去掉x锁

RR级别下:

1.RR级别、id主键

和RC级别下,id主键的情况相同,都是在主键上加x锁;

2.RR级别、id辅助唯一索引

和RC级别下,id辅助唯一索引的情况相同,都是在对应辅助索引和主键上加x锁;

3.RR级别、id辅助非唯一索引

此种情况下会加入gap锁,加上记录锁,也就是在辅助索引上加next key lock,在对应主键x加锁

4.RR级别、id无索引

全变扫描全部x锁加gap锁,优化后,不符合记录的记录去掉x锁,也不会加gap锁;

四、资源地址

官网:https://www.mysql.com

文档:《Mysql技术内幕-innodb存储引擎》《高性能Mysql》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章