news 2026/7/2 5:15:26

UPDATE users SET name=‘John‘ WHERE id=100000000000000的庖丁解牛

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
UPDATE users SET name=‘John‘ WHERE id=100000000000000的庖丁解牛

UPDATE users SET name='John' WHERE id=100000000000000看似简单,但其执行过程涉及索引查找、行锁、Redo/Undo 生成、Buffer Pool 修改等多层机制。


一、整体执行链路

Yes

No

SQL

Parser

Optimizer

Executor

InnoDB: 主键查找

找到记录?

加行锁

生成 Undo Log

修改 Buffer Pool

生成 Redo Log

COMMIT 刷 Redo

直接返回

核心原则
即使 UPDATE 未命中任何行,仍会走完整执行流程(含锁和日志)


二、分阶段深度拆解

阶段 1:SQL 解析与优化
  • Parser
    生成 AST,识别UPDATE、表名、SET 子句、WHERE 条件
  • Resolver
    • 验证users表存在
    • 检查nameid列是否存在
    • 验证用户有UPDATE权限
  • Optimizer
    • 分析WHERE id=...选择主键索引(聚簇索引)
    • 估算行数:1 行(主键唯一)

⚠️关键点
主键查询是 InnoDB 最高效的路径(O(log n) B+ 树搜索)


阶段 2:存储引擎执行(InnoDB)
步骤 1:主键查找
  • 定位数据页
    • 从根节点开始遍历聚簇索引 B+ 树
    • 目标页 = 包含id=100000000000000的 16KB 页
  • 加载到 Buffer Pool
    • 若页已在内存 → 直接使用
    • 若不在 → 触发同步 I/O从磁盘读取
步骤 2:检查记录是否存在
  • 在页内二分查找id=100000000000000
  • 结果
    • 存在→ 继续更新
    • 不存在→ 返回Affected rows: 0

💡为什么仍需锁
即使记录不存在,InnoDB 可能加间隙锁(Gap Lock)防止幻读(取决于隔离级别)。


阶段 3:事务与锁机制
场景 A:记录存在
  • 加行锁(Record Lock)
    • 锁类型:X 锁(排他锁)
    • 锁对象:主键id=100000000000000对应的记录
  • 隔离级别影响
    • READ COMMITTED:仅锁记录
    • REPEATABLE READ:锁记录 + 间隙(防幻读)
