InnoDB与MyISAM的最大不同有两点:一是支持事务(TRANSACTION);二是采用了行级锁。关于事务我们之前有专题介绍,这里就着重介绍下它的锁机制。

总的来说,InnoDB按照不同的分类共有七种类型的锁:

共享/排它锁(Shared and Exclusive Locks)

意向锁(Intention Locks)

间隙锁(Gap Locks)

记录锁(Record Locks)

临键锁(Next-key Locks)

插入意向锁(Insert Intention Locks)

自增锁(Auto-inc Locks)

共享/排它锁(Shared and Exclusive Locks)

按照兼容性来分类,InnoDB有共享锁和排它锁两种行级锁。共享锁(S):又称读锁。允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁。这保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。

排他锁(X):又称写锁。允许获取排他锁的事务更新数据,阻止其他事务取得相同的数据集共享读锁和排他写锁。若事务T对数据对象A加上X锁,事务T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T释放A上的锁。

对于共享锁大家可能很好理解,就是多个事务只能读数据不能改数据。 对于排他锁大家的理解可能就有些差别,我当初就犯了一个错误,以为排他锁锁住一行数据后,其他事务就不能读取和修改该行数据,其实不是这样的。排他锁指的是一个事务在一行数据加上排他锁后,其他事务不能再在其上加其他的锁,但是仍然可以进行普通无锁查询。mysql InnoDB引擎默认的修改数据语句:update,delete,insert都会自动给涉及到的数据加上排他锁,select语句默认不会加任何锁类型,如果加排他锁可以使用select …for update语句,加共享锁可以使用select … lock in share mode语句。所以加过排他锁的数据行在其他事务中是不能修改数据的,也不能通过for update和lock in share mode锁的方式查询数据,但可以直接通过select …from…查询数据,因为普通查询没有任何锁机制。

另外还需要注意的是:InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点MySQL与Oracle不同,后者是通过在数据块中对相应数据行加锁来实现的。InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!

意向锁(Intention Locks)

InnoDB为了支持多粒度锁机制(multiple granularity locking),即允许行级锁与表级锁共存,而引入了意向锁(intention locks)。意向锁是指,未来的某个时刻,事务可能要加共享/排它锁了,先提前声明一个意向。

意向锁是一个表级别的锁(table-level locking);

意向锁又分为:意向共享锁(intention shared lock, IS),它预示着,事务有意向对表中的某些行加共享S锁;

意向排它锁(intention exclusive lock, IX),它预示着,事务有意向对表中的某些行加排它X锁;

加锁的语法为:

select ... lock in share mode;  //要设置IS锁

select ... for update;      //要设置IX锁

意向锁和共享级排它锁的兼容性关系如下:

前面讨论的行锁按照兼容性分为共享/排它锁,但是从实现算法上来分的话,Innodb的三种行锁分别是:间隙锁、记录锁和临键锁,下面分别阐述。

间隙锁(Gap Locks)

当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据的索引项加锁;

对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁(Next-Key锁)。

举例来说,假如lock_example表中只有101条记录,其id的值分别是1,2,...,100,101,下面的SQL:

Select * from lock_example where id > 100 for update;

InnoDB 不仅会对符合条件的 id值为 101 的记录加锁;

也会对 id大于101(这些记录并不存在)的“间隙”加锁。

间隙锁的目的防止幻读,以满足相关隔离级别的要求

对于上例,若不使用间隙锁,如果其他事务插入 id大于 100 的任何记录,那么本事务如果再次执行上述语句,就会发生幻读。满足其恢复和复制的需要

在使用范围条件检索并锁定记录时,InnoDB 这种加锁机制会阻塞符合条件范围内键值的并发插入,这往往会造成严重的锁等待,因此,在实际开发中,尤其是并发插入较多的应用,我们要尽量优化业务逻辑,尽量使用相等条件来访问更新数据,避免使用范围条件。

记录锁(Record Locks)

顾名思义,记录锁就是为某行记录加锁,它封锁该行的索引记录:

-- id 列为主键列或唯一索引列

SELECT * FROM lock_example WHERE id = 1 FOR UPDATE;

id 为 1 的记录行会被锁住。

需要注意的是:id 列必须为唯一索引列或主键列,否则上述语句加的锁就会变成临键锁。

同时查询语句必须为精准匹配(=),不能为 >、

其他实现

在通过 主键索引与唯一索引对数据行进行UPDATE 操作时,也会对该行数据加记录锁:

-- id 列为主键列或唯一索引列

UPDATE lock_example SET age = 50 WHERE id = 1;

临键锁(Next-Key Locks)

Next-Key 可以理解为一种特殊的间隙锁,也可以理解为一种特殊的算法。通过临键锁可以解决幻读的问题。 每个数据行上的非唯一索引列上都会存在一把临键锁,当某个事务持有该数据行的临键锁时,会锁住一段左开右闭区间的数据。需要强调的一点是,InnoDB 中行级锁是基于索引实现的,临键锁只与非唯一索引列有关,在唯一索引列(包括主键列)上不存在临键锁,会降级为记录锁,即仅锁住索引本身,不是范围。

