Mysql基础(十九):锁

2023-05-16

目录

1、Mysql锁机制

1.1、乐观锁和悲观锁

1.1.1、乐观锁

1.1.2、悲观锁

1.2、共享锁与排他锁

1.2.1、共享锁

1.2.2、排他锁

1.3、行锁与表锁

1.3.1、行锁

  1.3.2、表锁 

1.4、间隙锁

1.4.1、生活中的间隙锁

1.4.2、Mysql中的间隙锁

1.4.3、间隙是怎么划分的?

1.4.3、间隙锁锁定的区域

1.4.4、间隙锁的作用范围

1.4.5、next-key锁

2、MyISAM与InnoDB

3、总结


1、Mysql锁机制

MySQL死锁系列-常见加锁场景分析 - 孙龙-程序员 - 博客园

|--表级锁(锁定整个表)

|--页级锁(锁定一页)

|--行级锁(锁定一行)

|--共享锁(S锁,MyISAM 叫做读锁)

|--排他锁(X锁,MyISAM 叫做写锁)

|--悲观锁(抽象性,不真实存在这个锁)

|--乐观锁(抽象性,不真实存在这个锁)

对mysql乐观锁、悲观锁、共享锁、排它锁、行锁、表锁概念的理解_水中加点糖-CSDN博客

  • 在 Mysql 中,行级锁并不是直接锁记录,而是锁索引
  • 索引分为主键索引和非主键索引两种,如果一条sql 语句操作了主键索引,Mysql 就会锁定这条主键索引如果一条语句操作了非主键索引,MySQL会先锁定该非主键索引,再锁定相关的主键索引
  • InnoDB 行锁是通过给索引项加锁实现的,如果没有索引,InnoDB 会通过隐藏的聚簇索引来对记录加锁。
  • 如果不通过索引条件检索数据,那么InnoDB将对表中所有数据加锁,实际效果跟表锁一样。因为没有了索引,找到某一条记录就得扫描全表,要扫描全表,就得锁定表。

1.1、乐观锁和悲观锁

乐观锁和悲观锁都是针对读(select)来说的。

1.1.1、乐观锁

  •  乐观锁不是数据库自带的,需要我们自己去实现。
  • 乐观锁是指 更新数据库时认为操作不会导致冲突,在操作数据时不加锁,而在进行更新后,再去判断是否有冲突了。

通常实现是这样的:在表中的数据进行更新时,先给数据表加一个版本(version)字段,每操作一次,将那条记录的版本号加1。

  • 先查询出那条记录,获取出version字段。
    • 如果要对那条记录进行更新操作,则先判断此刻version的值是否与刚刚查询出来时的version的值相等。
    • 如果相等,则说明这段期间没有其他程序对其进行操作,则可以执行更新,将version字段的值加1。
    • 如果version值与刚刚获取出来的version的值不相等,则说明这段期间已经有其他程序对其进行操作了,则不进行更新操作。

举例:下单操作包括3步骤:

  • 1、查询出商品信息

        select (status,status,version) from t_goods where id=#{id}

  • 2、根据商品信息生成订单
  • 3、修改商品status为2

update t_goods set status=2,version=version+1 where id=#{id} and version=#{version};

        除了自己手动实现乐观锁之外,现在网上许多框架已经封装好了乐观锁的实现,如hibernate,需要时,可能自行搜索"hiberate 乐观锁"试试看。

1.1.2、悲观锁

        悲观锁就是在操作数据时,认为此操作会出现数据冲突,所以在进行每次操作时都要通过获取锁才能进行对相同数据的操作,这点跟java中的synchronized很相似,所以悲观锁需要耗费较多的时间。

  • 悲观锁是由数据库自己实现了的,要用的时候,我们直接调用数据库的相关语句就可以了。
  • 悲观锁涉及到的另外两个锁:就是共享锁与排它锁。
  • 共享锁和排它锁是悲观锁的不同的实现,它俩都属于悲观锁的范畴。

1.2、共享锁与排他锁

