MySQL存储引擎InnoDB的索引和锁深学习

MySQL存储引擎InnoDB的索引和锁深学习:

电商案例:

订单表构建:

mysql 复制代码
CREATE TABLE `orders` (
  `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '订单ID',
  `order_no` VARCHAR(32) NOT NULL COMMENT '订单号',
  `user_id` BIGINT UNSIGNED NOT NULL COMMENT '用户ID',
  `status` TINYINT NOT NULL COMMENT '订单状态',
  `pay_status` TINYINT NOT NULL COMMENT '支付状态',
  `total_amount` DECIMAL(10,2) NOT NULL COMMENT '订单总金额',
  `created_at` DATETIME NOT NULL COMMENT '创建时间',
  `paid_at` DATETIME DEFAULT NULL COMMENT '支付时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_order_no` (`order_no`)  # 设置唯一索引,允许数据为null
) ENGINE=InnoDB;

核心查找场景:

场景一:根据订单号查询

mysql 复制代码
SELECT * FROM orders WHERE order_no = ?;

索引命中:

索引命中唯一索引,属于二级索引,回表查询一次

锁行为:

​ 普通,快照读,无锁

行锁:唯一索引,精确命中,行锁,无间隙锁

mysql 复制代码
select...for update

场景二:用户订单列表(分页)

user_id和created_at为复合索引

mysql 复制代码
SELECT * FROM orders
WHERE user_id = ?
ORDER BY created_at DESC
LIMIT 20;

RR 隔离级别:

  • Next-Key Lock
  • 锁定:
    • user_id = 100
    • created_at 范围内的 20 条 + 间隙
  • 会阻塞同一用户的新订单插入

场景三:订单状态流转(高风险)

mysql 复制代码
UPDATE orders
SET status = 2
WHERE order_no = ? AND status = 1;

索引命中

  • uk_order_no 命中
  • status 是过滤条件,但不在索引中

锁行为

  • 通过唯一索引定位记录
  • 对该行加 行锁
  • 不会锁其他订单

安全写法

❌ 错误写法(常见事故)

mysql 复制代码
UPDATE orders
SET status = 2
WHERE status = 1;

锁行为(灾难)

  • status 无索引
  • 全表扫描
  • 锁全表所有行

🚨 高并发下直接拖垮数据库

场景四:索引失效导致锁升级

mysql 复制代码
SELECT * FROM orders
WHERE user_id = 100
AND DATE(created_at) = '2026-01-01'
FOR UPDATE;

DATE(created_at) 使索引失效

👉 实际效果:

  • 全表扫描
  • 锁全表

✅ 正确写法:

mysql 复制代码
WHERE user_id = 100
AND created_at >= '2026-01-01 00:00:00'
AND created_at <  '2026-01-02 00:00:00'

疑惑一:范围查询没用索引,是不是会导致全表锁?

如果是"当前读",是的;如果是"快照读",不是。

拆解说明

场景 是否加锁 锁到哪
SELECT(无 FOR UPDATE) 不加锁
SELECT ... FOR UPDATE(无索引) 全表所有行
UPDATE / DELETE(无索引) 全表所有行

📌 关键不是"范围查询",而是"是不是当前读"

疑惑二:是不是快照读即使没有索引也不会锁?

不会锁

快照读只使用 MVCC,不需要锁最新数据版本,因此不会加行锁。

疑惑三:当前读一定会锁吗?

正确,这是一道分水岭,只要是当前读,就一定加锁,差别在于锁几行

SQL 是否当前读
SELECT ... FOR UPDATE
SELECT ... LOCK IN SHARE MODE
UPDATE
DELETE

疑惑四:是不是update和delete天生就有排他锁?

update和delete本身就是当前读,执行时一定会加入排他锁(x lock)

你不用写,for update 引擎内部就已经帮你做了。

对比 表锁 无索引 UPDATE
锁类型 表级 行级
实现 MySQL InnoDB
锁对象 整表 每一行
业务感知 ❌ 写不了 ❌ 写不了

可以直接背下来的 6 句"工程级定论"

1️⃣ SELECT 默认是快照读,不加锁

2️⃣ UPDATE / DELETE 一定加排他锁

3️⃣ 是否锁全表,取决于是否命中索引

4️⃣ 没索引的当前读 = 锁全表所有行

5️⃣ RR 比 RC 锁得更"广",但不救无索引

6️⃣ 索引设计就是锁设计

相关推荐
JuneXcy19 小时前
第4章 Mysql数据操纵语句--单表查询
mysql
XDHCOM19 小时前
ORA-32152报错咋整啊,数据库操作遇到null number问题远程帮忙修复
服务器·数据库·oracle
专利观察员19 小时前
输配电行业创新转型实践:南宁迪**力有限公司的专利策略调整、专利检索工具采用
数据库·科技·专利·专利申请
jgyzl19 小时前
2026.3.9 Redis内存回收内存淘汰
数据库·redis·缓存
白露与泡影19 小时前
MySQL 时间类型选型避坑:timestamp 和 datetime 该怎么选?
数据库·mysql
青槿吖20 小时前
第二篇:告别XML臃肿配置!Spring注解式IOC/DI保姆级教程,从入门到真香
xml·java·开发语言·数据库·后端·sql·spring
运维 小白21 小时前
2. 部署mysql服务并监控mysql
数据库·mysql·adb
聪明人21 小时前
macOS安装Redis
数据库·redis·macos
北漂Zachary21 小时前
Mysql中使用sql语句生成雪花算法Id
sql·mysql·算法
weixin_5051544621 小时前
Bowell Studio:重塑工业互联网时代的装配制造与运维检修
运维·数据库·人工智能·制造·数字孪生·3d产品配置器·3d交互展示