MySql系列-各种锁

前言

锁是计算机在执行多线程或线程时用于并发访问同一共享资源时的同步机制,MySQL中的锁是在服务器层或者存储引擎层实现的,保证了数据访问的一致性与有效性。

MySQL锁可以按模式分类为:乐观锁与悲观锁。按粒度分可以分为全局锁、表级锁、页级锁、行级锁。按属性可以分为:共享锁、排它锁。按状态分为:意向共享锁、意向排它锁。按算法分为:间隙锁、临键锁、记录锁。

在这里插入图片描述
在这里插入图片描述

乐观锁和悲观锁

乐观锁

(1) 概念
乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则返回给用户错误的信息,让用户决定如何去做。

(2) 应用场景
适用于读多写少,因为如果出现大量的写操作,写冲突的可能性就会增大,业务层需要不断重试,会大大降低系统性能。

(3) 实现方式
一般使用数据版本(Version)记录机制实现,在数据库表中增加一个数字类型的“version”字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据。
(4) 实战
订单order表中id,status,version分别代表订单ID,订单状态,版本号。
1.查询订单信息

select id,status,version from order where id=#{id};

2.用户支付成功

3.修改订单状态

update order set status='支付成功', version=version+1 where id='' and version='';

悲观锁

(1) 概念
悲观锁,正如其名,具有强烈的独占和排他特性,每次去拿数据的时候都认为别人会修改,对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。

(2) 应用场景
适用于并发量不大、写入操作比较频繁、数据一致性比较高的场景。

(3) 实现方式
在MySQL中使用悲观锁,必须关闭MySQL的自动提交,set autocommit=0。共享锁和排它锁是悲观锁的不同的实现,它俩都属于悲观锁的范畴。
(4) 实战
商品goods表中id,name,number分别代表商品ID,商品名称,商品库存。

1.开启事务并关闭自动提交

set autocommit=0;

2.查询商品信息

select id,name,number from goods where id=1 for update;

3.用户下单,生成订单

4.修改商品库存

update goods set number=number-1 where id=1;

5.提交事务

commit;

总结:
select...for update 是MySQL提供的实现悲观锁的方式,属于排它锁,在goods表中,id为1的那条数据就被当前事务锁定了,其它的要执行select id,name,number from goods where id=1 for update;的事务必须等本次事务提交之后才能执行。这样我们可以保证当前的数据不会被其它事务修改。

**注意:**此时MySQL InnoDB默认行级锁。行级锁都是基于索引的,如果一条SQL语句用不到索引是不会使用行级锁的,会使用表级锁把整张表锁住。

共享锁和排它锁

共享锁

(1) 概念
共享锁,又称之为读锁,简称S锁,当事务A对数据加上读锁后,其他事务只能对该数据加读锁,不能做任何修改操作,也就是不能添加写锁。只有当事务A上的读锁被释放后,其他事务才能对其添加写锁。

(2) 应用场景
共享锁主要是为了支持并发的读取数据而出现的,读取数据时,不允许其他事务对当前数据进行修改操作,从而避免”不可重读”的问题的出现。

排它锁

(1) 概念
排它锁,又称之为写锁,简称X锁,当事务对数据加上写锁后,其他事务既不能对该数据添加读写,也不能对该数据添加写锁,写锁与其他锁都是互斥的。只有当前数据写锁被释放后,其他事务才能对其添加写锁或者是读锁。

MySQL InnoDB引擎默认update,delete,insert都会自动给涉及到的数据加上排他锁,select语句默认不会加任何锁类型。

(2) 应用场景
写锁主要是为了解决在修改数据时,不允许其他事务对当前数据进行修改和读取操作,从而可以有效避免”脏读”问题的产生。

意向锁

意向锁存在的意义在于,使得行锁和表锁能够共存。

意向锁是表级别的锁,用来说明事务稍后会对表中的数据行加哪种类型的锁(共享锁或独占锁)。

当一个事务对表加了意向排他锁时,另外一个事务在加锁前就会通过该表的意向排他锁知道前面已经有事务在对该表进行独占操作,从而等待。
需要强调一下,意向锁是一种不与行级锁冲突表级锁
需要强调一下,意向锁是一种不与行级锁冲突表级锁
需要强调一下,意向锁是一种不与行级锁冲突表级锁

