MySQL 技术浅析(聚簇索引、UndoLog、RedoLog、MVCC)

devtools/2025/3/14 8:28:40/

MySQL 核心技术深度解析

一、聚簇索引与非聚簇索引

1. 聚簇索引结构

  • 存储方式
    InnoDB 中,聚簇索引的叶子节点直接存储完整数据行,数据按主键值物理排序存储。

    • 主键索引即数据文件,非叶子节点存储主键范围和子节点指针
    • 数据行与主键索引绑定,主键顺序决定磁盘存储顺序
  • 示例存储结构

    B+树结构:
    根节点 → [id<100, page1] [100≤id<200, page2]
    叶子节点(page1)→ | id=50 | Alice | 25 | → | id=80 | Bob | 30 |  
    (数据行按id排序存储)
    

2. 聚簇索引 vs 非聚簇索引查询速度对比

查询类型聚簇索引性能非聚簇索引性能
主键查询⚡️ O(1) 直接定位数据🐢 需两次查找(索引+回表)
范围查询⚡️ 顺序I/O快(数据连续存储)🐢 随机I/O多(数据分散)
覆盖查询✅ 无需回表若索引包含查询字段可避免回表

结论:主键查询优先使用聚簇索引,二级索引需评估回表代价


二、事务日志机制

1. Undo Log(回滚日志)

  • 核心作用

    • 事务回滚:记录数据修改前的旧值
    • MVCC 支持:存储多版本数据实现非阻塞读
  • 存储结构

    ┌───────────────┐
    | Undo Log Segment |
    | - INSERT → 记录主键值         |
    | - UPDATE → 旧数据镜像         |
    | - DELETE → 数据完整拷贝       |
    └───────────────┘
    
    • 通过 purge 线程定期清理已提交事务的旧版本

2. Redo Log(重做日志)

  • 核心作用
    确保事务持久性,防止数据丢失

  • 工作流程

    innodb_flush_log_at_trx_commit=1
    innodb_flush_log_at_trx_commit=0
    事务修改数据
    写入Log Buffer
    刷盘策略
    同步刷盘
    每秒异步刷盘
    • 物理日志:记录数据页的物理修改(如"page5 offset10 值从’A’改为’B’")

三、MVCC(多版本并发控制)

1. 实现三要素

要素说明
隐藏字段DB_TRX_ID(最后修改事务ID)、DB_ROLL_PTR(回滚指针指向旧版本)
版本链数据通过回滚指针形成多版本链
ReadView决定事务可见哪个版本(包含活跃事务ID列表)

2. 可见性判断逻辑

def is_visible(trx_id, read_view):if trx_id < read_view.min_trx_id: return True  # 已提交的旧事务elif trx_id in read_view.active_trx_ids: return False # 未提交的活跃事务else: return trx_id <= read_view.creator_trx_id

3. 隔离级别与MVCC

隔离级别MVCC 行为
读已提交(RC)每次查询生成新 ReadView
可重复读(RR)事务内复用同一个 ReadView

四、锁机制与隔离性实现

1. 核心锁类型

锁类型描述应用场景
共享锁(S Lock)允许其他读锁,阻塞写锁SELECT ... LOCK IN SHARE MODE
排他锁(X Lock)阻塞其他所有锁SELECT ... FOR UPDATE
间隙锁(Gap Lock)锁定索引记录间的间隙防止幻读(RR级别)
Next-Key Lock行锁+间隙锁组合同时防止幻读和写冲突

2. 隔离级别实现方案

隔离级别实现机制典型场景
读未提交(RU)无锁直接读最新数据实时统计
读已提交(RC)语句级快照读 + 行锁高并发OLTP
可重复读(RR)事务级快照读 + Next-Key LockMySQL默认级别
串行化(Serial)全表范围锁 + 严格串行执行金融交易

3. 死锁处理示例

-- 事务1
BEGIN;
UPDATE accounts SET balance=balance-100 WHERE id=1; -- 获得X锁
UPDATE accounts SET balance=balance+100 WHERE id=2;  -- 等待事务2释放锁-- 事务2
BEGIN;
UPDATE accounts SET balance=balance-50 WHERE id=2;  -- 获得X锁
UPDATE accounts SET balance=balance+50 WHERE id=1;  -- 等待事务1释放锁-- InnoDB 检测到死锁后,自动回滚代价较小的事务

