MySQL 表读锁(S锁)详解:LOCK TABLES … READ 原理、行为、坑点实战

前言

MySQL锁体系里面,表级锁分为表读锁(S,共享表锁) 和表写锁(X,排他表锁) 。

很多人会混淆:行级S共享锁 LOCK IN SHARE MODE 和表级S读锁。二者完全不是一个东西:

  • 行S锁:InnoDB行锁,锁住某几行数据;
  • 表S读锁:LOCK TABLES xxx READ,锁住整张数据表,属于表粒度锁,MyISAM原生大量使用,InnoDB也支持。

注意:LOCK TABLES 是MySQL原生表锁语法,InnoDB虽然兼容,但是会破坏InnoDB事务机制,生产尽量避免手动使用。

一、什么是表读锁 S(Shared Table Read Lock)

表读锁,共享锁(Shared Lock),简称S表锁。

语法:

sql 复制代码
LOCK TABLES `t_user` READ;

给整张 t_user 表加上表级共享读锁。

核心特性:共享,多个会话可以同时加S读锁。

加锁成功之后权限规则

  1. 当前持有锁的会话
  • ✅可以执行普通SELECT查询本表;
  • ❌不能对本表做 INSERT / UPDATE / DELETE 写入;
  • ❌不能执行 DDL(alter、drop、truncate);
  • ✅可以访问其他没有被锁定的表。
  1. 其他会话(别的连接)
  • ✅可以正常SELECT读取这张表;
  • ❌所有写入操作 insert/update/delete 会被阻塞排队等待;
  • ❌DDL语句同样被阻塞;
  • ✅其他未锁定的表不受任何影响。

释放锁两种方式:

sql 复制代码
-- 手动释放当前会话所有表锁
UNLOCK TABLES;

-- 会话断开,自动释放所有表锁

重点:LOCK TABLES READ,锁只作用于指定的这一张(或多张)表,不会影响数据库中其余表,这点和全局锁FTWRL有本质区别。

二、锁兼容性矩阵(表级别)

表级S读锁、X写锁之间的兼容关系:

已经持有 请求S表读锁 请求X表写锁
S表读锁(读锁) ✅兼容 ❌互斥阻塞
X表写锁(写锁) ❌互斥阻塞 ❌互斥阻塞

解读:

  1. 多个会话可以同时给同一张表加 S读锁,读和读互相兼容;
  2. 只要存在任意一个S读锁,所有会话想要加X写锁全部阻塞;
  3. 只要存在X写锁,任何人不能再加S读锁。

通俗理解:很多人可以一起"读上锁",只要有一个读锁在,任何人都不能写。

三、MyISAM下的表S锁行为

MyISAM引擎不支持事务,自动使用表锁。

当MyISAM执行查询SELECT,会自动拿S表读锁;执行DML写入自动拿X表写锁。

  • MyISAM:select自动S表锁,多个select并发没问题;
  • 一旦有写入,X写锁到来,所有后续select、写入全部等待。

所以MyISAM读多写少还可以;一旦写请求多,全部阻塞,并发性能很差。

现在业务基本淘汰MyISAM。

四、InnoDB 使用 LOCK TABLES READ 巨大坑点

⚠️InnoDB是支持LOCK TABLES语法,但是会绕过InnoDB行锁、破坏事务,很多人踩大坑。

坑1:LOCK TABLES 会提交当前事务

执行 LOCK TABLES t READ; 的瞬间,会隐式提交当前正在运行的事务。事务还没做完就被强制commit,数据一致性直接破坏。

sql 复制代码
BEGIN;
UPDATE t_user SET name='A' WHERE id=1;

-- 执行这一句,上面的事务直接被隐式提交!!
LOCK TABLES t_user READ;

不要在InnoDB事务中途使用LOCK TABLES,事务直接被意外提交。

坑2:LOCK TABLES之后,不能感知MVCC,也不会触发行锁

当执行 LOCK TABLES t READ,InnoDB不再使用行锁,直接整张表锁住。

即使你只想要锁定部分行,也会锁全表,失去InnoDB行锁优势。

坑3:区分【表S读锁】和【行S共享锁 LOCK IN SHARE MODE】

这两个非常容易混淆,完全两码事。

1)表级S读锁

sql 复制代码
LOCK TABLES t_user READ;

锁整张表,禁止所有人写入,破坏InnoDB事务。

2)行级S共享锁(InnoDB行锁)

sql 复制代码
SELECT * FROM t_user WHERE id=1 LOCK IN SHARE MODE;

只锁住id=1这一行记录,其他行不受影响,属于行锁,事务内生效,不会提交事务。

对比项 表S读锁 LOCK TABLES READ 行S锁 LOCK IN SHARE MODE
锁粒度 整张数据表 匹配索引的行记录
存储引擎 MyISAM、InnoDB都支持 仅InnoDB
事务影响 执行即隐式提交事务 事务内使用,不自动提交
锁范围 全表,所有行无法写入 仅命中行,其余行正常读写

绝大多数并发业务,我们需要的是行S锁,而不是表S读锁。

五、实操演示,复现表读锁阻塞现象

准备两个会话 Session A、Session B。

步骤1:Session A 加表读锁

sql 复制代码
LOCK TABLES t_user READ;

A会话可以正常select本表;A会话不能写本表。