意向共享锁

intention shared lock, IS):事务有意向对表中的某些行加共享锁(S锁)

-- 事务要获取某些行的 S 锁,必须先获得表的 IS 锁。
SELECT column FROM table ... LOCK IN SHARE MODE;

意向排它锁

intention exclusive lock, IX):事务有意向对表中的某些行加排他锁(X锁)
意向锁是有数据引擎自己维护的,用户无法手动操作意向锁,在为数据行加共享 / 排他锁之前,InnoDB会先获取该数据行所在在数据表的对应意向锁

插入意向锁

插入意向锁是在数据行插入之前通过插入操作设置的间隙锁定类型

如果多个事务插入到相同的索引间隙中,如果它们不在间隙中的相同位置插入,则无需等待其他事务。例如:在4和7的索引间隙之间两个事务分别插入5和6,则两个事务不会发冲突阻塞。

共享锁(S)排它锁(X)意向共享锁(IS)意向排它锁(IX)
共享锁(S)兼容冲突兼容冲突
排它锁(X)冲突冲突冲突冲突
意向共享锁(IS)兼容冲突兼容兼容
意向排它锁(IX)冲突冲突兼容兼容

按粒度划分

全局锁

所谓全局锁,其实就是给整个数据库实例加锁。
数据库实例与数据库是有所区别的:

  • 数据库,就是保存数据的仓库,具体到mysql中,数据库其实是一系列数据文件集合(也就是我们通常所说的database,比如创建数据库语句就是
    create database...)。
  • 数据库实例,是指访问数据库的应用程序,在Mysql中,就是mysqld进程了。
  • 简单来理解,数据库实例中包含了你创建的各种数据库

如果给数据库实例加全局锁会导致整个库处于只读状态(这是非常危险的)。

一般来说,全局锁的典型使用场景是用于全库备份,即把数据库中所有的表都select出来。但是要注意,让整个库都处于只读状态,会导致一些严重的问题。
全局锁的加锁语句是:

Flush tables with read lock

解除全局锁的方法是:

  1. 断开执行全局锁的session即可;
  2. 执行解锁sql语句:unlock tables;

在主库上加全局锁,在加锁期间,不能执行任何更新操作,业务基本上很多功能都不可用了;

在从库上加全局锁,在加锁期间,不能执行主从同步,会导致主从同步延迟。

元数据锁

元数据锁(MetaData Lock),也叫MDL锁,是用来保护元数据信息,系统级的锁无法主动控制。在MySQL5.5版本,开始引入MDL锁,主要是为了在并发环境下对DDL、DML同时操作下保持元数据的一致性。比如下面这种情况:

隔离级别:RR

事务1事务2
begin;#开始事务
select * from t_user where id = 1begin
drop table t_user
select * from t_user where id = 1

如果没有元数据锁的保护,那么事务2可以直接执行DDL操作,导致事务1出错。MYSQL5.5版本的时候加入 MDL 锁,是为了保护这种情况的发生。由于事务1开启了查询,那么获得了元数据锁,锁的模式为MDL读锁,事务2要执行DDL,则需获得 MDL 写锁,由于读写锁互斥,所以事务2需要等待事务1释放掉读锁才能执行。

  1. 对表中的记录进行增删改查(DML操作)的时候,自动加MDL读锁
  2. 对表的结构(DDL操作)进行修改的时候,自动加MDL写锁
    MDL锁的粒度
    MDL锁是Mysql服务器层面中实现的,而不是在存储引擎插件中实现。按照锁定的范围,MDL锁可以分为以下几类:
属性含义锁定的是范围/对象加锁语句
GLOBAL全局锁所有的数据库flush tables with read lock
COMMIT提交保护锁当前提交记录
SCHEMA库锁对象
TABLE表锁对象
FUNCTION函数锁对象
PROCEDURE存储过程锁对象
TRIGGER触发器锁对象
EVENT事件锁对象

MDL锁的模式

