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️⃣ 索引设计就是锁设计

相关推荐
web182854825121 小时前
代码诊疗室:破解疑难Bug实战
数据库
数据知道2 小时前
MongoDB 数据库与集合管理:显式创建与隐式创建的区别及生产环境建议
数据库·mongodb·oracle
数据知道2 小时前
MongoDB 的 CRUD 极速上手:insertOne/insertMany 与批量写入的性能差异
数据库·mongodb
愚公搬代码2 小时前
【愚公系列】《数据可视化分析与实践》019-数据集(自定义SQL数据集)
数据库·sql·信息可视化
甲枫叶2 小时前
【claude产品经理系列11】实现后端接口——数据在背后如何流动
java·数据库·人工智能·产品经理·ai编程·visual studio code
甲枫叶2 小时前
【claude产品经理系列12】接入数据库——让数据永久保存
java·数据库·人工智能·产品经理·ai编程
Elastic 中国社区官方博客2 小时前
Elasticsearch:通过最小分数确保语义精度
大数据·数据库·人工智能·elasticsearch·搜索引擎·ai·全文检索
Hoffer_2 小时前
更好理解ORDER BY内部排序和性能优化-mysql
后端·mysql
abyyyyy1233 小时前
oj题目练习
java·前端·数据库
lzhdim3 小时前
SQL 入门 2:LIKE、正则、 ORDER BY 与LIMIT
数据库·sql·mysql