mysql中的锁

目录

什么是“锁”?

  • *锁(Lock)**是数据库为保证多用户并发访问数据时,数据的一致性与完整性而采取的一种机制。

就像你在一个文档上修改,防止别人同时修改冲突,给它上锁

数据库主要有两类锁:

分类 子类型 简要说明
物理层锁 表锁、行锁、页锁 作用于数据“位置”
逻辑层锁 乐观锁、悲观锁 作用于“逻辑业务”,一般由程序实现

✅ 1. 表锁(Table Lock)

  • 整张表加锁,其他线程无法读写
  • 粒度大,冲突概率高,MyISAM引擎用得多
LOCK TABLES user WRITE;

✅ 2. 行锁(Row Lock)

  • 只锁住当前操作的行
  • 粒度小,性能好,InnoDB默认支持
  • 举例:
UPDATE user SET name = '张三' WHERE id = 1;

✅ 3. 乐观锁(Optimistic Lock)

  • 假设没有冲突,不加锁
  • 提交前检查“版本”或“时间戳”
  • 冲突时提示失败
  • 一般通过字段控制,如:
UPDATE table SET version = version + 1
WHERE id = 1 AND version = 3;

✅ 4. 悲观锁(Pessimistic Lock)

  • 认为有冲突,加锁保护
  • 每次读取/写入都锁住数据(阻塞其他操作)
  • InnoDB默认是悲观锁模式,如:
SELECT * FROM user WHERE id = 1 FOR UPDATE;

🔁 四、UPDATE 语句的锁行为

情况分析(基于 InnoDB 引擎):

  • 使用索引可确保行锁,否则可能锁整张表
SQL语句 锁类型 说明
UPDATE … WHERE id=1 行锁 默认加排他锁(X锁),锁住符合条件的行
UPDATE … 没有索引 表锁(或间隙锁) InnoDB 尝试行锁失败时退化为表锁或意外加范围锁
事务外操作 自动提交 + 加锁 没有事务会立即提交释放锁
加 FOR UPDATE 显式加锁 同样是行锁,但常用于事务读取后锁定数据

为什么insert/update 不会锁整张表?

在使用 InnoDB 引擎 时,MySQL 默认采用的是 行级锁(Row Lock),这是它区别于 MyISAM 表锁的重要优势之一:

操作类型 加锁级别 说明
INSERT 行锁 / 无锁 通常不会锁已有数据,只是插入,几乎不会阻塞其他操作
UPDATE … WHERE id=1 行锁 会锁住满足条件的行(id=1),不会锁整张表
DELETE … WHERE id=1 行锁 同样只锁住目标行

⚠️ 但注意:

如果:

  • 没有使用索引 或
  • WHERE 语句无法命中索引

那 MySQL 可能会退化为表锁或间隙锁,这是性能隐患!

✅ 二、如果我此时在执行 DDL 操作(比如 ALTER TABLE)会发生什么?

🔒 DDL 操作(如 ALTER TABLE)一定会加表锁!

操作 锁类型 说明
ALTER TABLE … 表锁 会阻塞该表的读写,直到结构变更完成
ADD/DROP COLUMN 表锁 同上
CREATE INDEX(同步) 表锁 会阻塞写入操作(可考虑在线索引)

如果在执行:

ALTER TABLE user ADD COLUMN nickname VARCHAR(255);

此时其他连接中的以下操作会被 阻塞

SELECT * FROM user;       --  阻塞
UPDATE user SET name='a'; --  阻塞
INSERT INTO user ...      --  阻塞

🚫 在该 ALTER TABLE 执行期间:

  • 任何对这张表的 读操作(SELECT * FROM user)都会被阻塞
  • 任何对这张表的 写操作(UPDATE、INSERT、DELETE)也都会被阻塞

✅ 等ALTER TABLE完成后:

这些被阻塞的操作才会依次执行,或者超时失败(取决于客户端/连接池设置的超时时间)。

🔧 解决建议(生产环境):

  • 避免高峰期执行 DDL 操作
  • 使用在线DDL工具(如 Percona 的 pt-online-schema-change、GitHub 的 gh-ost),避免阻塞业务请求

DDL的排队

Data Definition Language

  • MySQL 的 DDL 操作本质上 需要排他访问表,也就是说:

    要等到当前表上没有其他事务正在访问或占锁

🔁 举个例子:

时间 操作 描述
T1 业务线程A执行 UPDATE user SET … WHERE id=1 并开启了事务,但没有提交 占用行锁
T2 你执行了 alembic upgrade,尝试 ALTER TABLE user ADD COLUMN xxx MySQL 尝试加表锁,发现表上还有事务未提交 → 被阻塞,卡住了
T3 业务线程A事务迟迟没提交 你这边就一直挂着
误解 实际机制
“DDL 最优先,其他操作应该等它” ❌ 错!DDL 也要等现有事务释放表相关锁
“表被频繁更新,DDL 就会失败” ✅ 是的,频繁操作造成的锁占用会阻塞 DDL
“表结构改不动是因为 MySQL 不稳定” ❌ 是因为 DDL 拿不到锁

✅ 你的 update 没写事务,为什么还是会“加锁”?

因为InnoDB 默认使用“自动提交模式”

也就是说,如果你没有显式写 BEGIN 或 START TRANSACTION,那么:

UPDATE user SET name = '张三' WHERE id = 1;

这条语句实际上会被 MySQL 当作:

START TRANSACTION;
UPDATE user SET name = '张三' WHERE id = 1;
COMMIT;

所以虽然你“看起来没用事务”,InnoDB 内部还是执行了一个隐式事务

✅ 这个隐式事务行为的结果是:

UPDATE 时,会加 行锁(InnoDB 默认)

因为你没显式控制事务,所以 语句一执行完就自动提交,锁也就立刻释放了

因此,大多数情况下你看不到锁阻塞的现象

点赞一下

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