锁模式对应SQL语句
MDL_INTENTION_EXCLUSIVEGLOBAL对象、SCHEMA对象操作会加此锁
MDL_SHAREDFLUSH TABLES WITH READ LOCK
MDL_SHARED_HIGH_PRIO仅对MyISAM存储引擎有效
MDL_SHARED_READSELECT查询
MDL_SHARED_WRITEDML语句
MDL_SHARED_WRITE_LOW_PRIO仅对MyISAM存储引擎有效
MDL_SHARED_UPGRADABLEALTER TABLE
MDL_SHARED_READ_ONLYLOCK xxx READ
MDL_SHARED_NO_WRITEFLUSH TABLES xxx,yyy,zzz READ
MDL_SHARED_NO_READ_WRITEFLUSH TABLE xxx WRITE
MDL_EXCLUSIVEALTER TABLE xxx PARTITION BY …

页级锁

MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。不同的存储引擎支持不同的锁机制。根据不同的存储引擎,MySQL中锁的特性可以大致归纳如下:

存储引擎行锁表锁页锁
MYISAM支持
Innodb支持支持
BDB支持支持

页级锁是MySQL中比较独特的一种锁定级别,应用于BDB引擎,并发度一般,页级锁定的特点是锁定颗粒度介于行级锁定与表级锁之间,所以获取锁定所需要的资源开销,以及所能提供的并发处理能力也同样是介于上面二者之间。另外,页级锁定和行级锁定一样,会发生死锁。

锁定粒度大小比较:表级锁 > 页级锁 > 行级锁

行级锁

在编写业务代码的过程中,我们接触最多的就是行级锁了(表级锁由于性能问题,一般不推荐使用)。相比于表级锁,行级锁具有明显的性能优势:

冲突少:多线程中访问不同的记录时只存在少量锁定冲突;

锁的粒度小:可以长时间锁定单一的行,对其他的行没有影响,所以并发度是最高的;

但是使用行锁时,一旦稍不注意,是非常容易出现死锁的(表锁就不存在死锁现象),所以使用行锁需要注意加锁的顺序和锁定的范围。

InnoDB的行锁是通过对索引项加锁实现的,这表示只有通过索引查询记录时才会使用行锁,如果不走索引查询数据将使用表锁,则性能会大打折扣。

需要记住:行锁也叫记录锁,记录锁都是加在索引上的。
where条件指定的是主键索引:则在主键索引上加锁;

wehre条件指定的是二级索引:记录锁不仅会加在这个二级索引上,还会加在这个二级索引所对应的聚簇索引上;
where条件如果无法走索引:MySQL会给整张表所有数据行加记录锁,存储引擎层将所有记录返回由MySQL服务端进行过滤。

表级锁

相比于行锁的细粒度加锁,表锁是对整张表加锁。由于是对整张表加锁,就没有行锁的加锁方式那么复杂,所以加锁比行锁快,而且不会出现死锁的情况(因为事务是一次性获取想要加的表表锁),但是表锁也存在一些问题:锁的范围过大,在并发比较高的情况下,会导致抢锁的冲突概率变高,这样并发性能就大打折扣了。
表锁的加锁方式
MYISAM引擎只支持表锁,不支持行锁
手动添加表级锁的语句如下:

1、加表读锁:lock table {tableName} read;
2、加表写锁:lock table {tableName} write;
3、释放表锁:unlock tables;或者客户端断开连接也也会自动释放锁

在使用MYISAM引擎时,通常我们不需要手动加锁,因为MYISAM引擎会针对我们的sql语句自动进行加锁,整个过程不需要用户干预:

查询语句(select):会自动给涉及的表加读锁;
更新语句(update、delete、insert):会自动给涉及的表加写锁

InnoDB引擎同时支持行级锁和表级锁默认为行级锁
给InnoDB引擎的表手动加锁,也同样使用 lock table {tableName} read/write 语句进行读/写锁的添加。

除此之外,innodb 还支持一种表级锁:意向锁(上文已经介绍过)。

总的来说,InnoDB 引擎的表级锁包含五种锁模式:
LOCK_IS:表意向读锁
LOCK_IX:表意向写锁
LOCK_S:表读锁
LOCK_X:表写锁
LOCK_AUTO_INC:自增锁

自增锁

1.1 定义

自增锁是一种表级锁( table-level lock ),专门针对插入 AUTO_INCREMENT 类型的列。同一表,假设事务 A 正在插入数据,则另一个事务 B 尝试 INSERT 语句,事务 B 会被阻塞住,直到事务 A 释放自增锁,以便事务A插入的行是连续的主键 ID。

