前言

前言为本人写这篇文章的牢骚,建议跳过不看。

  之前好几次都想好好的学习MySQL中的锁,但是找了几篇文章,看了一些锁的类型有那么多种,一时间也没看懂是什么意思,于是跟自己说先放松下自己,便从书桌起来在阳台发呆、做做运动、打扫下公寓,时间就这样过去了,真就印证了那句话:"当你在学习的时候,无论做什么其他的事情都是有趣的。"但却又能安慰自己:"我不是不想学习,只是在学习的过程中放松了下自己,下次再看吧吧",这样心里也不会存有罪恶感,安稳睡去,下次亦是重复如此。但是日志一长,MySQL锁就变成了一块又硬又重的石头,完全不想碰它。但是这几天重新看了一遍,发现这块石头减肥了,于是便有了这篇文章。

MySQL的锁

  首先在阅读文章之前,得先理解一个概念:锁之间的互斥是作用于获取锁来说的。举个例子,我拿到了一条记录的X锁,如果其他线程想要获取这条记录的其他锁,那么就需要等待,这就是互斥;但是如果其他线程不想获取锁,只是简单的访问,例如说select * from t where id = 1这些不用锁的语句,并不需要获取此记录的锁,那么这个SQL不会进入阻塞,并不会形成互斥。

共享锁/独占锁(Shared and Exclusive Locks)

  一种比较大的分类,不一定指行锁,也可能是表锁。如果是X锁(独占锁)则跟其他所有锁形成互斥;如果是S锁(共享锁),则可实现读读(SS)并行。哈?你叫我举个例子。okay,拿行锁(记录锁)来说,当我们执行下面的SQL时当前事务拿到的是id为3的X行锁。

update t set name = 'name1' where id = 3;

  那么代表什么呢,代表如果上方的事务还没结束的话,此时下面的操作都会被阻塞。

// 获取记录上的S锁
select * from t where id = 3 lock in share mode;
// 获取记录上的X锁
update t set name = 'name2' where id = 3;

  也就是说,共享锁和独占锁的互斥关系如下表。

X S
X 互斥 互斥
S 互斥 兼容

  但是,这个时候我有一个问题。

  如果执行的这个语句呢?

select * from t where id = 3;

  是否还会出现阻塞的情况呢?好好的思考一下,然后想想自己为什么要想这个没有意义的问题。

  答案是不会出现阻塞的情况,原因我在一开始的时候就说了,互斥是针对锁的争夺来说的,上面的这条SQL并不需要获取到锁,所以也就不会进入阻塞。

意向锁(Intention Locks)

  在讲意向锁之前,我们首先思考这样的一个问题:

  如果我想对整张表进行独占式锁定(即X表锁),我没办法像个莽夫一样直接就锁上了,我得知道表中的记录有没有被其他的事务锁住,如果有的话那么我就进入阻塞等待释放,那要怎么知道呢?

  暴力点从第一条记录开始遍历到最后?不行,如果一张表有几百万甚至上千万的话效率就会非常低下(别跟我说该分表了)。于是,为了解决这种性能问题,意向锁应援而生。

  意向锁就是为了提升获取表锁前判断而存在的锁。首先意向锁是表级别锁(表锁跟行锁是可以同时存在不冲突),在获取数据行上的独占锁或者共享锁之前首先得获取到表级别的意向锁,这样子,当有其他线程想要进行锁表的时候,看到已经存在意向锁,那么就进入阻塞状态,而不用遍历所有数据行判断是否行上有锁,这样子就大幅提升了性能。

  意向锁也分共享意向锁(IS)和独占意向锁(IX)。意思也很简单,获取S锁之前需要获取的就是IS锁,获取X锁之前需要获取的就是IX锁,其锁之间的互斥关系如下图。

X S
IX 互斥 互斥
IS 互斥 兼容

  看了上面的图,可能有人会问:"兄弟,你这只有跟独占/共享锁的互斥图,意向锁之间的互斥关系呢?"

  首先,我得跟你说,我绝对不是因为懒得画图才不画的,因为意向锁之间是不互斥的。你想嘛,本来意向锁就是为了提升锁表时的判断性能而存在的,而且本身自己也是表级别的锁,你还给整个互斥关系,到时候不给你堵得像北京三环一样,所以不互斥是一级棒的。

记录锁(Record Locks)

  在上方也简单的提到过记录锁,记录锁也称行锁,是一种针对特定索引上的锁,注意是索引,而且如果只有记录锁的话必须是唯一索引(意思就是说只有记录锁一种,而非临建锁),过程为通过唯一索引定位到对应的聚簇索引,然后将这条聚簇索引锁住。如果唯一索引是多列组成的,但是查询条件只用到了其中几列,这种情况就不是施加记录锁,比方说,唯一索引为uniq_index(name,age),但是条件为where name = 'name1',这个时候用到的就不是记录锁而是临建锁了。