五、性能优化实践

1. 索引优化技巧

  • 主键设计为自增整型(减少页分裂)
  • 联合索引遵循最左前缀原则
    -- 有效使用索引
    SELECT * FROM users WHERE name='Alice' AND age>20; 
    -- 索引设计为 (name, age)
    

2. 事务控制建议

  • 控制事务粒度:单个事务执行时间 < 1秒
  • 批量操作分批次提交
    SET autocommit=0;
    INSERT INTO big_table VALUES (...);  -- 执行1000次
    COMMIT;
    

3. 锁优化策略

  • 避免长事务:减少锁持有时间
  • 使用等值查询:WHERE id=123(比范围查询更高效)
  • 设置合理锁超时:
    SET innodb_lock_wait_timeout=3;  -- 锁等待超时3秒
    

通过深入理解存储引擎机制,可针对性优化数据库设计,提升系统并发性能与数据可靠性。


http://www.ppmy.cn/devtools/166971.html

相关文章

JavaScript class

1. 抽象类 抽象类定义之后不能被实例化&#xff0c;只能被继承&#xff0c;抽象方法定义之后&#xff0c;必须由继承类实现 abstract class Animal {name: string;constructor(name) {this.name name;}abstract say():void }class Dog extends Animal {constructor(name) {s…

小程序酒店:如何实现智能预订与在线支付?

在移动互联网快速发展的今天,酒店行业面临着前所未有的机遇与挑战。用户需求日益多样化,市场竞争愈发激烈,传统酒店预订方式已经难以满足现代消费者的需求。而小程序作为一种轻量化、便捷化的移动应用形式,正在成为酒店行业数字化转型的重要工具。通过小程序开发,酒店可以…

用DasViewer的时候3Dtiles 转osgb 可以直接指定目标坐标系吗?

没有指定坐标系选项&#xff0c;可以转换后&#xff0c;再进行一次坐标系转换。 DasViewer是一款免费极速实景三维模型浏览器&#xff0c;采用多细节层次模型逐步自适应加载技术,让用户在极低的电脑配置下,也能流畅的加载较大规模实景三维模型,提供方便快捷的数据浏览操作。 目…

从零开始的python学习(五)P75+P76+P77+P78+P79+P80

本文章记录观看B站python教程学习笔记和实践感悟&#xff0c;视频链接&#xff1a;【花了2万多买的Python教程全套&#xff0c;现在分享给大家&#xff0c;入门到精通(Python全栈开发教程)】 https://www.bilibili.com/video/BV1wD4y1o7AS/?p6&share_sourcecopy_web&v…

QQuick-Binding的介绍

QQuick-Binding的介绍 Binding的概述 属性绑定&#xff1a;在qml中两个对象的属性和属性之间可以形成依赖&#xff0c;直接属性绑定 如 width: parent.width; 即 对象宽度 与父对象的宽度进行绑定&#xff1b;除了默认的属性绑定&#xff0c;还可以通过Bingding对象建立显示的…

Python:函数(一)

python函数相关的知识点 1. 函数定义与调用 定义&#xff1a;使用 def 关键字&#xff0c;后接函数名和参数列表。 def greet(name):"""打印问候语&#xff08;文档字符串&#xff09;"""print(f"Hello, {name}!") 调用&#xff1a…

【设计模式】建造者模式——工厂模式

三、建造者模式——工厂模式 3.1 工厂模式 创建一个类对象的传统方式是使用关键字new, 因为用new 创建的类对象是一个堆对象&#xff0c;可以实现多态。工厂模式通过把创建对象的代码包装起来&#xff0c;实现创建对象的代码与具体 的业务逻辑代码相隔离的目的(将对象的创建和…

重学vue3:vue3组件通信的几种方法

1. Props(属性传递) 应用场景: 适用于父组件向子组件传递数据,通常用于单向数据流。流程: 父组件在子组件标签上通过属性绑定数据(使用 : 或 v-bind)。子组件使用 defineProps 宏声明并接收这些属性。子组件可以直接使用接收到的 props 数据。代码示例:<!-- 父组件 …