数据库的增删改操作默认都会加排他锁,而查询不会加任何锁

1.2.1、共享锁

        悲观锁一般数据库已经实现了,共享锁也属于悲观锁的一种,那么共享锁在mysql中是通过什么命令来调用呢。

在执行语句后面加上 lock in share mode就代表对某些资源加上共享锁了

select * from table where id=1 lock in share mode

1.2.2、排他锁

update、insert、delete 语句会自动加排它锁。

在执行的语句后面加上 for update

select * from table where id=1 for update

1.3、行锁与表锁

1.3.1、行锁

        mysql锁机制分为表级锁和行级锁。mysql行级锁包括共享锁与排他锁:

  • 共享锁又称为读锁,简称S锁
    • 共享锁:多个事务都可以加共享锁读同⼀⾏数据,但是别的事务不能写这⾏数据。
      • 一个事务获取了共享锁,其他事务也只能加共享锁或不加锁查询。
      • 其他事务不能写。
      • 其他事务加排他锁查不到,因为排他锁与共享锁不能存在同一数据上。  
  • 排他锁又称为写锁,简称X锁
    • 排他锁:⼀个事务可以读/写这⾏数据,别的事务只能读不能写。 
      • 一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁
      • 获取排他锁的事务可以对数据就行读取和修改
      • 其他事务可以通过select ...from...查询数据,因为普通查询没有任何锁机制。
      • 其他事务不能写。

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

  • 加排他锁可以使用select ...for update语句
  • 加共享锁可以使用select ... lock in share mode语句

  1.3.2、表锁 

 innodb的 表锁,分成意向共享锁 和意向拍他锁,表锁是innodb引擎⾃动加的,不⽤你⾃⼰去加。

  • 意向共享锁:加共享⾏锁时,必须先加共享表锁;
  • 意向排他锁 :给某⾏加排他锁时,必须先给表加排他锁。

mysql共享锁与排他锁 - java攻城狮 - 博客园

  •   insert、update、delete,innodb会⾃动给那⼀⾏加⾏级排他锁。
  •   select,innodb啥锁都不加。

 innodb从来不会⾃⼰主动加个共享锁的,除⾮你⽤下⾯的语句⾃⼰⼿动加个锁:

  • ⼿动加共享锁:select * from table where id=1 lock in share mode,那你就给那⼀⾏加了个共享锁,其他事务就不能来修改这⾏数据了。
  • ⼿动加排他锁:select * from table where id=1 for update,那你就给那⼀⾏加了个排他锁,意思就是你准备修改,别的事务就别修改了,别的事务的修改会hang住。这个要慎⽤,⼀般我们线上系统不⽤这个,容易搞出问题来。

      对⼀⾏数据,如果有⼈在修改会加个排他锁,然后你不能修改只能等着获取这把锁,但这时可以随便select,查询你的事务开始之前那⾏数据的某个版本。然后如果你修改某⾏数据,会同时拿这个表的排他锁,但是如果不同的事务修改不同的⾏,会拿不同⾏的⾏级排他锁,但⼤家都会拿⼀个表的排他锁,实际上 innodb 的表级排他锁可以随便拿,这个是没冲突的
        mysql innodb 存储引擎的默认锁模式,其实还挺不错的。相当于就是⼀⾏数据,同⼀个时刻只能⼀个⼈在修改,但是别⼈修改,你可以随便读,读是读某个版本的,⾛mvcc 机制

1.4、间隙锁

mySQL数据库间隙锁(mysql是如何解决幻读的)_sinat_27143551的博客-CSDN博客_mysql间隙锁解决幻读

MySQL 中锁的面试题总结 - IT酸菜鱼 - 博客园

其实innodb下的记录锁(也叫行锁),间隙锁,next-key锁统统属于排他锁。

  • Record Lock — 单个行记录上的锁;
  • Gap Lock — 间隙锁,锁定一个范围,不包括记录本身;
  • Next-Key Lock — 锁定一个范围,包括记录本身。