1.2 插入方式
插入方式解释
Simple Inserts(简单插入)可以预估插入行数的语句(普通的 insert/replace into 语句)但不包含像 insert … on duplicate key update … 插入或者更新的语句
Bulk Inserts(批量插入)无法预估插入行数的语句(包括 insert … select, replace … select 和 load data 语句 )
Mixed-mode insert (混合插入)类似 insert into t1(id, age) values (1,"zhang3"),(null, "li4"),(5,"wang5");有些行指定了自增id,有些行未指定自增id
1.3 自增锁模式

其实在 InnoDB 中,把锁的行为叫做锁模式可能更加准确,那具体有哪些锁模式呢,如下:

  • 传统模式(Traditional
  • 连续模式(Consecutive
  • 混合模式(Mixed-mode

分别对应配置项 innodb_autoinc_lock_mode 的值 0、1、2。
这三种模式下,InnoDB 对并发的处理是不一样的,而且具体选择哪种锁模式跟你当前使用的 MySQL 版本还有关系。

在 MySQL 8.0 之前,InnoDB 锁模式默认为连续模式,值为1,而在 MySQL 8.0 之后,默认模式变成了交叉模式。

模式解释innodb_autoinc_lock_mode
传统模式执行语句时加 AUTO-INC 表级锁,statement 语句执行完毕后释放0
连续模式针对批量插入 时会采用 AUTO-INC 锁,针对简单插入时,采用轻量级的互斥锁(mutex 锁)1
混合模式不使用 AUTO-INC 表级锁 ,采用轻量级的互斥锁(mutex 锁)2
传统模式: 在 innoDB 没有 引入了`锁模式`之前默认的方式,表锁,锁颗粒度大,比较重

在这里插入图片描述

缺点:传统模式的弊端就自然暴露出来了,如果有多个事务并发的执行 `INSERT` 操作,`AUTO-INC`的存在会使得 MySQL 的性能略有下降,因为同时只能执行一条 `INSERT` 语句。
连续模式:`8.0版本之前`为默认设置,该模式下可以保证同一 `insert` 语句中新插入的自增 ID 都是连续
混合模式:`8.0版本默认设置`,由于锁的粒度减少,多条语句在插入时进行锁竞争,自增长的值可能不连续。并且当 `Binlog 模式为 statement(statement-based replication,SBR)`时, 直接导致`主从之间`同行的数据`主键 ID 不同`

并发执行所带来的副作用就是单个 INSERT 的自增值并不连续,因为 AUTO_INCREMENT 的值分配会在多个 INSERT 语句中来回交叉的执行。

优点很明确,缺点是在并发的情况下无法保证数据一致性。
在这里插入图片描述

混合模式缺陷

在了解缺陷前,还得先了解一下 MySQLBinlog。Binlog 一般用于 MySQL 的数据复制,通俗一点就是用于主从同步。在 MySQLBinlog 的格式有 3 种,分别是:

  • Statement
    基于语句,只记录对数据做了修改的SQL语句,能够有效的减少binlog的数据量,提高读取、基于binlog重放的性能。

  • Row
    只记录被修改的行,所以Row记录的binlog日志量一般来说会比Statement格式要多。基于Row的binlog日志非常完整、清晰,记录了所有数据的变动,但是缺点是可能会非常多,例如一条update语句,有可能是所有的数据都有修改;再例如alter table之类的,修改了某个字段,同样的每条记录都有改动。
    在这里插入图片描述

  • MixedStatementRow的结合,怎么个结合法呢。例如像alter table之类的对表结构的修改,采用Statement格式。其余的对数据的修改例如updatedelete采用Row格式进行记录。

如果 MySQL 采用的格式为 Statement ,那么 MySQL 的主从同步实际上同步的就是一条一条的 SQL 语句。如果此时我们采用了混合模式,那么并发情况下 INSERT 语句的执行顺序就无法得到保障。

换句话说,如果 DB 有主从同步,并且 Binlog 存储格式为 Statement,那么不要将 InnoDB 自增锁模式设置为混合模式,会有问题。其实主从同步的过程远比上图中的复杂,可以了解一下:MySQL主从同步的文章

MySQL 将日志存储格式从 Statement 变成了 Row,这样一来,主从之间同步的就是真实的行数据了,而且 主键ID 在同步到从库之前已经确定了,就对同步语句的顺序并不敏感,就规避了上面 Statement 的问题。

基于 MySQL 默认 Binlog 格式从 StatementRow 的变更,InnoDB 也将其自增锁的默认实现从连续模式,更换到了效率更高的混合模式

如果你的 MySQL 版本仍然默认使用连续模式,但同时又想要提高性能,该怎么办呢?这个其实得做一些取舍。

如果你可以断定你的系统后续不会使用 Binlog,那么你可以选择将自增锁的锁模式从连续模式改为混合模式,这样可以提高 MySQL 的并发。并且,没有了主从同步,INSERT 语句在从库乱序执行导致的 AUTO_INCREMENT 值不匹配的问题也就自然不会遇到了。

`为啥要了解这些?实际工作中需要处理这部分的事情不?`
例如在业务中你有一个需要执行 几十秒 的脚本,脚本中不停的调用多次 INSERT,这时就问你这个问题,在这几十秒里,会阻塞其他的用户使用对应的功能吗?
1.4 自增 ID 分配问题

1.4.1 自增 ID 的初始化
8.0版本之前,自增 ID 的值存储在内存中,重启后丢弃,下一次将读取内存预分配最大自增 ID之后的 ID 值进行发号

8.0版本之后,自增 ID 的值将会持久化到磁盘。每次发号时会写入 Redolog 日志,重启时通过 Redolog 恢复之前的值

1.4.2 自增 ID 的连续性
三种模式都无法保障自增id的连续性,除非设置隔离界别为 串行化(Serialiable)隔离级别。

  • i插入发生唯一索引冲突校验
 如已存数据(2, 清水),name 字段是唯一键,再次插入(null, 清水), 唯一索引冲突校验,但这时自增ID已经变化为3,如再次插入(null, 水哥),会使用4的自增 ID
  • 事务回滚
因为 id 是在内存中不持久化,如在同一事务中插入不提交事务,再回滚,会丢弃ID发号段,当再次执行插入语句,提交的的 id 不会连续,类似于产生幻读。

1.4.3 自增 ID 上限问题
如果表未设置主键,默认使用隐式的 ROW_ID 作为主键, 它的取值范围为 [0, 2^32 -1]

如果自增主键达到上限,则发放的下一个 ID 为最大 id 也就是4294967295 ,然后就会提示主键冲突。

Duplicate entry '4294967295' for key 'PRIMARY'

当然我们可以使用 bigint 类型,基本不会有这种问题

如果表设置主键,但主键类型是字符串,那么隐式的 ROW_ID 如果超过 4294967295 这个最大值,那么
新产生的数据行对应的 row_id 又会从 0 开始发放,此时新插入的数据行会覆盖 row_id=0 的数据记录。

1.5 连续模式实践
CREATE TABLE user_test(
    id BIGINT(20) NOT NULL AUTO_INCREMENT,
	name VARCHAR(50) UNIQUE,
	age tinyint(3) DEFAULT 18,
	PRIMARY KEY(id),
	INDEX(name)
) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4

  • 唯一索引不冲突情况
insert into user_test(name) values("qinqshui"),("yunyan"),("heitie");
select * from user_test;

简单插入情况下,插入的语句,可以提前预估插入的行数,所以连续的自增,是 1 2 3
在这里插入图片描述

  • 唯一索引冲突情况
# 插入冲突冲突
insert into user_test(name) values("qinqshui");
# 插入不冲突记录
insert into user_test(name) values("hanlin");
select * from user_test;

有唯一性索引校验,但是后续的插入不是预期的 4 而是变成了 5 ,主键ID的连续性遭到破坏。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
混合插入

DROP TABLE user_test;
CREATE TABLE user_test(
    id BIGINT(20) NOT NULL AUTO_INCREMENT,
 name VARCHAR(50) UNIQUE,
 age tinyint(3) DEFAULT 18,
 PRIMARY KEY(id),
 INDEX(name)
) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4
  • ID 依次插入
insert into user_test(id, name) values(1, "qingshui");
  • 混合插入
insert into user_test(id, name) values (50, "yunyan"),(NULL, "heitie"),(100, "hanlin"),(NULL,"tianqi");
SELECT * FROM user_test;

在这里插入图片描述

  • 插入小于最大自增ID的情况
    在这里插入图片描述
`混合插入`情况下,如果行指定了自增 ID,则不使用数据库生成新的 ID,但对应的最大自增 ID 会更新覆盖,如果未指定自增 ID,即 (NULL) 的情况下,数据库才会生成 ID,所以插入小于主键 ID 的情况,虽然插入的 ID 是2,但最大的自增 ID 更新至了102,后续插入变成 103
  • insert … on duplicate key update
# 一条更新,两条插入
insert into user_test(name)values("qingshui"),("longgu"),("renjie") on duplicate key update age=28;
select * from user_test;

一条更新的情况下,自增 ID 会+1但不使用,Id = 104 被占用,其他插入的情况会新增 ID 并使用,所以变成了 105 106。
在这里插入图片描述
总结:

 `mutex` 锁 通过 dict_table_t 中的 ib_mutex_t 结构体实现,通过类似 cas 比较并交换 的方式实现加锁解锁,性能好 自增锁 依赖于锁系统的实现,属于表锁,性能差。
1.6 源码解析

请点击此处查看-mysql之innodb锁系统源码分析

按算法划分

记录锁

记录锁是封锁记录,记录锁也叫行锁,例如:
它会在 id=1 的记录上加上记录锁,以阻止其他事务插入,更新,删除 id=1 这一行。

select * from goods where id=1 for update;

间隙锁

LOCK_GAP(只锁间隙)

间隙锁是一种区间锁。锁加在不存在的空闲空间上,或者两个索引记录之间,或者第一个索引记录,或者最后一个索引之后的空间,用来表示只锁住一段范围(一般在进行范围查询时且隔离级别在RR或Serializable隔时)。

一般在RR隔离级别下会使用到GAP锁。使用GAP锁,主要是为了防止幻读产生,在被GAP锁锁住的区间,不允许插入数据或者更新数据。

间隙锁的产生条件:innodb的隔离级别为 Repeatable Read 或者Serializable

间隙锁基于非唯一索引,它锁定一段范围内的索引记录。使用间隙锁锁住的是一个区间。

select * from goods where id between 1 and 10 for update;

即所有在(1,10)区间内的记录行都会被锁住,所有id 为 2、3、4、5、6、7、8、9 的数据行的插入会被阻塞,但是 1和 10 两条记录行并不会被锁住。

CREATE TABLE goods(
    id int NOT NULL AUTO_INCREMENT,
 name VARCHAR(50) UNIQUE,
 number tinyint(3) DEFAULT 0,
 PRIMARY KEY(id)
) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4;

begin;
select * from goods where id between 1 and 10 for update;

在这里插入图片描述在这里插入图片描述

临键锁

临键锁(Next-Key Lock),是记录锁与间隙锁的组合,它的封锁范围,既包含索引记录,又包含索引区间,是一个左开右闭区间。临键锁的主要目的,也是为了避免幻读(Phantom Read)。如果把事务的隔离级别降级为RC,临键锁则也会失效

每个数据行上的非唯一索引列上都会存在一把临键锁,当某个事务持有该数据行的临键锁时,会锁住一段左开右闭区间的数据。需要强调的一点是,InnoDB 中行级锁是基于索引实现的临键锁只与非唯一索引列有关,在唯一索引列(包括主键列)上不存在临键锁。

在这里插入图片描述
goods表中隐藏的临键锁有:(-∞, 96],(96, 99],(99, +∞]。

session1 在对 number 为 96 的列进行 update 操作的同时,也获取了(-∞, 96],(96, 99]这两个区间内的临键锁。

最终我们就可以得知,在根据非唯一索引对记录行进行 UPDATE \ FOR UPDATE \LOCK IN SHARE MODE 操作时,InnoDB 会获取该记录行的临键锁,公式为:左gap lock + record lock + 右gap lock。

即session1在执行了上述的 SQL 后,最终被锁住的记录区间为 (-∞, 99)。
在这里插入图片描述

死锁

死锁产生原因和示例

所谓死锁<DeadLock>:是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。表级锁不会产生死锁.所以解决死锁主要还是针对于最常用的InnoDB

死锁的关键在于:两个(或以上)的Session加锁的顺序不一致。
那么对应的解决死锁问题的关键就是:让不同的session加锁有次序

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值