史上最全MySQL各种锁详解

锁详解

锁是计算机协调多个进程或线程并发访问某一资源的机制。

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

锁分类

  • 从性能上分为乐观锁(用版本对比来实现)和悲观锁
  • 从对数据库操作的类型分,分为读锁和写锁(都属于悲观锁)

读锁(共享锁,S锁(Shared)):针对同一份数据,多个读操作可以同时进行而不会互相影响

写锁(排它锁,X锁(eXclusive)):当前写操作没有完成前,它会阻断其他写锁和读锁

  • 从对数据操作的粒度分,分为表锁和行锁

表锁

每次操作锁住整张表。开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低;一般用在整表数据迁移的场景。

  • 手动增加表锁

lock table 表名称 read(write),表名称2 read(write);

例如lock tables t1 read, t2 write; 命令,则其他线程写 t1、读写 t2 的语句都会被阻塞。同时,线程 A 在执行 unlock tables 之前,也只能执行读 t1、读写 t2 的操作。连写 t1 都不允许,自然也不能在unlock tables之前访问其他表。

  • 查看表上加过的锁

show open tables;

  • 删除表锁

unlock tables;

行锁

每次操作锁住一行数据。锁定粒度最小,发生锁冲突的概率最低,并发度最高,但是加锁慢、开销大,容易发生死锁现象。

InnoDB与MYISAM的最大不同有两点:

  • InnoDB支持事务(TRANSACTION)
  • InnoDB支持行级锁

InnoDB在执行查询语句SELECT时(非串行隔离级别),不会加锁。但是update、insert、delete操作会加行锁。

简而言之,就是读锁会阻塞写,但是不会阻塞读。而写锁则会把读和写都阻塞

实现方式

在MySQL中,行级锁并不是直接锁记录,而是锁索引。索引分为主键索引和非主键索引两种,如果一条sql语句操作了主键索引,MySQL就会锁定这条主键索引;如果一条语句操作了非主键索引,MySQL会先锁定该非主键索引,再锁定相关的主键索引。在UPDATE、DELETE操作时,MySQL不仅锁定WHERE条件扫描过的所有索引记录,而且会锁定相邻的键值,即所谓的next-key locking。

可以看到由于session1迟迟未提交事务,session2在等待session1释放锁时出现了超过锁定超时的警告了。

无索引行锁会升级为表锁(RR级别会升级为表锁,RC级别不会升级为表锁)

锁主要是加在索引上,如果对非索引字段更新,行锁可能会变表锁。

InnoDB的行锁是针对索引加的锁,不是针对记录加的锁。并且该索引不能失效,否则都会从行锁升级为表锁

锁定某一行还可以用lock in share mode(共享锁) 和for update(排它锁),例如:select * from test_innodb_lock where a = 2 for update; 这样其他session只能读这行数据,修改则会被阻塞,直到锁定行的session提交

卧槽,又来一个 Java 神器!!

间隙锁(Gap Lock)

间隙锁,锁的就是两个值之间的空隙。间隙锁基于非唯一索引,它锁定一段范围内的索引记录。使用间隙锁锁住的是一个区间,而不仅仅是这个区间中的每一条数据。

Mysql默认级别是repeatable-read,有办法解决幻读问题吗?间隙锁在某些情况下可以解决幻读问题。

假设account表里数据如下:

那么间隙就有 id 为 (3,10),(10,20),(20,正无穷) 这三个区间,

在Session_1下面执行 update account set name = ‘张三’ where id > 8 and id <18;,则其他Session没法在这个范围所包含的所有行记录(包括间隙行记录)以及行记录所在的间隙里插入或修改任何数据,即id在(3,20]区间都无法修改数据,注意最后那个20也是包含在内的。

间隙锁是在可重复读隔离级别下才会生效。

事务A:

事务B:

事务A:

事务B:

临键锁(Next-key Locks)

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

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

Next-Key Locks是行锁与间隙锁的组合。像上面那个例子里的这个(3,20]的整个区间可以叫做临键锁。

卧槽,又来一个 Java 神器!!

记录锁

记录锁也叫行锁,例如:

select * from emp where empno = 1 for update;

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

乐观锁

