MySQL中的锁详解

1.概念

锁是计算机协调多个进程或者线程并发访问某一资源的机制。那么如何保证数据并发访问的一致性、有效性是数据库必须解决的一个问题,锁的冲突也是影响数据库并发访问性能的一个重要因素,所以数据库中锁的应用极为重要,其复杂度也更高。

锁的分类,以锁的颗粒度为三类:

  1. 全局锁:锁定数据库中的所有表。
  2. 表级锁:每次操作锁住整张表。
  3. 行级锁:每次操作都锁到对应行的数据。

接下来就分别对这几种锁做一个解释。

数据库锁是数据库管理系统用来管理对数据库中数据的并发访问的一种机制。锁的存在主要是为了保证数据的完整性和一致性,以及支持事务的原子性。以下是全局锁、表级锁和行级锁的详细解释:

1. 全局锁(Global Lock):

• 全局锁是一种锁定数据库中所有表的锁。当数据库处于只读模式时,通常使用全局锁。

• 在这种模式下,没有写操作被允许,只能执行读操作。这可以用于数据库的备份或维护期间,以防止数据被修改。

• 全局锁的范围是最广的,它锁定了整个数据库实例,所有试图进行写操作的事务都会被阻塞。

sql 复制代码
-- 全局锁,整个数据库处于只读状态,其他操作均阻塞
FLUSH TABLES WITH READ LOCK

-- 释放全局锁
UNLOCK TABLES

2. 表级锁(Table-Level Lock):

• 表级锁是锁定数据库中特定表的锁。当一个事务对表进行写操作(如 INSERT、UPDATE、DELETE)时,通常会请求表级锁。

• 表级锁会阻止其他事务对同一张表进行写操作,但仍然允许其他事务对这张表进行读操作,这取决于锁的类型(共享锁或排他锁)。

• 表级锁的粒度比全局锁小,它只锁定涉及的特定表,其他表可以被并发访问。

3. 行级锁(Row-Level Lock):

• 行级锁是锁定数据库表中特定行记录的锁。这种锁的粒度最细,只锁定事务操作影响到的具体行。

• 行级锁通常在执行精确的数据操作时使用,如 UPDATE 某个具体的行或 DELETE 某个具体的行。

共享锁和排他锁

  • 共享锁(S Lock):其他事务可读,但不可写
  • 排他锁(X Lock):其他事务不能读取也不能写

这两种行锁之间的兼容性如下:

共享锁和共享锁可以兼容,排他锁和其它锁都不兼容。例如,事务 A 获取了一行数据的共享锁,事务 B 可以立即获得该数据行的共享锁,也就是锁兼容;但是此时事务 B 如果想获得该数据行的排他锁,则必须等待事务 A 释数据行上的共享锁,此种情况存在锁冲突。

默认情况下,数据库中的锁都可以自动获取;但是也可以手动为数据进行加锁。我们来看一个示例,首先创建一个表:

sql 复制代码
create table t(
  id int auto_increment primary key,
  c1 int,
  c2 int,
  c3 int
);
create unique index idx_t_c1 on t(c1);
create index idx_t_c2 on t(c2);

insert into t(c1,c2,c3) values (1,1,1),(2,3,4),(3,6,9);

其中,id 是主键;c1 上创建了一个唯一索引;c2 上创建了一个非唯一索引;c3 上没有索引。

接下来的示例都使用 MySQL 默认的隔离级别 Repeatable Read,除非另有说明。

然后创建两个数据库连接 T1 和 T2,先在 T1 中锁定一行数据:

sql 复制代码
-- T1
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from t where id = 1 for share;
+----+------+------+------+
| id | c1   | c2   | c3   |
+----+------+------+------+
|  1 |    1 |    1 |    1 |
+----+------+------+------+
1 row in set (0.00 sec)

我们在事务中使用select ... for share语句获得了数据行 id = 1 上的共享锁;对于 MySQL 8.0 之前的版本,可以使用select ... lock in share mode命令。

由于 InnoDB 中的自动提交 autocommit 默认设置为 ON,我们必须在事务中为数据行加锁;或者将 autocommit 设置为 OFF。

然后在 T2 中执行以下语句:

sql 复制代码
-- T2
mysql> select * from t where id = 1 for share;
+----+------+------+------+
| id | c1   | c2   | c3   |
+----+------+------+------+
|  1 |    1 |    1 |    1 |
+----+------+------+------+
1 row in set (0.00 sec)

结果显示,在 T2 中成功获取改行数据上的共享锁。然后尝试获取排他锁:

sql 复制代码
-- T2
mysql> select * from t where id = 1 for update;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

使用select ... for update命令获取排他锁,此时该命令会一直处于等待状态并且最终超时。也就是说,共享锁和排他锁不兼容。

最后,在 T1 中提交或者回滚事务:

sql 复制代码
-- T1
mysql> commit;
相关推荐
SelectDB技术团队20 分钟前
快手基于 Apache Doris 千亿多模态检索的实践
数据库·数据分析·全文检索·快手·apache doris·向量索引·多模态检索
政企项目老覃26 分钟前
金融转账“幽灵失败“排查实录:从本地消息表到 Seata TCC 的选型与权衡
数据库·程序人生·性能优化·数据分析·系统架构
TLA技术27 分钟前
LogMiner vs 裸日志解析(三):Oracle日志解析中的“前镜像”和“后镜像”,到底怎么用?
数据库·oracle·flink·dba·迁移学习
IT邦德34 分钟前
BIC-QA如何重新定义数据库知识服务
运维·数据库
逐米时代1 小时前
BOM智能构建:全链路一致性自动校验
大数据·数据库·人工智能
SelectDB技术团队1 小时前
StarRocks 迁移至 Apache Doris 完整指南:三步完成结构、数据与业务平滑切换
数据库·人工智能·sql·clickhouse·apache doris·selectdb·湖仓架构升级
Gauss松鼠会1 小时前
GaussDB 系统表与系统视图详解(内网运维版)
运维·服务器·网络·数据库·gaussdb·经验总结
倔强的石头_1 小时前
OceanBaseVS金仓:同一条复杂 SQL,为什么架构选择会改变延迟曲线
数据库
+VX:Fegn08951 小时前
计算机毕业设计|基于springboot + vue图书借阅管理系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
李金虎_1 小时前
大模型搜索时代的 GEO 工程实践:从零搭建仿真环境到可控变量的完整复盘
数据库·机器学习