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

相关推荐
小白勇闯网安圈4 小时前
Django 响应对象、文件上传与类视图
数据库·django·sqlite
努力的小雨5 小时前
同一份 SQL 跑多套环境:Ksql 变量怎么用才安全
数据库
科技绘图5 小时前
快鲸GEO vs 传统AI搜索优化:全链路自动化与高效内容生产在转化闭环上的对比
数据库·人工智能·自动化
ltl5 小时前
数据库作为 LLM 记忆体:语义缓存、RAG 与一致性
数据库
ltl5 小时前
WAL 与崩溃恢复:ARIES 协议怎么跑通
数据库
ltl6 小时前
TEE 数据库:EnclaveDB、Oblivious 原语与机密 SQL
数据库
麻瓜code7 小时前
【Mysql】重新学一遍 SQL 执行顺序
数据库·sql
这个DBA有点耶7 小时前
数据库一体机架构演进:从硬件堆叠到软硬深度耦合
服务器·网络·数据库·硬件架构·运维开发·database·数据库架构
一只fish7 小时前
优化器架构对比:Oracle vs PostgreSQL vs MySQL
mysql·postgresql·oracle
安_8 小时前
RAG的向量数据库:为LLM提供语义搜索能力
数据库