1.4.1、生活中的间隙锁

        编程的思想源于生活,生活中的例子能帮助我们更好的理解一些编程中的思想。生活中排队的场景,小明,小红,小花三个人依次站成一排,此时,如何让新来的小刚不能站在小红旁边,这时候只要将小红和她前面的小明之间的空隙封锁,将小红和她后面的小花之间的空隙封锁,那么小刚就不能站到小红的旁边。这里的小红,小明,小花,小刚就是数据库的一条条记录。
他们之间的空隙也就是间隙,而封锁他们之间距离的锁,叫做间隙锁。

1.4.2、Mysql中的间隙锁

        表中id为主键,number字段上有非唯一索引的二级索引,有什么方式可以让该表不能再插入number=5的记录?注意:此时按照二级索引建索引表,即插入时按照number顺序排序。

         只要控制几个点,number=5之前不能插入记录,number=5现有的记录之间不能再插入新的记录,number=5之后不能插入新的记录,那么新的number=5的记录将不能被插入进来。

那么,mysql是如何控制number=5之前,之中,之后不能有新的记录插入呢(防止幻读)?
答案是用间隙锁,在RR级别下,mysql通过间隙锁可以实现锁定number=5之前的间隙,number=5记录之间的间隙,number=5之后的间隙,从而使的新的记录无法被插入进来

1.4.3、间隙是怎么划分的?

我们规定(id=A,number=B)代表一条字段id=A,字段number=B的记录,(C,D)代表一个区间,代表C-D这个区间范围。

图中根据number列,我们分为几个区间:(无穷小,2)(2,4)(4,5)(5,5)(5,11)(11,无穷大)。
只要这些区间对应的两个临界记录中间可以插入记录,就认为区间对应的记录之间有间隙。
例如:区间(2,4)分别对应的临界记录是(id=1,number=2),(id=3,number=4),这两条记录中间可以插入(id=2,number=3)等记录,那么就认为(id=1,number=2)与(id=3,number=4)之间存在间隙。

很多人会问,那记录(id=6,number=5)与(id=8,number=5)之间有间隙吗?
答案是有的,(id=6,number=5)与(id=8,number=5)之间可以插入记录(id=7,number=5),因此(id=6,number=5)与(id=8,number=5)之间有间隙的,

1.4.3、间隙锁锁定的区域

        根据检索条件向左寻找最靠近检索条件的记录值A,作为左区间,向右寻找最靠近检索条件的记录值B作为右区间,即锁定的间隙为(A,B)。
图一中,where number=5的话,那么间隙锁的区间范围为(4,11);

间隙锁的目的是为了防止幻读,其主要通过两个方面实现这个目的:

  • 防止间隙内有新数据被插入。
  • 防止已存在的数据,更新成间隙内的数据(例如防止numer=3的记录通过update变成number=5)

innodb自动使用间隙锁的条件:

  • 必须在RR级别下
  • 检索条件必须有索引(没有索引的话,mysql会全表扫描,那样会锁定整张表所有的记录,包括不存在的记录,此时其他事务不能修改不能删除不能添加)

1.4.4、间隙锁的作用范围

案例一:

````
session 1:
start  transaction ;
select  * from news where number=4 for update ;

session 2:
start  transaction ;
insert into news value(2,4);#(阻塞)
insert into news value(2,2);#(阻塞)
insert into news value(4,4);#(阻塞)
insert into news value(4,5);#(阻塞)
insert into news value(7,5);#(执行成功)
insert into news value(9,5);#(执行成功)
insert into news value(11,5);#(执行成功)
````

        检索条件number=4,向左取得最靠近的值2作为左区间,向右取得最靠近的5作为右区间,因此,session 1的间隙锁的范围(2,4),(4,5),如下图所示:

         间隙锁锁定的区间为(2,4)(4,5),即记录(id=1,number=2)和记录(id=3,number=4)之间间隙会被锁定,记录(id=3,number=4)和记录(id=6,number=5)之间间隙被锁定。

