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

相关推荐
无限码力5 小时前
华为OD技术面真题 - 数据库MySQL - 3
数据库·mysql·华为od·八股文·华为od技术面八股文
heartbeat..5 小时前
Redis 中的锁:核心实现、类型与最佳实践
java·数据库·redis·缓存·并发
Prince-Peng5 小时前
技术架构系列 - 详解Redis
数据结构·数据库·redis·分布式·缓存·中间件·架构
虾说羊5 小时前
redis中的哨兵机制
数据库·redis·缓存
_F_y5 小时前
MySQL视图
数据库·mysql
2301_790300965 小时前
Python单元测试(unittest)实战指南
jvm·数据库·python
九章-5 小时前
一库平替,融合致胜:国产数据库的“统型”范式革命
数据库·融合数据库
2401_838472516 小时前
使用Scikit-learn构建你的第一个机器学习模型
jvm·数据库·python
u0109272716 小时前
使用Python进行网络设备自动配置
jvm·数据库·python
wengqidaifeng6 小时前
数据结构---顺序表的奥秘(下)
c语言·数据结构·数据库