PHP8.1.21版本已发布
vue8.1.21版本已发布
jquery8.1.21版本已发布

重温MySQL锁机制

coldplay.xixi
coldplay.xixi 转载
2021-04-06 10:07:50 1806浏览

文章目录

    • 锁分类
    • 共享锁(读锁,读写互斥,读读互不影响)
    • 排他锁(写锁,独占锁)
    • 共享锁和排他锁的区别
  • 表锁
    • 意向锁:意向共享锁和意向排他锁
      • 为什么需要加入意向锁
    • 单个表锁定
    • 全局表锁定
  • 行锁
  • 行锁升级为表锁(行锁实际是给索引加锁,如果没用索引而全表扫描,则会给全表加锁)
  • 修改、删除某一行记录,且未提交事务时,该行会一直被行锁锁定
  • 记录锁
  • 间隙锁(Gap Locks)
    • 间隙锁的作用:防止幻读
    • innodb自动使用间隙锁的条件
    • 间隙锁锁定的区域
    • next-key锁【临键锁】
  • 记录锁、间隙锁、临间锁的区别

相关免费学习推荐:mysql视频教程

锁是计算机协调多个线程访问同一共享资源的机制,主要用于解决多线程访问同一共享资源的并发安全问题。
在这里插入图片描述

锁分类

(1)从性能分为:乐观锁和悲观锁
MySQL中采用版本号控制,JVM中使用CAS

(2)从数据库操作类型分为:读锁(共享锁)、写锁(排他锁) 【读锁和写锁都属于悲观锁】

  • 读锁(共享锁):针对相同行记录,多个读操作可以同时进行,但任何事务都不能进行
  • 写锁(排他锁):获取排他锁的事务既能读数据,又能修改数据。获取到写锁的事务为完成之前,会阻塞其他事务获取写锁或读锁。

(3)从颗粒度划分为,表锁和行锁

注意:
(1)读锁、写锁都属于行级锁,即事务1 对商品A 获取写锁,和事务2对商品B 获取写锁互相不会阻塞的。
(2)如果sql语句使用了行锁,当SQL未使用索引而使用全表扫描的时候,行级锁会变成表锁。

(3)锁只有在执行commit或者rollback的时候才会释放,并且所有的锁都是在同一时刻被释放。
(4)Innodb引擎执行普通的修改、删除sql语句时,会给修改和删除的行加行锁。

共享锁(读锁,读写互斥,读读互不影响)

事务A使用共享锁获取了某条(或某些)记录时,事务B可以读取这些记录,可以继续添加共享锁,但是不能修改这些记录(当事务C对这些数据修改或删除时,会进入阻塞状态,直至锁等待超时或者事务A提交)

  • 如何使用共享锁和释放共享锁
# 加锁SELECT ... LOCK IN SHARE MODE# 释放锁commit;rollback;
  • 作用
    SELECT … LOCK IN SHARE MODE在读取的若干行记录加共享锁(share lock),其他事务只能对这些行查询而不能修改这些记录。多个事务可以对相同的行记录加共享锁,因此获取了共享锁的事务不一定能修改行数据

  • 使用场景: 读取结果集的最新版本,同时防止其他事务产生更新该结果集
    例如:并发下对商品库存的操作

排他锁(写锁,独占锁)

select … for update在对读取的行记录加排他锁,只允许获取到该排他锁的事务修改该行记录,防止其他事务对该行进行修改,就像普通的update语句执行时会加行锁一样。

  • 如何使用排他锁
# 加排他锁select ... for update //排他锁 # 释放锁commit;rollback;

共享锁和排他锁的区别

(1)一旦某个事务获取了排他锁,其他事务就无法再获取排他锁。
多个事务可以对相同行数据加共享锁。
(2)给指定行加了共享锁的事务,不一定能修改该行数据,因为其他事务也可能对该行加了共享锁或排他锁; 给指定行加了排他锁的事务,则可以修改加了排他锁的该行数据

表锁

使用表级锁定的主要是MyISAM,MEMORY,CSV等一些非事务性存储引擎。
表锁一般在数据迁移时使用。

意向锁:意向共享锁和意向排他锁

