mysql行锁由innodb引擎实现,基于索引加锁;无索引时退化为表锁;java仅通过sql指令间接控制,关键取决于存储引擎、sql写法(如for update)及是否命中索引。

Java 应用里不直接“分”行锁或表锁,而是通过 SQL 语句和数据库引擎行为间接控制——真正决定锁类型的是 MySQL 服务端,Java 只负责发指令。关键看三点:用的什么存储引擎、写的什么 SQL、有没有走索引。
看存储引擎:InnoDB 支持行锁,MyISAM 只能表锁
InnoDB 是 MySQL 默认引擎,支持行级锁(如 SELECT ... FOR UPDATE),也支持隐式/显式表锁(如 LOCK TABLES)。MyISAM 不支持行锁,所有 DML 操作都会自动加表锁——哪怕只更新一行,整张表都不可写。
- 查引擎:执行
SHOW CREATE TABLE your_table,看ENGINE=InnoDB还是ENGINE=MyISAM - Java 中建表时指定引擎:
ENGINE=InnoDB才有行锁能力 - Spring Boot + JPA/Hibernate 默认用 InnoDB,但需确认配置未被覆盖
看 SQL 写法:FOR UPDATE 和 LOCK IN SHARE MODE 触发行锁
在事务中执行带锁的 SELECT,InnoDB 才会加行级排他锁或共享锁:
-
SELECT * FROM user WHERE id = 100 FOR UPDATE;→ 加 X 锁(排他锁),其他事务不能改/删这行,也不能再加 FOR UPDATE -
SELECT * FROM user WHERE id = 100 LOCK IN SHARE MODE;→ 加 S 锁(共享锁),其他事务可读但不能改/删 - 普通
SELECT在 RR/RC 隔离级别下默认不加锁(快照读),只有显式加锁才生效
看是否命中索引:没索引 = 自动升为表锁
InnoDB 行锁是“锁索引”,不是锁数据行本身。如果 WHERE 条件没走索引(比如对无索引字段查询、或用了函数、或 like '%abc'),就会全表扫描,此时行锁失效,升级为表锁。
- 例如:
UPDATE user SET name='x' WHERE phone='138...';—— 若phone没建索引,整个表被锁 - 验证方式:执行
EXPLAIN SELECT ... FOR UPDATE,看key列是否非 NULL,type是否为const/ref - Java 侧要配合 DBA 做索引评审,不能只靠代码逻辑判断“应该只改一行”
显式表锁:Java 调用 LOCK TABLES 属于强制干预
除非必要(如批量重建索引、导出前冻结表),一般不建议在业务代码里用表锁。它会阻塞所有并发访问,且必须配对使用:
-
LOCK TABLES user WRITE;→ Java 中用Statement.execute("LOCK TABLES user WRITE") - 后续只能由当前连接执行读写,其他连接全部等待
- 必须调用
UNLOCK TABLES,否则锁一直持有(连接断开会自动释放,但不可依赖) - 注意:InnoDB 表上用
LOCK TABLES会禁用事务自动提交,且与 MVCC 冲突,慎用
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