假设有如下表:

MySql,InnoDB,Repeatable-Read:lock_example(id PK, age KEY, name)

该表中 age 列潜在的临键锁有:

(-∞, 10],

(10, 24],

(24, 32],

(32, 45],

(45, +∞],

在事务 A 中执行如下命令:

-- 根据非唯一索引列 UPDATE 某条记录

UPDATE lock_example SET name = Vladimir WHERE age = 24;

-- 或根据非唯一索引列 锁住某条记录

SELECT * FROM lock_example WHERE age = 24 FOR UPDATE;

不管执行了上述 SQL 中的哪一句,之后如果在事务 B 中执行以下命令,则该命令会被阻塞:

INSERT INTO table VALUES(100, 16, 'Ezreal');

很明显,事务 A 在对 age 为 24 的列进行 UPDATE 操作的同时,也获取了 (10, 24] 这个区间内的临键锁。

不仅如此,在执行以下 SQL 时,也会陷入阻塞等待:

INSERT INTO table VALUES(100, 30, 'Tom');

那最终我们就可以得知,在根据非唯一索引 对记录行进行 UPDATE \ FOR UPDATE \ LOCK IN SHARE MODE 操作时,InnoDB 会获取该记录行的临键锁 ,并同时获取该记录行下一个区间的间隙锁。

即事务 A在执行了上述的 SQL 后,最终被锁住的记录区间为 (10, 32]。

插入意向锁(Insert Intention Locks)

对已有数据行的修改与删除,必须加强互斥锁(X锁),那么对于数据的插入,是否还需要加这么强的锁,来实施互斥呢?插入意向锁,孕育而生。

插入意向锁,是间隙锁(Gap Locks)的一种(所以,也是实施在索引上的),它是专门针对insert操作的。多个事务,在同一个索引,同一个范围区间插入记录时,如果插入的位置不冲突,不会阻塞彼此。先看下官方的解释:

Insert Intention Lock signals the intent to insert in such a way that multiple transactions inserting into the same index gap need not wait for each other if they are not inserting at the same position within the gap.

举个例子(表依然是如上的例子lock_example,数据依然是如上),事务A先执行,在10与24两条记录中插入了一行,还未提交:

insert into lock_example values(11,23, 'Jim');

事务B后执行,也在10与24两条记录中插入了一行:

insert into lock_example values(12,24, 'Bob');

因为是插入操作,虽然是插入同一个区间,但是插入的记录并不冲突,所以使用的是插入意向锁,此处A事务并不会阻塞B事务。

自增锁(Auto-inc Locks)

自增锁是一种特殊的表级别锁(table-level lock),专门针对事务插入AUTO_INCREMENT类型的列。最简单的情况,如果一个事务正在往表中插入记录,所有其他事务的插入必须等待,以便第一个事务插入的行,是连续的主键值。官方解释如下:

AUTO-INC lock is a special table-level lock taken by transactions inserting into tables with AUTO_INCREMENT columns. In the simplest case, if one transaction is inserting values into the table, any other transactions must wait to do their own inserts into that table, so that rows inserted by the first transaction receive consecutive primary key values.

举个例子(表依然是如上的例子lock_example),但是id为AUTO_INCREMENT,如果A事务执行如下语句:

insert into lock_example values(23, 'Jim');

B事务执行的语句如下:

insert into lock_example values(24, 'Bob');

此时事务B插入操作会阻塞,直到事务A提交。

总结

以上总结的7种锁,可以按两种方式来区分:

1. 按锁的兼容性来划分,可以分为共享、排他锁;

共享锁(S锁、IS锁),可以提高读读并发;

为了保证数据强一致,InnoDB使用强互斥锁(X锁、IX锁),保证同一行记录修改与删除的串行性;

2. 按锁的粒度来划分,可以分为:

表锁:意向锁(IS锁、IX锁)、自增锁;

行锁:记录锁、间隙锁、临键锁、插入意向锁;

其中,InnoDB的细粒度锁(即行锁),是实现在索引记录上的(如果未命中索引则会失效);

记录锁锁定索引记录;间隙锁锁定间隔,防止间隔中被其他事务插入;临键锁锁定索引记录+间隔,防止幻读;

InnoDB使用插入意向锁,可以提高插入并发;

间隙锁(gap lock)与临键锁(next-key lock)只在Repeatable-Read(RR可重复读)以上的级别生效,Read-Committed(RC已提交读)下会失效。

欢迎移步搜索关注公众号:互联网架构师之路(hlw_architector),获取最新架构材料。Mysql之Innodb锁机制详解​mp.weixin.qq.com