因此记录(id=2,number=4),(id=2,number=2),(id=4,number=4),(id=4,number=5)正好处在(id=3,number=4)和(id=6,number=5)之间,所以插入不了,需要等待锁的释放,而记录(id=7,number=5),(id=9,number=5),(id=11,number=5)不在上述锁定的范围内,因此都会插入成功。


案例二:

````
session 1:
start  transaction ;
select  * from news where number=13 for update ;

session 2:
start  transaction ;
insert into news value(11,5);#(执行成功)
insert into news value(12,11);#(执行成功)
insert into news value(14,11);#(阻塞)
insert into news value(15,12);#(阻塞)
update news set id=14 where number=11;#(阻塞)
update news set id=11 where number=11;#(执行成功)
````
检索条件number=13,向左取得最靠近的值11作为左区间,向右由于没有记录因此取得无穷大作为右区间,因此,session 1的间隙锁的范围(11,无穷大),如下图所示:

        

         此表中没有number=13的记录的,innodb依然会为该记录左右两侧加间隙锁,间隙锁的范围(11,无穷大)。有人会问,为啥update news set id=14 where number=11会阻塞,但是update news set id=11 where number=11却执行成功呢?

        间隙锁采用在指定记录的前面和后面以及中间的间隙上加间隙锁的方式避免数据被插入

此图间隙锁锁定区域(11,无穷大),即记录(id=13,number=11)之后不能再插入记录,

update news set id=14 where number=11

这条语句如果执行的话,将会被插入到(id=13,number=11)的后面,也就是在区间(11,无穷大)之间,由于该区间被间隙锁锁定,所以只能阻塞等待,而

update news set id=11 where number=11

执行后是会被插入到(id=13,number=11)的记录前面,也就不在(11,无穷大)的范围内,所以无需等待,执行成功。


案例三:

````
session 1:
start  transaction ;
select  * from news where number=5 for update;

session 2:
start  transaction ;
insert into news value(4,4);#(阻塞)
insert into news value(4,5);#(阻塞)
insert into news value(5,5);#(阻塞)
insert into news value(7,11);#(阻塞)
insert into news value(9,12);#(执行成功)
insert into news value(12,11);#(阻塞)
update news set number=5 where id=1;#(阻塞)
update news set id=11 where number=11;#(阻塞)
update news set id=2 where number=4 ;#(执行成功)
update news set id=4 where number=4 ;#(阻塞)
````

        检索条件number=5,向左取得最靠近的值4作为左区间,向右取得11为右区间,因此,session 1的间隙锁的范围(4,5),(5,11),如下图所示:

        有人会问,为啥insert into news value(9,12)会执行成功?间隙锁采用在指定记录的前面和后面以及中间的间隙上加间隙锁的方式避免数据被插入,(id=9,number=12)很明显在记录(13,11)的后面,因此不再锁定的间隙范围内。

为啥update news set number=5 where id=1会阻塞?
        number=5的记录的前面,后面包括中间都被封锁了,你这个update news set number=5 where id=1根本没法执行,因为innodb已经把你可以存放的位置都锁定了,因为只能等待。

同理,update news set id=11 where number=11

由于记录(id=10,number=5)与记录(id=13,number=11)中间的间隙被封锁了,你这句sql也没法执行,必须等待,因为存放的位置被封锁了。


案例四:

session 1:
start  transaction;
select * from news where number>4 for update;

session 2:
start  transaction;
update news set id=2 where number=4 ;#(执行成功)
update news set id=4 where number=4 ;#(阻塞)
update news set id=5 where number=5 ;#(阻塞)
insert into news value(2,3);#(执行成功)
insert into news value(null,13);#(阻塞)

检索条件number>4,向左取得最靠近的值4作为左区间,向右取无穷大,因此,session 1的间隙锁的范围(4,无穷大),如下图所示:

 session2中之所以有些阻塞,有些执行成功,其实就是因为插入的区域被锁定,从而阻塞。


1.4.5、next-key锁

        next-key锁其实包含了记录锁和间隙锁,即锁定一个范围,并且锁定记录本身,InnoDB默认加锁方式是next-key 锁。