在关系数据库管理系统里,乐观并发控制(又名“乐观锁”,Optimistic Concurrency Control,缩写“OCC”)是一种并发控制的方法;乐观锁( Optimistic Locking ) 是相对悲观锁而言,乐观锁是假设认为即使在并发环境中,外界对数据的操作一般是不会造成冲突,所以并不会去加锁(所以乐观锁不是一把锁),而是在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回冲突信息,让用户决定如何去做下一步,比如说重试,直至成功为止;数据库的乐观锁,并不是利用数据库本身的锁去实现的,可能是利用某种实现逻辑去实现做到乐观锁的思想。

数据库的乐观并发控制要解决的是数据库并发场景下的写-写冲突,指在用无锁的方式去解决

CAS思想

数据库乐观锁的具体实现几乎就跟Java中乐观锁采用的CAS算法思想是一致,所以我们可以从CAS算法中学习到数据库乐观锁的设计:

CAS指令全称为Compare and Swap,它是系统的指令集,整个CAS操作是一个原子操作,是不可分割的。从具体的描述上,我们可以这么看CAS操作:

CAS指令需要3个操作数,分别是内存位置V,旧的预期值A,和新值B。CAS指令执行时,当我们读取的内置位置V的现值等于旧预期值A时,处理器才会将新值B去更新内置位置V的值。否则它就不执行更新,但无论是否更新V的值,都会返回V的旧值。

我们通俗的放到代码层次上去理解i = 2; i++,就是说:

  • 首先线程1从内存位置V中读取到了值,保存并作为旧预期值A. (v = 2 ,a = 2)

  • 然后在因为i要进行++操作,系统会比较内存位置V的现值跟旧预期值A进行比较,既V =? A。

  • 如果相等,B = i++ = 3 ,新值B就会对内存位置V进行更新,所以内存位置V的值就变成了B的值,3

  • 如果不相等,则说明有其他的线程修改过了内存位置V的值,比如线程2在线程1修改i的值前就更新了i的值。,所以线程1会更新变量i失败。但线程不会挂起,而是返回失败状态,等待调用线程决定是否重试或其他操作。(通常会重试直到成功)

  • 数据库层的乐观锁实现也类似代码层面的实现

实现方式

通常乐观锁的实现有两种,但它们的内在都是CAS思想的设计:

方式一: 使用数据版本(version)实现
这是乐观锁最常用的一种实现方式。什么是数据版本呢?就是在表中增添一个字段作为该记录的版本标识,比如叫version,每次对该记录的写操作都会让 version+ 1。
所以当我们读取了数据(包括version),做出更新,要提交的时候,就会拿取得的version去跟数据库中的version比较是否一致,如果一致则代表这个时间段,并没有其他的线程的也修改过这个数据,给予更新,同时version + 1;如果不一致,则代表在这个时间段,该记录以及被其他线程修改过了, 认为是过期数据,返回冲突信息,让用户决定下一步动作,比如重试(重新读取最新数据,再过更新)

update table set num = num + 1 , version = version + 1 where version = #{version} and id = #{id}

方式二: 使用时间戳(timestamp)实现

表中增加一个字段,名称无所谓,比如叫update_time, 字段类型使用时间戳(timestamp)
原理和方式一一致,也是在更新提交的时检查当前数据库中数据的时间戳和自己更新前取到的时间戳是否一致,如果一致则代表此刻没有冲突,可以提交更新,同时时间戳更新为当前时间,否则就是该时间段有其他线程也更新提交过,返回冲突信息,等待用户下一步动作。

update table set num = num + 1 ,update_time = unix_timestamp(now()) where id = #{id} and update_time = #{updateTime}

但是我们要注意的是,要实现乐观锁的思想的同时,我们必须要要保证CAS多个操作的原子性,即获取数据库数据的版本,拿数据库的数据版本与之前拿到的版本的比较,以及更新数据等这几个操作的执行必须是连贯执行,具有复合操作的原子性;所以如果是数据库的SQL,那么我们就要保证多个SQL操作处于同一个事务中。

悲观锁

在关系数据库管理系统里,悲观并发控制(又名“悲观锁”,Pessimistic Concurrency Control,缩写“PCC”)是一种并发控制的方法; 悲观锁指的是采用一种持悲观消极的态度,默认数据被外界访问时,必然会产生冲突,所以在数据处理的整个过程中都采用加锁的状态,保证同一时间,只有一个线程可以访问到数据,实现数据的排他性;通常,数据库的悲观锁是利用数据库本身提供的锁机制去实现的.

数据库的悲观并发控制可以解决读-写冲突和写-写冲突,指在用加锁的方式去解决。

实现方式

