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

相关推荐
泡海椒几秒前
JQuick-SQL SELECT 投影:字段、别名与表达式查询
数据库·sql
猫咪很可爱5 分钟前
mysqldump 失败常见场景
数据库·sql·mysql
大明二代11 分钟前
从零构建 Mastra RAG MVP:DeepSeek、SiliconFlow 与 PostgreSQL/pgvector 实战
javascript·数据库·人工智能
LMT流氓兔22 分钟前
第四单元——第17课:让回答边生成、边显示
服务器·数据库·人工智能·python·oracle·langchain
1024奇点24 分钟前
【仓颉语言入门 · 第二篇 第 8 课】
开发语言·数据库·ide·mysql·开源
其实防守也摸鱼42 分钟前
Linux文本处理命令理论学习
linux·服务器·前端·数据库·sql·学习·安全
l1t1 小时前
DuckDB 各版本 tpch 10 查询时间对比
数据库
ggaofeng1 小时前
linux中的绿色版内存mysql
linux·运维·mysql
Nebula_g1 小时前
SpringBoot企业级实战项目—云禾外卖—SpringBoot常用注解解析
java·网络·数据库·spring boot·后端·spring·javase
雨落在了我的手上1 小时前
MySQL数据库基础(14):JDBC编程
数据库·mysql