步骤2:Session B 查询本表

sql 复制代码
SELECT * FROM t_user;

✅正常执行,S读锁之间兼容。

步骤3:Session B 执行写入

sql 复制代码
UPDATE t_user SET name='test' WHERE id=1;

❌语句被阻塞,处于等待锁状态。

show processlist看到状态:Waiting for table lock。

步骤4:Session A释放锁

sql 复制代码
UNLOCK TABLES;

释放之后,Session B的update立刻执行。

现象总结:只要表存在任意S读锁,全部DML写入阻塞。

六、表读锁S 和 全局锁 FTWRL 的区别

很多人把二者混淆:

  1. LOCK TABLES t READ:单张表加S读锁,其他库其他表完全不受影响。
  2. FLUSH TABLES WITH READ LOCK FTWRL全局锁:实例所有数据库全部表统一加上读锁,范围是整个MySQL实例。

七、什么场景才适合手动使用 LOCK TABLES READ

  1. 遗留MyISAM表,做简单的只读导出,临时锁住单张表防止变更;
  2. 极少数运维场景,需要冻结某一张表写入,其他表继续业务;

❗InnoDB业务系统,尽量不要手动写LOCK TABLES READ。

InnoDB想要"读的时候阻止别人修改",优先使用事务 + LOCK IN SHARE MODE行锁,而不是表S锁。

八、排查表锁等待

sql 复制代码
show processlist;

状态出现 Waiting for table lock,代表线程正在等待表级锁(S/X)。

注意区分:

  • Waiting for table lock:表锁;
  • Waiting for X lock:InnoDB行锁等待。

找到持有锁的会话,执行 kill 会话id,会话断开,表锁自动释放。

九、开发避坑规范

  1. InnoDB业务,禁止手动使用 LOCK TABLES xxx READ;会隐式提交事务,锁全表,抛弃行锁能力。
  2. 如果想要"读取同时防止别人修改该行",使用行共享锁:select ... lock in share mode,放在事务中。
  3. MyISAM引擎才会大量出现表S锁;新项目不要使用MyISAM。
  4. LOCK TABLES READ 只会锁定指定表,不等于全局锁FTWRL。
  5. 用完表锁必须执行UNLOCK TABLES,或者关闭会话,否则写入一直阻塞。
  6. 不要混淆表S读锁和行S共享锁,二者粒度、行为完全不一样。

十、常见问题问答

Q1:加了表READ锁之后,加锁的会话自己能不能写这张表?

A:不能。哪怕是自己,也不能insert/update,只能select。

Q2:多个会话同时执行 LOCK TABLES t READ,会阻塞吗?

A:不会,S读锁共享,多个会话可以同时持有。只要有一个S锁,所有写操作就卡住。

Q3:BEGIN事务里面执行LOCK TABLES READ,事务还生效吗?

A:不生效。LOCK TABLES会隐式commit当前事务。

Q4:InnoDB不手动执行LOCK TABLES,会不会自动产生表S读锁?

A:正常DML、SELECT不会自动产生表S锁;只有DDL、部分特殊操作会触发表级锁逻辑。

总结

  1. LOCK TABLES table READ 是表级S共享读锁,粒度整张表,多个会话可同时加读锁;读与读兼容,读与写互斥。
  2. 持有S表读锁期间:所有会话都无法执行本表DML/DDL写入;仅SELECT查询放行。
  3. 表S读锁主要服务MyISAM;InnoDB兼容该语法,但会隐式提交事务,破坏行锁与MVCC,业务尽量避开。
  4. 区分开表S读锁 和 行S锁 LOCK IN SHARE MODE,前者锁整张表,后者只锁行。
  5. 释放:UNLOCK TABLES 或者断开会话。
  6. 等待状态 Waiting for table lock 代表在等待表锁,kill持有锁的会话快速恢复业务。

核心一句话:InnoDB环境,尽量不要碰手动表锁,优先用事务+行锁解决并发问题。

相关推荐
李兆龙的博客2 小时前
问津集 #26:Lakebase——Postgres 的版本化页面存储、数据库分支与计算弹性
数据库
倔强的石头_4 小时前
聊聊金仓KFS:一款把数据同步软件做扎实的产品
数据库
闲云野鹤在人间4 小时前
MySQL|从理论、安装、备份到主从复制、MHA高可用详解
linux·运维·数据库·mysql·云计算
禾小西5 小时前
Redis:从两大维度和三大主线建立知识体系
数据库·redis·缓存
禾小西5 小时前
Redis 数据结构:快速的 Redis 有哪些慢操作?
数据结构·数据库·redis
数据库小学妹6 小时前
数据共享交换平台选型:交换方式对比与避坑指南
数据库·信创·数据同步·数据交换平台·政务数据共享·数据共享交换平台·数据库底座
要开心吖ZSH6 小时前
MySQL 慢 SQL 排查操作手册-个人笔记版
java·笔记·sql·mysql·慢查询
loong_XL7 小时前
决策模型做内容安全检测:从踩坑到上线
数据库·安全·jev·决策模型
这个DBA有点耶7 小时前
数据库双轨并行实战:全量并行策略、增量延迟控制、双向回切与一致性校验
数据库·架构·dba
Gl�ria8 小时前
MySQL 单机版 vs 高可用版:宕机排查 + 故障处理
mysql·adb·架构