数据库的悲观锁就是利用数据库本身提供的锁去实现的(select…for update是MySQL提供的实现悲观锁的方式)

  • 外界要访问某条数据,那它就要首先向数据库申请该数据的锁
  • 如果获得成功,那它就可以操作该数据,在它操作期间,其他客户端就无法再操作该数据了
  • 如果获得失败,则代表同一时间已有其他客户端获得了该锁,那就必须等待其他客户端释放锁

共享锁

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

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

实现方式
select …lock in share mode

session1持有共享锁,未提交事务。session2的查询不受影响,但是update操作会被一直阻塞,直到超时。

排它锁

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

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

session1 不提交事务,session2不能加锁。

兼容性加锁方式
S锁:共享锁加了S锁的记录,允许其他事务再加S锁,不允许其他事务再加X锁select…lock in share mode
X锁:排他锁加了X锁的记录,不允许其他事务再加S锁或者X锁select…for update

意向共享锁和意向排它锁

意向锁是表锁,为了协调行锁和表锁的关系,支持多粒度(表锁与行锁)的锁并存。

意向共享锁(IS锁):事务在请求S锁前,要先获得IS锁
意向排他锁(IX锁):事务在请求X锁前,要先获得IX锁

为什么意向锁是表级锁呢?

当我们需要加一个排他锁时,需要根据意向锁去判断表中有没有数据行被锁定(行锁);

(1)如果意向锁是行锁,则需要遍历每一行数据去确认;

(2)如果意向锁是表锁,则只需要判断一次即可知道有没数据行被锁定,提升性能。

下图表示意向锁和共享锁、排他锁的兼容关系。
1.当事务A对某个数据范围(行或表)上了“某锁”后,另一个事务B是否能在这个数据范围上“某锁”。
2.意向锁相互兼容,因为IX、IS只是表明申请更低层次级别元素(比如 page、记录)的X、S操作。
3.表级S锁和X、IX锁不兼容:因为上了表级S锁后,不允许其他事务再加X锁。
4.表级X锁和 IS、IX、S、X不兼容:因为上了表级X锁后,会修改数据,所以即使是行级排他锁,因为表级锁定的行肯定包括行级锁定的行,所以表级X和IX、X都不兼容。

注意:上了行级X锁后,行级X锁不会因为有别的事务上了IX而堵塞,一个mysql是允许多个行级X锁同时存在的,只要他们不是针对相同的数据行。

行锁分析

通过检查InnoDB_row_lock状态变量来分析系统上的行锁的争夺情况

show status like 'innodb_row_lock%';              

对各个状态量的说明如下:

Innodb_row_lock_current_waits: 当前正在等待锁定的数量

Innodb_row_lock_time: 从系统启动到现在锁定总时间长度

Innodb_row_lock_time_avg: 每次等待所花平均时间

Innodb_row_lock_time_max:从系统启动到现在等待最长的一次所花时间

Innodb_row_lock_waits: 系统启动后到现在总共等待的次数

对于这5个状态变量,比较重要的主要是:

Innodb_row_lock_time_avg (等待平均时长)

Innodb_row_lock_waits (等待总次数)

Innodb_row_lock_time(等待总时长)

尤其是当等待次数很高,而且每次等待时长也不小的时候,我们就需要分析系统中为什么会有如此多的等待,然后根据分析结果着手制定优化计划。

查看INFORMATION_SCHEMA系统库锁相关数据表

-- 查看事务 
select * from INFORMATION_SCHEMA.INNODB_TRX; 
-- 查看锁 
select * from INFORMATION_SCHEMA.INNODB_LOCKS; 
-- 查看锁等待 
select * from INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
-- 释放锁,trx_mysql_thread_id可以从INNODB_TRX表里查看到 
kill trx_mysql_thread_id 
-- 查看锁等待详细信息 
show engine innodb status\G;       

死锁

set tx_isolation=‘repeatable-read’;

Session_1执行:select * from account where id=1 for update;

Session_2执行:select * from account where id=2 for update;

Session_1执行:select * from account where id=2 for update;

Session_2执行:select * from account where id=1 for update;

查看近期死锁日志信息:show engine innodb status\G;

大多数情况mysql可以自动检测死锁并回滚产生死锁的那个事务,但是有些情况mysql没法自动检测死锁

锁优化建议

  • 尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁
  • 合理设计索引,尽量缩小锁的范围
  • 尽可能减少检索条件范围,避免间隙锁
  • 尽量控制事务大小,减少锁定资源量和时间长度,涉及事务加锁的sql尽量放在事务最后执行
  • 尽可能低级别事务隔离

