【MySQL】——锁

 

前言:

     MySQL锁机制涉及到数据库并发控制的核心概念,它可以保证多个会话(连接)对数据库中数据的读写操作不会相互干扰,从而确保了数据的正确性和可靠性。然而,锁的引入也带来了一定的复杂性和性能开销,特别是在高并发环境下,不合理的锁使用可能导致数据库性能下降甚至出现死锁等问题。

    本文将深入探讨MySQL中的各种锁类型及其工作原理,我们将从理论和实践两个角度出发,介绍MySQL锁机制的基本概念、各个锁的使用,常见情况。更好的了解锁的相关知识。

目录

前言:

一.概述

1.介绍

2.锁的分类

二. 全局锁

三.表锁

1.表锁

2.元数据锁

3.意向锁

1.小例子引入意向锁

2.意向锁

3.意向锁分类:

4.意向锁兼容情况

四.行级锁

1.行级锁介绍

2.行锁

3.间隙锁和临键锁


一.概述

1.介绍

    锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中,除传统的计算资源(CPU、RAM、I/O)的争用以外,数据也是-种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来说,锁对数据库而言显得尤其重要,也更加复杂。  Mysql中也我们提供了各种各样的锁,来应对不同的场景。

2.锁的分类

MySQL中的锁,按照锁的粒度分,分为以下三类:
1.全局锁:锁定数据库中的所有表。
2.表级锁:每次操作锁住整张表。
3.行级锁:每次操作锁住对应的行数据。

二. 全局锁

全局锁就是对整个数据库实例加锁,加锁之后整个实例就处于只读状态,后续DML的写语句,DDL语句,已经更新操作的事务提交语句都将被阻塞。

一般也就是使用在做全库的逻辑备份,需要对所有的表进行锁定,从而获取一致性视图,保障数据的完整性。

 逻辑备份:把数据库的数据文件备份成一整个SQL文件。

如何加全局锁:flush tables with read lock;
如何解全局锁:unlock tables;

全局锁的缺点:

如果在主库上备份的时候,那么在备份期间都不能执行更新,业务基本就要停摆。

如果在从库上备份,那么备份期间从库不能执行主库同步过来的二进制日志,会导致主从延迟。

在InnoDB引擎中,我们可以在备份的时候添加上参数-----single-transaction参数来完成不加锁的一致性数据备份。

三.表锁

1.表锁

2.元数据锁

元数据锁(meta data lock,MDL)
        元数据是什么呢?我们可以简单的理解为表结构。MDL加锁过程是系统自动控制,无需显式使用,在访问一张表的时候会自动加上。MDL锁主要作用是维护表元数据的数据一致性,在表上有活动事务的时候,不可以对元数据进行写入操作,即表中存在未提交的事务,我们不能修改表结构

        元数据表存在的作用是为了避免DML与DDL冲突,保证读写的正确性。

举个例子来说,当表中有事务正在进行select语句,这时我们无法执行alter table..语句修改表结构,因为在执行select语句时,系统会自动为我们加上SHARED_READ锁,与EXCLUSIVE是互斥的,等到事务提交后,才可以进行表结构的修改。反过来也是如此。

3.意向锁

1.小例子引入意向锁

这里我们先不介绍意向锁的概念,我们先来看一个例子

这里是一个并发执行的情况,含两个线程A,B。首先A线程开启事务,并对第三行语句进行修改操作,当然也会自动对这一行语句加上行锁。而线程B并发访问这张表,想要锁住这张表。这个时候问题来了,第三行数据已经有了一个行锁了,而现在又要对这张表加一个表锁,会发生冲突。那要怎么办呢?那么B线程会一行数据一行的检查,检查这张表中有没有加入行锁,以及行锁的类型,决定表锁能不能添加成功,其实这样一行行的检查效率就比较低了。

那么为了解决这个问题,我们这时引入了意向锁。

2.意向锁

为了避免DML在执行时,加的行锁与表锁的冲突,在InnoDB中引入了意向锁,使得表锁不用检查每行数据是否加锁,使用意向锁来减少表锁的检查。从而提高了性能。

那么有了意向锁之后,执行的过程变成什么样了,我们一起来看看。

首先A线程的前面操作不变,区别的是,在加完行锁后,同时会为这张表加上一个意向锁。此时,B线程想为表加锁时,它就不需要像上面的情况一样去一行行的检查数据,而是去检查它加的锁是否与意向锁兼容(不要急,后面有判断兼容的情况),如果兼容就加锁,若不兼容,B线程会进入阻塞状态,那什么时候好呢?等到A线程提交了事务后就好了。

3.意向锁分类:

意向共享锁(IS):由语句 select ...lock in share mode添加。
意向排他锁(IX):由insert、update、delete、select...for update 添加。

4.意向锁兼容情况

意向共享锁(IS):与表锁共享锁(read)兼容,与表锁排它锁(write)互斥
意向排他锁(IX):与表锁共享锁(read)及排它锁(write)都互斥。意向锁之间不会互斥

四.行级锁

1.行级锁介绍

