当前位置: 首页 > article >正文

MySQL数据库的锁

一、锁(Lock)

1. 概念

数据库锁是数据库管理系统中用来管理对数据库对象(如行、页或表)的并发访问的机制。

其主要目的是确保数据的完整性和一致性,同时允许合理的并发操作。

数据库锁可以防止多个事务同时修改同一数据,从而避免数据竞争条件和不一致性。

2. MySQL锁的分类

MySQL的锁可以根据不同的标准进行分类,以下是一些常见的分类方式:

  1. 按锁定粒度分类

    • 全局锁:锁定整个数据库实例,用于全库备份等操作。

    • 表级锁:锁定整个表。适用于MyISAM等不支持行级锁的存储引擎。包括元数据锁(Metadata Locks, MDL)、表锁(Table Lock)

    • 行级锁: 锁定单个数据行,是最细粒度的锁。行级锁包括记录锁(Record Lock)、间隙锁(Gap Lock)、临键锁(Next Key Lock)。

    • 页级锁: 锁定数据库页,介于行级锁和表级锁之间,InnoDB存储引擎使用。

  2. 按锁定类型分类

    • 共享锁(Shared Locks):允许多个事务读取同一数据,但不能修改。
    • 排他锁(Exclusive Locks):允许事务读取并修改数据,但不允许其他事务访问。
  3. 按锁定意图分类

    • 意向共享锁(Intention Shared Lock, IS):表明事务想要在更细粒度上加共享锁。

    • 意向排他锁(Intention Exclusive Lock, IX):表明事务想要在更细粒度上加排他锁。

  4. 按锁定模式分类

    • 悲观锁:假设会发生冲突,事务在操作前先锁定数据。

    • 乐观锁:假设不会发生冲突,事务在提交时检查是否发生了冲突。

这些分类并不是完全独立的,一个锁可能同时属于多个分类。例如,行级锁可以是共享锁或排他锁,也可以是意向锁的一部分。了解这些锁的分类有助于更好地理解MySQL的并发控制机制和性能调优。

二、锁定类型分类的锁

1. 共享锁(Shared Locks)

共享锁(Shared Locks)又称读锁(Read Locks),是一种允许多个事务同时读取同一数据资源的锁。当数据被加上共享锁时,其他事务也可以获取该数据的共享锁并读取它,但不允许修改数据。简而言之,共享锁保证了对同一个数据,多个读操作可以同时进行,互不干扰。
共享锁创建语句:
对表(表级)创建共享锁:

LOCK TABLE table_name READ;

(表级)解锁语句:

UNLOCK TABLES;

对读取某些记录(行级)加共享锁

BEGIN;
SELECT column1, column2, ... FROM table_name WHERE condition LOCK IN SHARE MODE;

(行级)事务提交后自动解锁

2. 排它锁(Exclusive Locks)

排它锁(Exclusive Locks),也称为写锁(Write Locks)。当事务对某个数据对象加上排它锁时,它可以独占该数据对象,进行读取和修改操作,而其他事务则不能同时对同一数据对象加任何类型的锁,直到排它锁被释放。简而言之,排它锁保证了在任何时刻,只有一个事务能够对特定数据进行写操作。
排他锁创建语句:
对表(表级)创建排他锁:

LOCK TABLE table_name WRITE;

(表级)解锁语句:

UNLOCK TABLES;

对读取某些记录(行级)加排他锁

BEGIN;
SELECT column1, column2, ... FROM table_name WHERE condition FOR UPDATE;

对操作的记录会自动加排他锁

BEGIN;
UPDATE TABLE table_name SET column1 = value1, column2 = value2, ...  WHERE condition; 
BEGIN;
DELETE FROM table_name WHERE condition;
BEGIN;
INSERT INTO table_name (column1, column2, ...) VALUES (value1, value2, ...);

事务提交后自动解锁

总结:

排它锁(X锁)共享锁(S锁)
排它锁(X锁)不兼容不兼容
共享锁(S锁)不兼容兼容

三、锁定粒度分类的锁

1. 全局锁(Global Lock)