总结

Innodb存储引擎由于实现了行级锁定,虽然在锁定机制的实现方面所带来的性能损耗可能比表级锁定会要更高一下,但是在整体并发处理能力方面要远远优于MYISAM的表级锁定的。当系统并发量高的时候,Innodb的整体性能和MYISAM相比就会有比较明显的优势了。

但是,Innodb的行级锁定同样也有其脆弱的一面,当我们使用不当的时候,可能会让Innodb的整体性能表现不仅不能比MYISAM高,甚至可能会更差。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/234310.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深度解析IP应用场景API:提升风险控制与反欺诈能力

前言 在当今数字化时代&#xff0c;网络安全和用户数据保护成为企业日益关注的焦点。IP应用场景API作为一种强大的工具&#xff0c;不仅能够在线调用接口获取IP场景属性&#xff0c;而且具备识别IP真人度的能力&#xff0c;为企业提供了卓越的风险控制和反欺诈业务能力。本文将…

2c 操作符详解

文章目录 1. 操作符分类&#xff1a;2. 算术操作符3. 移位操作符(对二进制移位)3.1 左移操作符3.2 右移操作符 4. 位操作符(重要)5. 赋值操作符6. 单目操作符6.1 单目操作符介绍6.2 sizeof 和 数组 7. 关系操作符8. 逻辑操作符(重要)9. 条件操作符10. 逗号表达式11. 下标引用、…

linux权限管理以及shell

1.shell 1.1什么是shell? shell即外壳&#xff0c;是运行在linux系统上的一个脚本语言&#xff0c;包裹在linux内核的外面。我们常说的linux操作系统实际上是linux内核。我们使用的所有指令都是一个个程序&#xff0c;而shell指令就是一个将我们用户的操作翻译给linux内核的程…

c语言堆排序(详解)

堆排序 堆排序是一种基于二叉堆数据结构的排序算法&#xff0c;它的基本概念包括&#xff1a; 建立堆&#xff1a;将待排序的列表构建成一个二叉堆&#xff0c;即满足堆的性质的完全二叉树&#xff0c;可以是最大堆或最小堆。最大堆要求父节点的值大于等于其子节点的值&#x…

【软件测试】年薪30万跟年薪15万的面试有些什么区别?

1、什么是兼容性测试&#xff1f;兼容性测试侧重哪些方面&#xff1f; 参考答案&#xff1a; 兼容测试主要是检查软件在不同的硬件平台、软件平台上是否可以正常的运行&#xff0c;即是通常说的软件的可移植性。 兼容的类型&#xff0c;如果细分的话&#xff0c;有平台的兼容…

鸿蒙OS应用开发之语句

在程序开发中&#xff0c;已经有上面的运算符和数据类型了&#xff0c;可以满足了大部的需求&#xff0c;但是这些程序还是比较简单的计算和逻辑运算&#xff0c;如果需要复杂的计算和逻辑处理&#xff0c;就需要采用复杂逻辑程序块来处理了&#xff0c;也就是复杂条件语句才能…

【9】Spring Boot 3 集成组件 : 基于spring security的认证权限开发【认证】

目录 【9】Spring Boot 3 集成组件 &#xff1a; 基于spring security的用户权限系统【认证】认证和权限流程【理论】认证授权鉴权权限控制认证、授权、鉴权、和权限控制关系认证和鉴权的关系 认证流程【理论】1. 直接携带认证令牌访问资源2. Session认证机制3. Jwt认证机制下的…

单调栈结构

单调栈 单调栈是一种特殊设计的栈结构&#xff0c;为了解决如下的问题&#xff1a; 给定一个可能含有重复数值的 arr[]&#xff0c;i位置的数一定存在如下两种信息&#xff1a; arr[i]的左侧离 i 最近并且小于&#xff08;或者大于&#xff09;arr[i] 的数在哪&#xff1f;arr…

JVM虚拟机系统性学习-运行时数据区(堆)

运行时数据区 JVM 由三部分组成&#xff1a;类加载系统、运行时数据区、执行引擎 下边讲一下运行时数据区中的构成 根据线程的使用情况分为两类&#xff1a; 线程独享&#xff08;此区域不需要垃圾回收&#xff09; 虚拟机栈、本地方法栈、程序计数器 线程共享&#xff08;数…

二叉搜索树中第K小的元素[中等]