场景 B:记录不存在
  • 加间隙锁(Gap Lock)(仅 REPEATABLE READ):
    • 锁范围:(前一个id, 后一个id)
      (例如(99999999999999, 100000000000001)
    • 目的:阻止其他事务插入id=100000000000000

⚠️死锁风险
高并发下,间隙锁可能引发死锁(需应用层重试)。


阶段 4:生成 Undo Log(用于回滚和 MVCC)
  • Undo 记录内容
    • 旧值:name='OldName'
    • 事务 ID、回滚指针
  • 存储位置
    • Undo 页(独立于数据页)
    • 通过回滚段(Rollback Segment)管理
  • 作用
    • 回滚ROLLBACK时恢复旧值
    • MVCC:其他事务可读取旧版本

💡即使 name 未变
InnoDB 仍会生成 Undo(因无法预知值是否相同)。


阶段 5:修改 Buffer Pool
  • 操作
    • 在内存中定位目标记录
    • name字段从旧值改为'John'
    • 更新记录的事务 ID回滚指针
  • 标记脏页
    • 所在 16KB 页标记为Dirty Page
    • 后台线程异步刷盘(Checkpoint)

阶段 6:生成 Redo Log(WAL 机制)
  • Redo 记录内容
    // 聚簇索引变更MLOG_REC_UPDATE_IN_PLACE:page_id=12345,offset=200,data="John"// Undo Log 变更MLOG_WRITE_STRING:undo_page=67890,offset=100,data="OldName"
  • 写入 Redo Log Buffer(内存)
  • COMMIT 时刷盘
    • fsync()确保 Redo 落盘
    • 此时事务已持久化(即使数据页未刷盘)

⚠️Double Write Buffer
脏页刷盘时,先写双写区 → 防页断裂。


三、性能关键点

环节潜在瓶颈优化方向
主键查找Buffer Pool 未命中 → 磁盘 I/O增大innodb_buffer_pool_size
行锁竞争高并发更新同一行应用层队列化
Redo 刷盘fsync延迟高使用 NVMe SSD
Undo 积累长事务阻塞 Purge避免大事务

四、特殊场景分析

场景 1:id 不存在
  • 仍会
    • 遍历 B+ 树到叶子页
    • 加间隙锁(REPEATABLE READ)
    • 生成空 Redo(仅事务提交记录)
  • 性能影响
    比命中记录略快(无数据修改),但仍有 I/O 和锁开销。
场景 2:name 值未变
UPDATEusersSETname='John'WHEREid=100ANDname='John';
  • InnoDB 仍会
    • 加锁
    • 生成 Undo/Redo
  • 优化建议
    应用层先SELECT判断,避免无效 UPDATE。

五、监控与诊断

1.查看锁信息
-- 当前锁等待SELECT*FROMinformation_schema.INNODB_LOCKS;-- 事务状态SELECT*FROMinformation_schema.INNODB_TRX;
2.分析执行计划
EXPLAINUPDATEusersSETname='John'WHEREid=100000000000000;-- 输出: type=const, key=PRIMARY
3.Redo 生成量
SHOWGLOBALSTATUSLIKE'Innodb_os_log_written';-- 对比 UPDATE 前后增量

六、工程最佳实践

  1. 确保主键高效

    • 使用自增 ID 或 UUID 有序化
    • 避免超大主键(如 100 字节 VARCHAR)
  2. 减少无效更新

    // 应用层判断if($oldName!=='John'){$db->update('users',['name'=>'John'],['id'=>$id]);}
  3. 批量更新替代单条

    UPDATEusersSETname=CASEidWHEN1THEN'A'WHEN2THEN'B'ENDWHEREidIN(1,2);
  4. 监控长事务

    -- 查找运行超过 60 秒的事务SELECT*FROMinformation_schema.INNODB_TRXWHERETIME_TO_SEC(TIMEDIFF(NOW(),trx_started))>60;

七、总结:UPDATE 的本质

  • 不是“直接改数据”,而是“加锁 → 记录旧值 → 改内存 → 写日志”
  • 持久化 = Redo 落盘,与数据页刷盘无关。
  • 性能 = 减少 I/O + 减少锁竞争 + 减少日志量
  • 终极心法
    “每一次 UPDATE,都是对 ACID 的庄严承诺——
    要么全部成功,要么全部回滚,绝不半途而废。”

💡一句话
主键 UPDATE 是 InnoDB 的拿手好戏,
但再快的操作,也快不过“不操作”。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/30 18:53:08

1小时搭建:许可证状态监控系统原型

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容: 快速开发一个最小可行许可证监控系统:1. 接收包含许可证状态的文本输入 2. 解析并提取关键信息(许可证号、状态) 3. 简单的状态展示界面 4. 基础的通知功能(控制台输出…

作者头像 李华
网站建设 2026/6/30 18:47:05

ResNet18 vs MobileNet对比测试:云端GPU 2小时搞定选型

ResNet18 vs MobileNet对比测试:云端GPU 2小时搞定选型 1. 为什么需要模型对比测试? 作为创业团队的技术负责人,当你需要为APP选择图像识别模型时,通常会面临这样的困境:ResNet18和MobileNet听起来都不错&#xff0c…

作者头像 李华
网站建设 2026/6/25 12:44:30

AI如何帮你快速实现I2C通信协议开发

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容: 请生成一个完整的I2C通信协议实现代码,要求包含以下功能:1) I2C主设备初始化函数 2) I2C从设备地址设置 3) 数据读写函数实现 4) 错误处理机制 5) 使用标准…

作者头像 李华
网站建设 2026/6/30 18:30:40

Java新手必看:虚拟机创建失败的简单解决方法

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容: 开发一个面向Java初学者的交互式学习工具,通过图形化界面引导用户逐步排查和解决虚拟机创建错误。工具应包含基础概念解释、可视化内存配置调整、一键测试解决方案等功…

作者头像 李华
网站建设 2026/6/30 15:12:30

Krita插件深度解析:如何通过扩展工具提升数字绘画创作效率

Krita插件深度解析:如何通过扩展工具提升数字绘画创作效率 【免费下载链接】krita Krita is a free and open source cross-platform application that offers an end-to-end solution for creating digital art files from scratch built on the KDE and Qt framew…

作者头像 李华