全局锁是锁定整个数据库实例。

主要用于全库备份等操作,因为全局锁可以使数据库在备份期间防止数据或表结构的更新,而出现备份文件的数据与预期的不一样的情况。

全局锁的执行语句

FLUSH TABLES WITH READ LOCK;

执行后,整个数据库就处于只读状态了,这时其他线程执行对数据的增删改、对表结构的更改操作,都会被阻塞。

释放全局锁的语句

UNLOCK TABLES;

2. 表级锁(Table Level Lock)

表级锁是锁定整个表的锁。适用于MyISAM等不支持行级锁的存储引擎。

(1)表锁(Table Lock)

表锁可以有共享锁也可以有排它锁。

表锁的创建语句如下:

表级别的共享锁,也就是读锁;允许当前会话读取被锁定的表,但阻止其他会话对这些表进行写操作。

LOCK TABLES table_name READ; 

表级别的独占锁,也就是写锁;允许当前会话对表进行读写操作,但阻止其他会话对这些表进行任何操作(读或写)。

LOCK TABLES table_name WRITE;

也可以在一条语句中完成对多个表的读写锁操作

LOCK TABLES table_name1 READ, table_name2 WRITE;

解除表锁的语句如下(当会话退出后,也会释放所有表锁):

UNLOCK TABLES;

表锁除了会限制别的线程的读写外,也会限制本线程接下来的读写操作。

例如:当线程1 对 表t1 进行加读锁操作后,那么此线程接下来只能对 表t1 进行读操作,不可以进行写操作,同时也不可以对其他表进行任何操作。直到 线程1 释放对 表t1 的锁。

(2)元数据锁(Metadata Locks, MDL)

元数据锁用于保护表的元数据,防止在执行DDL操作时被并发修改。

元数据锁保证当用户对表执行增删改查操作时,防止其他线程对这个表结构做了变更。

对数据库表进行操作时,会自动给这个表加上元数据锁。所以不需要额外的创建语句。
例如,对一张表进行增删改查操作的时候,会自动加元数据读锁;对一张表进行表结构变更操作的时候,会自动加元数据写锁

(3)自增锁(Auto-Increment Lock)

自增锁(Auto-Increment Lock)是MySQL中用于管理自增字段的一种特殊锁机制。自增字段在插入新记录时自动生成唯一标识,广泛应用于主键或唯一索引的生成。

自增锁是一种表级锁(Table Level Lock),专门针对插入 自增(AUTO_INCREMENT) 类型的列。它确保在并发插入操作中,自增字段的值不会发生冲突,从而保证数据的唯一性和一致性。

自增锁的工作原理如下:

当执行插入操作时,MySQL会自动获取自增锁,生成下一个自增值,并在插入完成后释放锁。这一过程确保了即使在多线程环境下,自增字段的值也不会重复。

自增锁是数据表自动创建的。所以不需要额外的创建语句。

3. 行级锁(Row Level Lock)

锁定单个数据行,InnoDB存储引擎支持行级锁,这是最细粒度的锁。

MyISAM 引擎并不支持行级锁。

(1)记录锁(Record Lock)

记录锁,顾名思义就是仅仅把一条记录加上锁。

记录锁的主要作用是 在事务修改某行数据时防止其他事务同时修改这条记录,确保数据的原子性和一致性。

可以有共享锁也可以有排它锁。

对读取某些记录加共享锁

BEGIN;
SELECT column1, column2, ... FROM table_name WHERE condition LOCK IN SHARE MODE;

对读取某些记录加排他锁

BEGIN;
SELECT column1, column2, ... FROM table_name WHERE condition FOR UPDATE;

同时update、delete、insert操作也会对操作的行记录自动添加排它锁。

BEGIN;
UPDATE TABLE table_name SET column1 = value1, column2 = value2, ...  WHERE condition; 
BEGIN;
DELETE FROM table_name WHERE condition;
BEGIN;
INSERT INTO table_name (column1, column2, ...) VALUES (value1, value2, ...);

当事务执行 commit 后,事务过程中生成的锁都会被释放。