优质博文&#xff1a;IT-BLOG-CN 一、题目 给定一个二叉搜索树的根节点root&#xff0c;和一个整数k&#xff0c;请你设计一个算法查找其中第k个最小元素&#xff08;从1开始计数&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [3,1,4,null,2], k 1 输出&#x…

编译Android14 AOSP原生代码并运行X86模拟器镜像过程记录

最近在研究Android Entreprise部分的特性&#xff0c;需要在Android手机上分析WorkProfile相关的源码&#xff0c;因为新买的Pixel样机还未到货&#xff0c;看了几天Android源码&#xff0c;迫切需要上真机对比分析。 又听说最近几年Android模拟器已经有些进步&#xff0c;至少…

【利用二手车数据进行可视化分析】

利用二手车数据进行可视化分析 查看原始数据去除重复数据需求分析1.统计全国总共有多少量二手车&#xff0c;用KPI图进行展示2.统计安徽总共有多少量二手车&#xff0c;用KPI图进行展示3.统计合肥总共有多少量二手车&#xff0c;用KPI图进行展示4.取最贵的10辆二手车信息&#…

游戏提示找不到d3dx10_43.dll怎么办?5种方法教你如何修复

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“缺少d3dx10_43.dll文件”。这个错误提示通常出现在运行某些游戏或应用程序时&#xff0c;它意味着系统无法找到所需的动态链接库文件。本文将详细介绍d3dx10_43.dll文件的作用以及导致其丢…

class066 一维动态规划【算法】

class066 一维动态规划 算法讲解066【必备】从递归入手一维动态规划 code1 509斐波那契数列 // 斐波那契数 // 斐波那契数 &#xff08;通常用 F(n) 表示&#xff09;形成的序列称为 斐波那契数列 // 该数列由 0 和 1 开始&#xff0c;后面的每一项数字都是前面两项数字的和。…

从关键新闻和最新技术看AI行业发展(2023.11.20-12.3第十一期) |【WeThinkIn老实人报】

Rocky Ding 公众号&#xff1a;WeThinkIn 写在前面 【WeThinkIn老实人报】旨在整理&挖掘AI行业的关键新闻和最新技术&#xff0c;同时Rocky会对这些关键信息进行解读&#xff0c;力求让读者们能从容跟随AI科技潮流。也欢迎大家提出宝贵的优化建议&#xff0c;一起交流学习&…

睡觉时总在凌晨3、4点醒来,再无睡意,一般暗示四大疾病,别忽视!

良好的睡眠是人体健康的重要因素&#xff0c;正常情况下的八小时睡眠时间在人们一天的时间中占比三分之一&#xff0c;睡眠是人体器官进行自我修复和调整的关键时间&#xff0c;睡眠质量不好&#xff0c;则会影响各个器官的健康&#xff0c;人们可以从睡眠中发现身体的异常&…

常见web漏洞的流量分析

常见web漏洞的流量分析 文章目录 常见web漏洞的流量分析工具sql注入的流量分析XSS注入的流量分析文件上传漏洞流量分析文件包含漏洞流量分析文件读取漏洞流量分析ssrf流量分析shiro反序列化流量分析jwt流量分析暴力破解流量分析命令执行流量分析反弹shell 工具 攻击机受害机wi…

midwayjs从零开始创建项目,连接mikro-orm框架(必须有java的springboot基础)

前言&#xff1a; 我一直都是用java的springboot开发项目&#xff0c;然后进来新公司之后&#xff0c;公司的后端是用node.js&#xff0c;然后框架用的是 midwayjs &#xff0c;然后网上的资料比较少&#xff0c;在此特地记录一波 文档&#xff1a; 1.官方文档&#xff1a;介绍…

Linux C/C++ 从内存转储中恢复64位ELF可执行文件

ELF&#xff08;Executable and Linking Format&#xff09;是一种对象文件的格式&#xff0c;它主要用于定义ELF&#xff08;Executable and Linking Format&#xff09;是一种对象文件的格式&#xff0c;它主要用于定义不同类型的对象文件中的内容以及它们的存储方式。一个EL…

深入了解对象与内置构造函数

1. 深入对象 1.1 创建对象的三种方式 1.2 构造函数 语法约定&#xff1a; 总结 构造函数可以快速创建多个对象大写字母开头的函数使用new关键字将对象实例化构造函数不需要返回值自动返回新的对象 new实例化的执行过程 创建空对象this指向对象执行代码&#xff0c;追加新…