上面的案例一session 1中的sql是:

select * from news where number=4 for update ;

        next-key锁锁定的范围为间隙锁+记录锁,即区间(2,4)(4,5)加间隙锁,同时number=4的记录加记录锁。

链接:https://www.jianshu.com/p/bf862c37c4c9

2、MyISAM与InnoDB

https://blog.csdn.net/localhost01/article/details/78720727

  • MyISAM 操作数据都是使用的表锁,你更新一条记录就要锁整个表,导致性能较低,并发不高。当然同时它也不会存在死锁问题。
  • InnoDB 与 MyISAM 的最大不同有两点:
    • 一是 InnoDB 支持事务;
    • 二是 InnoDB 采用了行级锁。也就是你需要修改哪行,就可以只锁定哪行。

3、总结

对于以上,可以看得出来乐观锁和悲观锁的区别:

浅谈Mysql共享锁、排他锁、悲观锁、乐观锁及其使用场景_localhost01-CSDN博客_排他锁

  • 悲观锁实际使用了排他锁来实现(select **** for update)。文章开头说到,innodb加行锁的前提是:必须是通过索引条件来检索数据,否则会切换为表锁。
    • 因此,悲观锁在未通过索引条件检索数据时,会锁定整张表。导致其他程序不允许“加锁的查询操作”,影响吞吐。故如果在查询居多的情况下,推荐使用乐观锁。
    • “加锁的查询操作”:加过排他锁的数据行在其他事务中是不能修改的,也不能通过for update或lock in share mode的加锁方式查询,但可以直接通过select ...from...查询数据,因为普通查询没有任何锁机制。
  • 乐观锁更新有可能会失败,甚至是更新几次都失败,这是有风险的。所以如果写入居多,对吞吐要求不高,可使用悲观锁。

也就是一句话:读用乐观锁,写用悲观锁。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Mysql基础(十九):锁 的相关文章