例如:给user表中id为10的记录加上排他(X型)的记录锁。
语句如下

BEGIN;
SELECT id, name, age FROM user WHERE id = 10 FOR UPDATE;

表中主键 id = 10 的这条记录加上 X 型的记录锁,如果这时候其他事务对这条记录进行删除或者更新操作,那么这些操作都会被阻塞。
示意图如下:
在这里插入图片描述

(2)间隙锁(Gap Lock)

间隙锁,顾名思义就是锁定记录之间的“间隙”的锁,但是不包含记录本身。

间隙锁的主要作用是防止其他事务在当前事务已经查询的索引记录的间隙中插入新的记录,从而避免幻读现象

间隙锁在可重复读(REPEATABLE READ)隔离级别下,使用范围查询时隐式创建,不需要用户手动创建。

BEGIN;
SELECT column_name FROM table_name WHERE column_name < value1 column_name > value2 FOR UPDATE;

例如:可重复读(REPEATABLE READ)隔离级别下,给user表中id在 (7,10) 的间隙加上间隙锁。
语句如下

BEGIN;
SELECT id, name, age FROM user WHERE id > 7 AND id < 10 FOR UPDATE;

表中有一个范围 id 为 (7,10) 间隙锁,那么其他事务就无法插入 id = 8 和 id = 9 的记录了,这样就有效的防止幻读现象的发生。
示意图如下:
在这里插入图片描述

(3)临键锁(Next Key Lock)

临键锁,是记录锁(Record Lock)与间隙锁(Gap Lock)的组合,它锁定一个索引记录,并在该记录之前的间隙上加锁。

临键锁可以防止幻读,并且允许锁住一个记录以及该记录之前的间隙,以确保在读取记录的同时阻止其他事务在该记录之前插入新记录。

临键锁在可重复读隔离级别下,执行范围查询时隐式创建,不需要用户手动创建。

BEGIN;
SELECT column_name FROM table_name WHERE column_name BETWEEN value1 AND value2 FOR UPDATE;

例如:可重复读(REPEATABLE READ)隔离级别下,给user表中id在 (7,10] 的间隙和记录加上临键锁。
语句如下

BEGIN;
SELECT id, name, age FROM user WHERE id > 7 AND id <= 10 FOR UPDATE;

表中有一个范围 id 为 (7,10] 的临键锁,相当于有一个范围 id 为 (7,10) 间隙锁和一个 id = 10 的记录锁,那么其他事务既无法插入 id = 8 和 id = 9 的记录,又不能更改 id = 10 的记录。
示意图如下:
在这里插入图片描述

四、锁定意图分类的锁

1. 意向锁(Intention Locks)

意向锁(Intention Locks)是数据库系统中用于表示事务对数据行锁定意图的一种锁。它们是多粒度锁定协议的一部分,主要用于在行级锁和表级锁之间协调锁请求。意向锁分为两种类型:

  1. 意向共享锁(Intention Shared Lock,IS Lock)

    • 当事务想要对数据行加读锁(共享锁)时,首先需要在该行上设置意向共享锁。
    • 它表明事务有意向在将来对数据行加读锁。
  2. 意向排他锁(Intention Exclusive Lock,IX Lock)

    • 当事务想要对数据行加写锁(排他锁)时,首先需要在该行上设置意向排他锁。
    • 它表明事务有意向在将来对数据行加写锁。

意向锁的主要作用是:

  • 兼容性检查:在实际的读锁或写锁被加到数据行之前,意向锁用于检查是否存在锁兼容性问题。如果一个事务想要加读锁,它会先设置意向共享锁;如果一个事务想要加写锁,它会先设置意向排他锁。这样,其他事务在尝试加锁时,可以通过查看意向锁来判断是否能够兼容。

  • 避免死锁:意向锁可以帮助数据库系统预测潜在的死锁情况。如果一个事务已经设置了意向排他锁,其他事务就知道不能同时设置意向排他锁,从而避免死锁的发生。

  • 锁升级:意向锁允许事务在不释放当前锁的情况下升级锁的类型。例如,一个事务可以先设置意向共享锁,然后根据需要升级到排他锁。
    在MySQL数据库中,乐观锁和悲观锁是两种不同的并发控制策略,它们用于处理多用户环境下的数据一致性和完整性问题。这两种锁的主要区别在于它们对数据冲突的预期和处理方式。

