在MySQL的并发控制体系中,表锁与行锁是两种最基础的锁粒度。表锁作用于整张数据表,行锁则只锁定具体的数据行。它们由不同的存储引擎以不同机制实现,适用的业务负载特征也完全不同。错误地选择锁类型,轻则导致请求排队变慢,重则引发大面积阻塞甚至死锁,因此理清二者的应用场景是数据库设计的基本功。

一、表锁的机制与典型应用场景
表锁是MySQL中粒度最大的锁,分为读锁(共享锁)和写锁(排他锁)。MyISAM存储引擎默认使用表级锁,InnoDB在某些特定操作(如ALTER TABLE)时也会退化到表锁。当线程对表加读锁后,其他线程可读但不可写;加写锁后,其他线程的读写全部阻塞。表锁的开销很小,加锁和释放锁的速度极快,不需要像行锁那样维护复杂的锁链表。
表锁最适合低频、大批量的操作场景。例如每天凌晨对日志表做全表归档,或者运营后台手动执行一次性数据清洗。这类操作本身就要扫描或改动绝大多数行,如果用行锁反而会因锁数量过多拖慢性能。另外,在明确知道业务并发写压力极低、且以读为主的简单系统中,使用MyISAM加表锁也能降低复杂度。
下面是手动给InnoDB表加表锁的示例,通过LOCK TABLES语句显式控制:
-- 给user表加写锁,当前会话可读写,其他会话阻塞 LOCK TABLES user WRITE; -- 执行批量更新 UPDATE user SET status = 0 WHERE create_time < '2020-01-01'; -- 释放表锁 UNLOCK TABLES;
需要注意的是,使用LOCK TABLES后,当前会话只能访问被锁的表,且InnoDB下的表锁与事务机制容易混淆,一般建议尽量依赖存储引擎自身锁而非手动表锁。表锁的最大弱点是并发度差,只要有一个写锁,整张表对外不可用。
二、行锁的机制与典型应用场景
行锁是InnoDB的核心特性之一,它基于索引实现,只锁定被访问的具体记录。行锁同样分共享锁(S锁)和排他锁(X锁),并且配合MVCC多版本控制,使得读不阻塞写、写不阻塞读(快照读)。行锁的并发能力远高于表锁,多个事务可以并行修改同一张表的不同行。
行锁广泛存在于高并发OLTP系统中,比如电商的库存扣减、社交平台的消息写入、支付流水记录等。这些场景每次只涉及少数几行,且请求并发量大。通过行锁,不同用户下单时只锁自己的商品记录,彼此不受影响。但行锁有一个严格要求:更新或删除必须命中索引,否则InnoDB会锁住全部行,等同于表锁。
以下代码展示了在事务中使用行锁进行安全的库存扣减:
-- 开启事务 START TRANSACTION; -- 对指定商品行加排他锁,防止超卖 SELECT stock FROM product WHERE id = 100 FOR UPDATE; -- 判断库存后更新 UPDATE product SET stock = stock - 1 WHERE id = 100; -- 提交释放行锁 COMMIT;
行锁虽然并发好,但加锁和死锁检测有额外开销。当多个事务以不同顺序锁定多行时,容易出现死锁,InnoDB会主动回滚其中一个事务。因此写并发高的业务要统一访问顺序、缩小事务范围。
三、表锁与行锁的对比与选择原则
从并发度看,表锁几乎串行化,行锁支持高并发;从加锁开销看,表锁极轻,行锁需维护锁记录与索引关联;从死锁概率看,表锁基本无死锁,行锁较常见。可以用下表概括:
| 对比维度 | 表锁 | 行锁 |
|---|---|---|
| 锁定范围 | 整张表 | 索引命中行 |
| 并发性能 | 低 | 高 |
| 加锁速度 | 快 | 相对慢 |
| 典型引擎 | MyISAM | InnoDB |
| 适用操作 | 全表批量处理 | 单行高频事务 |
选择原则很直接:如果操作覆盖表中大部分数据,或者表很小且并发写少,用表锁或MyISAM更省心;如果是核心交易链路、需要多用户同时改不同记录,必须用InnoDB行锁并建好索引。另外,DDL变更表结构时无论引擎都会触及表级锁,应在低峰期执行。
实践中也可以混合思路,比如先用行锁处理细粒度业务,再通过定时任务在低峰用表级批量整理。关键是根据读写比例、数据量和并发数做权衡,而不是盲目追求细粒度。
四、常见误区与避坑建议
一个典型误区是认为InnoDB永远只用行锁。实际上,当SQL语句未走索引,如WHERE name LIKE '%abc'导致全表扫描时,InnoDB只能锁所有行,现象和表锁一致。另一个误区是在事务里混用LOCK TABLES,这会破坏InnoDB的事务隔离,造成难以排查的阻塞。
建议线上系统统一使用InnoDB,通过慢查询日志观察是否出现全表锁行;为高频更新字段建索引;控制事务长度,避免长事务占着行锁不放。对于报表类全表统计,可以落到只读从库,或用表锁隔离,不影响主库写入。
锁的选择没有绝对优劣,只有是否匹配场景。理清业务模型,才能让MySQL既稳又快。