间隙锁(Gap Locks)

  对于一些非唯一索引的查询或者范围查询的情况下,使用到的就是间隙锁。怎么理解呢,来看下官方给出的文档:

A gap lock is a lock on a gap between index records, or a lock on the gap before the first or after the last index record.

  意思就是说间隙锁是在索引之间、第一个索引之前或者最后一个索引之后的锁。这样讲可能还是有点抽象,举个简单的例子,假设现在有这样的一张表:

id(bigint) name(varchar)
3 n3
5 n5
7 n7

  再结合上面那句话的意思,那么可以知道这张表中间隙锁的取值范围为

(负无穷,3),

(3,5),

(5,7),

(7, 正无穷)

  有些文章的范围会将右边边界也取上,但我觉得这样反而不符合官方文档的定义,而且不好理解,不过取上也没关系,因为间隙锁一般不会单独使用,一般使用的都是临键锁(临键锁的话会把记录带上,也就是边界也取到了),所以锁的范围都是一样的,看个人觉得哪个容易理解。另外关于间隙锁的范围,官方描述中有一段有趣的描述,是这样的:

​ A gap might span a single index value, multiple index values, or even be empty.

  意思就是说,间隙可以是一个索引、可以跨越多个索引,甚至可以是空的;换句话说,"根本就没有间隙锁,或者说,哪里都是间隙锁。"

  另外需要注意:间隙锁只有在RC级别及以上的隔离级别中才有,其他级别是没有间隙锁的。

  讲了这么多可能还是云里雾里的,来举个简单的例子,还是上面表格中的记录。

id(bigint) name(varchar)
3 n3
5 n5
7 n7

  id的值分别为3、5、7,那么下面这条SQL的间隙范围是多少呢?

select * from t where id between 3 and 5 for update;

  你以为是(3,5)?其实是(3,5)和(5,7)哒,这也另一方面证实了官方说的间隙锁可能跨越索引的说法。实际上由于临键锁的存在,会把记录锁也带上,也就是边界3、5、7都会被锁住(即范围为[3,7],已验证,若有其他见解请在评论区狠狠的打我的脸,不要客气,打得越狠越好)。

  讲到这里,可能有人会说,”你说的这些我现在懂了,那这个间隙锁到底有什么用呢?"

  emmm,先看下官方文档是怎么说的:

  Gap locks in InnoDB are “purely inhibitive”, which means that their only purpose is to prevent other transactions from inserting to the gap.

  用我的工地英语翻译过来就是,间隙锁不会阻塞,唯一的作用就是防止其他事务往这个间隙中插入记录;毫不客气的说,间隙锁除了防止在间隙之间插入记录之外一无是处,间隙锁之间甚至还可以共存(无论是X还是S间隙锁),你锁(3,5)跟我锁(2,6)又有什么关系呢?

临键锁(next-key locks)

  上方介绍间隙锁的时候多多少少也提到过,临键锁就是记录锁+间隙锁的结合。一般来说使用的都是临键锁而非间隙锁,上方的SQL:

select * from t where id between 3 and 5 for update;

 锁定的范围带上边界也说明了,实际上边界上的锁都是记录锁,所以范围才变成了[3, 7]。当然使用的是唯一索引的话会进化成记录锁(多列索引的情况需另外考虑,记录锁处已提到),下面这个SQL用的就是记录锁。

select * from t where id = 3 for update;

插入意向锁

  一种特殊的间隙锁,在其间隙中可以进行非当前记录的插入,目的是为了提高插入的并发,可以理解为间隙为1的间隙锁。 同时也是一种意向锁,只不过这是给插入专用的。

自增锁

  表锁,一般用在设置了auto_increment的字段。大致就是先获取锁,然后将最大的ID自增,与其他锁不同的时候,完成自增操作之后其就释放了,不需要等待事务的完成。

  

本文为博客园原创文章,文章地址

参考:

https://blog.csdn.net/qq_41026740/article/details/97408858

https://zhuanlan.zhihu.com/p/48269420

https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html#innodb-insert-intention-locks