随机推荐

  • VFH避障流程以及策略

    VFH避障核心代码讲解 xff0c 以及避障流程 VFH是一种由人工势场法改进而来的机器人导航算法 在机器人移动的过程中 xff0c 利用传感器探测周围障碍物信息 xff08 图1 xff09 xff0c 生成极坐标直方图 xff08 图2
  • 解决E: 仓库 “ubuntu bionic Release” 没有 Release 文件

    span class token function sudo span span class token function apt get span update 时候 xff0c 发现404 Not Found E 仓库 http ppa
  • c#/winform 串口编程

    这里结合看到的一些知识和在实际项目中应用的一些方法说明一下如何在 NET平台下使用C 创建串口通信程序 在 net 2 0中提供了串口通信的功能 xff0c 其功能的实现主要是System IO Ports 命名空间下实现的 可以通过加入这
  • c++:线程和进程的区别

    线程是指进程内的一个执行单元 也是进程内的可调度实体 与进程的区别 1 地址空间 进程内的一个执行单元 进程至少有一个线程 它们共享进程的地址空间 而进程有自己独立的地址空间 2 资源拥有 进程是资源分配和拥有的单位 同一个进程内的线程共享
  • Mac设置环境变量

    环境变量是电脑操作系统中常用的一些变量 xff0c 作用类似于将一些常用命令所在的文件夹位置预先告诉操作系统 xff0c 当以后需要用到这些命令时 xff0c 操作系统就自动来这些位置取 例如 xff0c 常见的Java开发中配置的相关环境
  • Java--Java版本和JDK版本

    对于Java初学者 xff0c 经常会听到同事 xff0c 或看到网上Java版本和JDK版本不一的叫法 xff0c 不明白这两者到底什么关系 xff1f 其实博主当年初学Java时也有这样的困惑 xff0c 今天我们就来好好探讨一下 xf
  • Oracle-Windows双击Oracle的setup.exe一闪而过;Oracle安装后的默认账号和密码

    双击Oracle解压包之后的setup exe之后 xff0c 界面一闪而过 xff0c 主要是如下三种原因 1 安装路径不要包含中文 2 安装路径不要包含空格 3 右击 xff0c 选择 以管理员身份运行 顺带提下Oracle安装之后的默
  • Java--Stream流详解

    Stream 是Java 8 API添加的一个新的抽象 xff0c 称为流Stream xff0c 以一种声明性方式处理数据集合 xff08 侧重对于源数据计算能力的封装 xff0c 并且支持序列与并行两种操作方式 xff09 Stream
  • Kubernetes - Kubernetes详解;安装部署(一)

    一 Kubernetes Kubernetes 这个单词源于希腊语 xff0c 意为 舵手 或 飞行员 Kubernetes xff0c 也称为K8S xff0c 其中8是代表中间 ubernete 的8个字符 xff0c 是Google在
  • Shiro - Shiro简介;Shiro与Spring Security区别;Spring Boot集成Shiro

    一 Shiro 以下引自百度百科 shiro xff08 java安全框架 xff09 百度百科 Apache Shiro是一个强大且易用的Java安全框架 xff0c 执行身份验证 授权 密码和会话管理 使用Shiro的易于理解的API
  • 最近

    距离软考还有 3 天的时间 xff0c 该复习的都复习了 xff0c 复习不到的知识点也只有搁置了 任何事情都不可能是完美的 xff0c 软考也一样 xff0c 要的只是追求完美的过程 xff0c 结果重要但是过程更重要 复习到现在感觉基础
  • 微信小程序-微信小程序登录流程(一)

    微信小程序 xff0c 小程序的一种 xff0c 英文名Wechat Mini Program xff0c 是一种不需要下载安装即可使用的应用 xff0c 它实现了应用 触手可及 的梦想 xff0c 用户扫一扫或搜一下即可打开应用 冷启动
  • 微信小程序-获取不限制的小程序码(二)

    一 获取小程序码 获取小程序码 微信开放文档 与 createQRCode 总共生成的码数量限制为 100 000 xff0c 请谨慎调用 调用方式 HTTPS 调用 POST https api weixin qq com wxa get
  • 微信-微信退款(三)

    一 微信申请退款 微信支付退款 API 地址 https pay weixin qq com wiki doc api app app php chapter 61 9 4 amp index 61 6 应用场景 当交易发生之后一段时间内
  • iOS-UILabel根据文本、字体大小计算label宽度;以及自适应高度

    下载地址 GitHub源码 或者 Demo下载 想获得所有字体 xff0c 如下 xff1a 获取到所有的字体名称 NSArray familyNames 61 UIFont familyNames NSLog 64 34 所有字体名称 6
  • iOS开发Provisioning profile "iOS Team Provisioning Profile:xxx" doesn't include signing certificate

    连接真机设备时爆红 因博主忘了截图已经处理过的截图 问题如下 xff1a Provisioning profile 34 iOS Team Provisioning Profile com xxx xxx 34 doesn 39 t inc
  • clang-format的使用

    clang format使用 1 clang format简介2 clang format的使用2 1 clang format中 clang format file2 2 示例 xff1a 配置google的代码风格2 3 vscode
  • NVIDIA TX2i刷机过程记录

    毕业设计做的视觉系统 xff0c 需要用到TX2i xff0c 以下为本人疫情期间在家摸索了十几天 xff0c 才刷机成功的全过程 关于tx2i的注意点 xff1a 首先推荐买个usb扩展器 xff0c 因为只有2个usb口 xff0c 我
  • Java基础(五):重写toString()方法

    目录 1 Object 类的 toString 2 重写toString 方法意义 3 总结 1 Object 类的 toString Java默认的toString方法来自Object类 在Java中每个类都直接或者间接继承Object类
  • Mysql基础(十九):锁

    目录 1 Mysql锁机制 1 1 乐观锁和悲观锁 1 1 1 乐观锁 1 1 2 悲观锁 1 2 共享锁与排他锁 1 2 1 共享锁 1 2 2 排他锁 1 3 行锁与表锁 1 3 1 行锁 1 3 2 表锁 1 4 间隙锁 1 4 1