行级锁,每次操作锁住对应的行数据。锁定粒度最小,发生锁冲突的概率最低,并发度最高。应用在InnoDB存储引擎中。
InnoDB的数据是基于索引组织的,行锁是通过对索引上的索引项加锁来实现的,而不是对记录加的锁。对于行级锁,主要分为以下三类:

  • 行锁(Record Lock):锁定单个行记录的锁,防止其他事务对此行进行update和delete。在RC、RR隔离级别下都支持。

  • 间隙锁(GapLock):锁定索引记录间隙(不含该记录),确保索引记录间隙不变,防止其他事务在这个间隙进行inser,产生幻读。在RR隔离级别下都支持。

  • 临键锁(Next-Key Lock):行锁和间隙锁组合,同时锁住数据,并锁住数据前面的间隙Gap。在RR隔离级别下支持。

2.行锁

InnoDB实现了以下两种类型的行锁:
共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排它锁。
排他锁(X):允许获取排他锁的事务更新数据,阻止其他事务获得相同数据集的共享锁和排他锁。

在进行增删改查时,所加的行锁类型

举个小例子:

默认情况下,InnoDB在 REPEATABLE READ事务隔离级别运行,InnoDB使用 next-key 锁进行搜索和索引扫描,以防止幻读。
1.针对唯一索引进行检索时,对已存在的记录进行等值匹配时,将会自动优化为行锁。

针对这一条,比较好理解,比如说针对id查询。

2.InnoDB的行锁是针对于索引加的锁,不通过索引条件检索数据,那么nnoDB将对表中的所有记录加锁,此时 就会升级为表锁。

对于这一条,举个例子方便理解:

这里我们有一张简单的表

idnameage
1java1
3java3
5rose8

这时我们执行一条语句:

begin
update stu set name = 'lei' where name = 'lily'

当我们在另一个客户端执行一条语句:

update stu set name = 'PHP' where id = 3

你会发现阻塞,这是为什么呢?这就是因为InnoDB的行锁是针对于索引加的锁,不通过索引条件检索数据,name现在就是没有索引的,那么InnoDB将对表中的所有记录加锁,此时就会升级为表锁,当然也就无法执行了。

3.间隙锁和临键锁

在行级锁中加锁规则比较复杂,不同的场景,加锁的形式还不同(烦人的很)。这里我自己整理了一下,方便阅读。

首先我们先简单回顾一下:Innodb支持的三种行锁方式

InnoDB默认加锁方式是next-key。如果某个加锁操作未使用到索引,该锁则会退化为表锁。

  • 行锁 (Record Lock):存在唯一索引中 (包含主键索引),锁是在加索引上而不是行上的,也就是key。innodb一定存在聚簇索引,因此行锁最终都会落到聚簇索引上。

  • 间隙锁 (Gap Lock):存在非唯一索引中,锁定索引记录间隙,确保索引记录的间隙不变。

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

    (1)防止间隙内有新数据被插入
    (2)防止已存在的数据,更新成间隙内的数据

  • 临键锁 (Next-Key Lock) :一种特殊的间隙锁 = 行锁+间隙锁,除了锁住记录本身,还会锁住索引之间的间隙,即锁定一段左开右闭的索引区间

默认情况下,InnoDB在REPEATABLE READ事务隔离级别运行,InnoDB使用next-key锁进行搜索和索引扫描,以防止幻读。
1.索引上的等值查询(唯一索引),给不存在的记录加锁时,会变为间隙锁

 举个例子,这里有一张表:

 执行一条语句,更新一条并不存在的数据,表中并没有这条数据,那么就会在3到8中间添加间隙锁。

 我们查看锁的情况也可以看出,在id = 8之前,也就是3到8加了间隙锁。

 所以当我们想在3到8插入数据时,无法插入数据。

2.索引上的等值查询(普通索引),向右遍历时最后一个值不满足查询需求时,next-keylock 退化为间隙锁。

 还是那张表,我们首先创建了一个普通索引age。

 执行查询语句(查询的age普通索引哦)并加上了共享锁。

 我们查看锁的情况,我们发现:

首先(看红色方框的第一行有个3,3)为id = 3 这一行加上了临键锁(左开右闭),同时为(7,7)也就是id = 7之前,3到7这一段加上了间隙锁。也可以理解为对id= 3这一行数据左开右开都加了锁。

3.索引上的范围查询(唯一索引)——会访问到不满足条件的第一个值为止。

执行范围查询语句

 对19这一条数据加上了一个行锁,对25加上了临键锁以及25到正无穷大加了一个临键锁。

这里我们一起来总结一下

那么何时使用行锁,何时产生间隙锁?何时产生临键锁呢?

  • 使用唯一索引查询,而且只锁定一条记录时,InnoDB会使用行锁。

  • 使用唯一索引查询,但是检索条件是范围检索,或者是唯一检索但检索结果不存在(试图锁住不存在的数据)时,会产生 Next-Key Lock(临键锁)。

  • 使用普通索引检索时,不管是何种查询,只要加锁,都会产生间隙锁(Gap Lock)。

  • 同时使用唯一索引和普通索引时,由于数据行是优先根据普通索引排序,再根据唯一索引排序,所以也会产生间隙锁。

在上面都可以找到相对的例子进行对照。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值