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环境,尽量不要碰手动表锁,优先用事务+行锁解决并发问题。

相关推荐
城管不管5 小时前
重生——第十一次面试之挖财一面2026.8.19已OC
java·服务器·jvm·数据库·spring·面试·职场和发展
倔强的石头1065 小时前
向量数据库从相似度检索走向融合数据底座
数据库
今天AI了吗6 小时前
Python 基础语法(一):常量、变量、输入输出与运算符
开发语言·数据库·人工智能·python·sql·深度学习·机器学习
夜雪一千7 小时前
MySQL 全局锁是什么?原理、风险、备份踩坑完整实战
android·mysql·adb
lilian2337 小时前
Harmony os 技术实战|拼豆制图27:用单字符编码承载 50 张 70×70 图纸
前端·数据库·华为·harmonyos
皮卡丘不断更8 小时前
手机优先的 Personal Ledger:把账目、学习和复盘放到同一个入口
数据库·sqlite·fastapi·开源项目·个人效率
保卫大狮兄9 小时前
设备管理从台账到报废,完整生命周期一次讲清
数据库·设备管理·设备
数据安全技术观察9 小时前
数据动态脱敏:让脱敏策略跟着数据目录走
大数据·网络·数据库
小席是个热心肠10 小时前
Redis的自我学习
数据库·redis·学习
2601_9657984710 小时前
Salient Theme Setup Guide for Fast Creative WordPress Sites
数据库·web3·php·wordpress