MySQL锁这块石头似乎没有我想的那么重的更多相关文章

  1. mysql锁

    锁是计算机协调多个进程或线程并发访问某一资源的机制.在数据库中,除传统的计算资源(如CPU.RAM.I/O等)的争用以外,数据也是一种供许多用户共享的资源.如何保证数据并发访问的一致性.有效性是所有数 ...

  2. mysql锁表机制及相关优化

    (该文章为方便自己查阅,也希望对大家有所帮助,转载于互联网) 1. 锁机制 当前MySQL支持 ISAM, MyISAM, MEMORY (HEAP) 类型表的表级锁,BDB 表支持页级锁,InnoD ...

  3. Mysql锁机制--并发事务带来的更新丢失问题

    Mysql 系列文章主页 =============== 刚开始学习 Mysql 锁的时候,觉得 Mysql 使用的是行锁,再加上其默认的可重复读的隔离级别,那就应该能够自动解决并发事务更新的问题.可 ...

  4. Mysql锁机制--索引失效导致行锁变表锁

    Mysql 系列文章主页 =============== Tips:在阅读本文前,最好先阅读 这篇(Mysql锁机制--行锁)文章~ 在上篇文章中,我们看到InnoDB默认的行锁可以使得操作不同行时不 ...

  5. Mysql 锁和锁算法

    相关命令: show engines;  查看数据库支持的引擎 show variables like '%storage_engine%';   查看数据库默认的引擎 select @@global ...

  6. MySQL锁解决并发问题详解

    文章分为以下几个要点 问题描述以及解决过程 MySQL锁机制 数据库加锁分析 下面讨论的都是基于MySQL的InnoDB. 0. 问题描述以及解决过程 因为涉及到公司利益问题,所以下面很多代码和数据库 ...

  7. 深入理解 MySQL ——锁、事务与并发控制

    本文首发于vivo互联网技术微信公众号 mp.weixin.qq.com/s/JFSDqI5ya… 作者:张硕 本文对 MySQL 数据库中有关锁.事务及并发控制的知识及其原理做了系统化的介绍和总结, ...

  8. MYSQL锁表问题解决

    本文实例讲述了MYSQL锁表问题的解决方法.分享给大家供大家参考,具体如下: 很多时候!一不小心就锁表!这里讲解决锁表终极方法! 案例一 ? 1 mysql>show processlist; ...

  9. MySQL锁(行锁、表锁、页锁、乐观锁、悲观锁等)

    锁,在现实生活中是为我们想要隐藏于外界所使用的一种工具.在计算机中,是协调多个进程或县城并发访问某一资源的一种机制.在数据库当中,除了传统的计算资源(CPU.RAM.I/O等等)的争用之外,数据也是一 ...

随机推荐

  1. 加载Properties文件工具类:LoadConfig

    import javax.servlet.http.HttpServletRequest; import javax.ws.rs.GET; import javax.ws.rs.Path; impor ...

  2. linux之DHCP服务端搭建 ( ip分配 四个阶段原理)

    DHCP服务 ip分配 四个阶段原理 1.DHCP服务目的 协议 作用 租约 原理四个阶段 动态主机配置协议(Dynamic Host Configuration Protocol,动态主机配置协议) ...

  3. 洛谷3月月赛div2 题解(模拟+数学+贪心+数学)

    由于本人太蒻了,div1的没有参加,胡乱写了写div2的代码就赶过来了. T1 苏联人 题目背景 题目名称是吸引你点进来的. 这是一道正常的题,和苏联没有任何关系. 题目描述 你在打 EE Round ...

  4. 打印java系统的信息

    System.getProperties() 确定当前系统属性 Properties.list() 将获取到的信息打印到流中 public class Userone { public static ...

  5. iOS开发多线程在实际项目中的运用

    实际项目开发中为了能够给用户更好的体验,有些延时操作我们都会放在子线程中进行. 今天我们就来聊聊多线程在实际项目中的运用. 我们先来看看多线程的基础知识: 1.多线程的原理: 同一时间,CPU只能处理 ...

  6. Python画各种 3D 图形Matplotlib库

    回顾 2D 作图 用赛贝尔曲线作 2d 图.此图是用基于 Matplotlib 的 Path 通过赛贝尔曲线实现的,有对赛贝尔曲线感兴趣的朋友们可以去学习学习,在 matplotlib 中,figur ...

  7. JSP中contentType、pageEncoding和meta charset的区别

    1.创建JSP 使用Eclipse创建JSP文件: <%@ page language="java" contentType="text/html; charset ...

  8. 【Python笔记】2020年7月22日练习=[定义一个函数quadratic(a, b, c),接收3个参数,返回一元二次方程的两个解]

    学习教程:廖雪峰-Python教程-函数-函数定义 学习记录:[定义一个函数quadratic(a, b, c),接收3个参数,返回一元二次方程的两个解] 学习心得: 1.对问题进行判断分析后再下手. ...

  9. 看DLI服务4核心如何提升云服务自动化运维

    摘要:今天我们来说说DLI是如何实现监控告警来提升整体运维能力,从而为客户更好的提供Serverless的DLI. DLI是支持多模引擎的Serverless大数据计算服务,免运维也是其作为Serve ...

  10. 手动向Maven本地仓库添加ORACLE ojdbc6jar包

    第一步: 把你的oracle中的ojdbc6.jar复制放到D盘首目录 这是我的D:\oracle\product\11.2.0\dbhome_1\jdbc\ D:ojdbc6.jar 但是Maven ...