某行加共享锁的前提是:该行数据所在表会先获取到意向共享锁。
某行加排他锁的前提是:该行数据所在表会先获取到意向排他锁。
注意:意向共享锁和意向排他锁均是表锁,无法手动创建。

为什么需要加入意向锁

意向锁是为了告知mysql该表已经存在数据被加锁,而不需要逐行扫描是否加锁,提搞加锁的效率。

单个表锁定

lock tables saas.goods read,saas.account write;  // 给saas库中的goods表加读锁,account表加写锁unlock tables;   //解锁

全局表锁定

FLUSH TABLES WITH READ LOCK;   // 所有库所有表都被锁定只读unlock tables;         //解锁

注意: 在客户端和数据库断开连接时,都会隐式的执行unlock tables。如果要让表锁定生效就必须一直保持连接。

  • 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低;
    行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高;

行锁

  • 行锁是锁一行或者多行记录
  • MySQL的行锁是基于索引,行锁是加在索引上,而不是加在行记录上的。
    在这里插入图片描述
    如上图所示,数据库中有1个主键索引和1个普通索引,图中的sql语句是基于普通索引查询,命中4条记录,此时一把行锁就锁定两条记录,而其他事务修改这两条记录中的任意一条,都会一直阻塞【获取锁的事务没有执行commit之前】,下图就是上图没有执行commit语句时的情况。
    在这里插入图片描述

行锁升级为表锁(行锁实际是给索引加锁,如果没用索引而全表扫描,则会给全表加锁)

在这里插入图片描述
上图中where条件中,虽然template_name建立普通索引,但使用or关键字,导致template_name的索引失效,从而进行了全表扫描,锁定了整张表。

修改、删除某一行记录,且未提交事务时,该行会一直被行锁锁定

在这里插入图片描述
窗口1中删除某一行,但没有提交。窗口2中更新该行会一直处于阻塞中。
在这里插入图片描述

记录锁

  • 行锁:行锁是命中索引,一把锁锁的是一张表的一条记录或多条记录
  • 记录锁:记录锁是在行锁的衍生锁,记录锁锁的是表中的某一条记录,记录锁出现的条件必须是:精确命中索引,且索引是唯一索引(比如主键id、唯一索引列)。

间隙锁(Gap Locks)

经典参考文章

间隙锁的作用:防止幻读

间隙锁的目的是为了防止幻读,其主要通过两个方面实现这个目的:
(1)防止止间隙内有新数据被插入
(2)防止范围内已存在的数据被更新

innodb自动使用间隙锁的条件

(1)数据隔离级别必须为可重复读
(2)检索条件必须使用索引(没有使用索引的话,mysql会全表扫描,那样会锁定整张表所有的记录,包括不存在的记录,此时其他事务不能修改不能删除不能添加)

间隙锁锁定的区域

根据检索条件向左寻找最靠近检索条件的记录值A,作为左区间,向右寻找最靠近检索条件的记录值B作为右区间,即锁定的间隙为(A,B)。下图中,where number=5的话,那么间隙锁的区间范围为[4,11];
在这里插入图片描述

session 1:start  transaction ;触发间隙锁的方式1:select  * from news where number=4 for update ;触发间隙锁的方式2:update news set number=3 where number=4; 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);#(执行成功)

next-key锁【临键锁】

next-key锁其实包含了记录锁和间隙锁,即锁定一个范围,并且锁定记录本身。InnoDB默认加锁方式是next-key 锁。

select * from news where number=4 for update ;

在这里插入图片描述

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

记录锁、间隙锁、临间锁的区别

在这里插入图片描述

update news  set number=0 where id>15

sql默认加的是next-key锁。根据上图,next-key锁的区间为(-∞,1],(1,5],(5,9],(9,11],(11,+∞),上面id>15,实际上next-key锁是加在[11,+∞)这个范围内,而不是(15,+∞)这个范围内。注意:需要使用锁的字段必须加索引,因为锁是加在索引上的,没有索引则加的表锁。

相关免费学习推荐:mysql数据库(视频)

声明:本文转载于:CSDN,如有侵犯,请联系admin@php.cn删除