mysql innodb 的锁机制_Mysql之Innodb锁机制详解相关推荐

  1. mysql 主键 自增_MySQL自增主键详解

    一.自增值保存在哪儿? 不同的引擎对于自增值的保存策略不同 1.MyISAM引擎的自增值保存在数据文件中 2.InnoDB引擎的自增值,在MySQL5.7及之前的版本,自增值保存在内存里,并没有持久化 ...

  2. mysql 8 my.cnf 配置文件_mysql之my.cnf配置文件详解

    # 以下选项会被MySQL客户端应用读取. # 注意只有MySQL附带的客户端应用程序保证可以读取这段内容. # 如果你想你自己的MySQL应用程序获取这些值. # 需要在MySQL客户端库初始化的时 ...

  3. mysql添加两种数据类型_MySQL入门(二) 数据库数据类型详解

    序言 今天去健身了,感觉把身体练好还是不错的,闲话不多说,把这个数据库所遇到的数据类型今天统统在这里讲清楚了,以后在看到什么数据类型,咱度应该认识,对我来说,最不熟悉的应该就是时间类型这块了.但是通过 ...

  4. mysql字符集的排序规则_MySql字符集与排序规则详解

    前段时间往MySQL中存入emoji表情或生僻字.繁体字时,报错无法添加,研究后发现这是字符集编码的问题,今天就来分析一下各个字符集与排序规则 一.字符集 先说字符,字符是各种文字和符号的总称,包括各 ...

  5. mysql中设置字符集语句_MYSQL字符集设置的方法详解(终端的字符集)

    前言 每次利用终端 创建数据库或者创建表的时候,字符集都是latin1,或者进行插入值的时候,显示的是乱码(不指定字符集的时候)如下: 查看当前数据库的字符集 character_set_client ...

  6. mysql数据库语句左连接_MySQL 左连接 右连接 详解

    表A记录如下: aID        aNum 1           a20050111 2           a20050112 3           a20050113 4          ...

  7. 分布式锁(基于redis和zookeeper)详解

    分布式锁(基于redis和zookeeper)详解 https://blog.csdn.net/a15835774652/article/details/81775044 为什么写这篇文章? 目前网上 ...

  8. SpringBoot默认包扫描机制及@ComponentScan指定扫描路径详解

    SpringBoot默认包扫描机制及@ComponentScan指定扫描路径详解 SpringBoot默认包扫描机制 标注了@Component和@Component的衍生注解如@Controller ...

  9. mysql5.7.11 linux_CentOS 7 中以命令行方式安装 MySQL 5.7.11 for Linux Generic 二进制版本教程详解...

    MySQL 目前的最新版本是 5.7.11,在 Linux 下提供特定发行版安装包(如 .rpm)以及二进制通用版安装包(.tar.gz).一般情况下,很多项目都倾向于采用二进制通用安装包形式来进行安 ...

  10. MySQL数据库锁构建_MySQL数据库InnoDB存储引擎中的锁机制

    00 – 基本概念 当并发事务同时访问一个资源的时候,有可能导致数据不一致.因此需要一种致机制来将访问顺序化. 锁就是其中的一种机制.我们用商场的试衣间来做一个比喻.试衣间供许多消费者使用.因此可能有 ...

最新文章

  1. 前后端分离接口规范~
  2. 2021 年 7 月程序员工资统计,平均 15302 元
  3. 为 hexo 博客添加本地搜索功能
  4. 【云炬大学生创业基础笔记】第1章第4节 为什么需要创业测试
  5. scala java抽象理解_Scala - 抽象类型和隐式参数解析
  6. 将nginx永久加入到系统环境变量
  7. 挖掘经典:几乎被人遗忘的HTML七种用法 (转)
  8. 【英语学习】【Daily English】U05 Places L01 How can I get to the city museum?
  9. CentOS7 安装 scala 2.11.1
  10. dede首页调用会员积分和头像代码
  11. linux查看tmp,linux下find(文件查找)命令的用法总结-tmp文件
  12. 用代码实现task列表里面的Assigned To选择多个人
  13. WebGL多模型光照综合实例
  14. 从Jetty、Tomcat和Mina中提炼NIO构架网络服务器的经典模式
  15. c 和java用cfb_一文彻底搞懂Java中的环境变量
  16. 物联网安全漏洞有哪些
  17. 定制网站建设流程有哪些
  18. Parallels Desktop | 在 Mac 里装 Windows 的最佳工具。
  19. Redis原生SET、SETNX、SETEX方法以及RedisTemplate的方法 + 分布式锁的实现
  20. 开发中使用到的sql总结

热门文章

  1. 个性化测试学生评测软件,电教目录:学生心理测评软件,心理测试系统,学生心理档案管理系统...
  2. 信息系统与管理完美结合
  3. oracle认证就业前景,华为认证的发展就业前景怎么样?
  4. jmeter断言2个变量的值相等
  5. 产品经理与交互设计师的核心区别是什么?
  6. Macbook安装win10双系统后用激活工具激活后开机启动巨慢
  7. 签了工作之后才发现,自己太草率了.....我看过的关于职业规划最好最全面的一篇文章
  8. Oracle的rac是什么?
  9. 【GPU】显卡算力对比表
  10. B端体验度量衡-用户行为监控篇