五、锁定模式分类的锁

1.悲观锁(Pessimistic Locking)

悲观锁是一种保守的锁定策略,它假设在事务处理过程中会发生数据冲突。因此,悲观锁会在事务开始时就锁定数据,以防止其他事务修改这些数据。悲观锁通常用于写操作频繁的场景,或者当数据冲突的可能性较高时。

特点

  • 锁定数据:在事务开始时就锁定涉及的数据行或表,直到事务结束。
  • 减少冲突:通过锁定数据来减少数据冲突的可能性。
  • 性能影响:可能导致数据库性能下降,因为锁定数据会阻塞其他事务的访问。
  • 适用场景:适用于写操作频繁,或者数据冲突可能性高的场景。

2.乐观锁(Optimistic Locking)

乐观锁是一种宽松的锁定策略,它假设在事务处理过程中数据冲突的可能性较低。乐观锁不会在事务开始时锁定数据,而是在事务提交时检查数据是否被其他事务修改过。如果数据被修改过,事务会回滚并重试。

特点

  • 无锁定:在事务处理过程中不锁定数据,允许多个事务同时访问数据。
  • 冲突检测:在事务提交时检查数据版本或时间戳,以确定数据是否被其他事务修改。
  • 性能优势:通常比悲观锁有更好的性能,因为它减少了锁定资源的需求。
  • 适用场景:适用于读操作多于写操作,或者数据冲突可能性低的场景。

实现方式版本号机制时间戳机制

(1)版本号锁(Version Locks):

乐观锁通常通过在数据表中添加一个版本号字段来实现。每次数据更新时,版本号增加。事务在更新数据时检查版本号是否一致,如果不一致,则表示数据已被其他事务修改。

(2)时间戳锁(Timestamp Locks):

乐观锁也可以通过时间戳来实现。每个事务都有一个时间戳,事务在提交时检查自己的时间戳是否小于数据的最新时间戳,如果是,则表示数据未被其他事务修改。


http://www.kler.cn/a/460486.html

相关文章:

  • 41.5 nginx拦截prometheus查询请求使用lua脚本做promql的检查替换
  • LVGL 移植到 Arduino IDE(适用SP32 Arduino RP系列)
  • IM系统在体育直播网站中的重要性
  • 概率论与数理统计
  • 微服务面试题:分布式事务和服务监控
  • 【人工智能机器学习基础篇】——深入详解强化学习之常用算法Q-Learning与策略梯度,掌握智能体与环境的交互机制
  • ubuntu 使用samba与windows共享文件[注意权限配置]
  • 留学生该如何进行文学分析类的essay写作
  • 分析电控发动机常见故障原因
  • vue使用el-select下拉框自定义复选框
  • IDEA修改编译版本
  • [2025] 如何在 Windows 计算机上轻松越狱 IOS 设备
  • 什么是 GPT?Transformer 工作原理的动画展示
  • TP 钱包插件版本的使用
  • 假设与思想实验:我们能否编写具有感知基础的人工智能形式来保护人类?
  • 数据库中的锁应用
  • SwiftUI:多语言实现富文本插值
  • DeepSeek:AI 领域的新兴力量
  • phpIPAM容器化部署场景下从1.5.x更新到1.7.0提示禁用安装脚本配置的处理
  • Cesium 实战 27 - 三维视频融合(视频投影)
  • springMVC报错java版本
  • Python编程技术
  • python导出可执行文件
  • LangChain4j与Elasticsearch:构建高效的语义嵌入存储
  • 迁移SVN工程到GITLAB
  • 【Vim Masterclass 笔记03】S03L10 + S03L11:Vim 中的文本删除操作以及 Vim 思维习惯的培养(含 DIY 拓展知识点)