<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>MySQL on Tequila's 学习笔记</title><link>https://latnx.github.io/docs/notes/d-205f3859abe469be/d-01bd224b3773410f/d-6bdd8a9456cf7add/</link><description>Recent content in MySQL on Tequila's 学习笔记</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><copyright>© 2026 Tequila</copyright><atom:link href="https://latnx.github.io/docs/notes/d-205f3859abe469be/d-01bd224b3773410f/d-6bdd8a9456cf7add/index.xml" rel="self" type="application/rss+xml"/><item><title>1 MySQL 执行流程</title><link>https://latnx.github.io/docs/notes/d-205f3859abe469be/d-01bd224b3773410f/d-6bdd8a9456cf7add/n-463e29b570f6849f/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://latnx.github.io/docs/notes/d-205f3859abe469be/d-01bd224b3773410f/d-6bdd8a9456cf7add/n-463e29b570f6849f/</guid><description>MySQL 的架构共分为两层：Server 层和存储引擎层，
Server 层负责建立连接、分析和执行 SQL。MySQL 大多数的核心功能模块都在这实现，主要包括连接器，查询缓存、解析器、预处理器、优化器、执行器等。另外，所有的内置函数（如日期、时间、数学和加密函数等）和所有跨存储引擎的功能（如存储过程、触发器、视图等。）都在 Server 层实现。 存储引擎层负责数据的存储和提取。支持 InnoDB、MyISAM、Memory 等多个存储引擎，不同的存储引擎共用一个 Server 层。现在最常用的存储引擎是 InnoDB，InnoDB 默认索引类型是 B+树 。 第一步：连接器 # # -h 指定 MySQL 服务得 IP 地址，如果是连接本地的 MySQL服务，可以不用这个参数； # -u 指定用户名，管理员角色名为 root； # -p 指定密码，如果命令行中不填写密码（为了密码安全，建议不要在命令行写密码），就需要在交互对话里面输入密码 mysql -h$ip -u$user -p 与客户端进行 TCP 三次握手建立连接； 校验客户端的用户名和密码，如果用户名或密码不对，则会报错； 如果用户名和密码都对了，会读取该用户的权限，然后后面的权限逻辑判断都基于此时读取到的权限； 第二步：查询缓存（已删除） # MySQL 服务收到 SQL 语句后，就会解析出 SQL 语句的第一个字段。 如果 SQL 是查询语句（select 语句），MySQL 就会先去查询缓存（ Query Cache ）里查找缓存数据，看看之前有没有执行过这一条命令，这个查询缓存是以 key-value 形式保存在内存中的，key 为 SQL 查询语句，value 为 SQL 语句查询的结果。
第三步：解析 SQL # 解析器 # 第一件事情，词法分析。MySQL 会根据你输入的字符串识别出关键字出来，例如，SQL语句 select username from userinfo，在分析之后，会得到4个Token，其中有2个Keyword，分别为select和from：</description></item><item><title>2 InnoDB 引擎</title><link>https://latnx.github.io/docs/notes/d-205f3859abe469be/d-01bd224b3773410f/d-6bdd8a9456cf7add/n-bd7a7f2f25f37011/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://latnx.github.io/docs/notes/d-205f3859abe469be/d-01bd224b3773410f/d-6bdd8a9456cf7add/n-bd7a7f2f25f37011/</guid><description>事务，外键，行级锁
连接层 服务层 引擎层：索引 存储层 逻辑存储结构 # MySQL 中每一张表的数据都存放在一个独立的 .ibd 文件。这个文件也称为独占表空间文件
表空间 # 段：
索引段：存放 B + 树的非叶子节点的区的集合； 数据段：存放 B + 树的叶子节点的区的集合； 回滚段：存放的是回滚数据的区的集合，MVCC 利用了回滚段实现了多版本查询数据。 区：
B+ 树中每一层都是通过双向链表连接起来的，如果是以页为单位来分配存储空间，那么链表中相邻的两个页之间的物理位置并不是连续的，可能离得非常远，那么磁盘查询时就会有大量的随机I/O，随机 I/O 是非常慢的。 在表中数据量大的时候，为某个索引分配空间的时候就不再按照页为单位分配了，而是按照区（extent）为单位分配。每个区的大小为 1MB，对于 16KB 的页来说，连续的 64 个页会被划为一个区，这样就使得链表中相邻的页的物理位置也相邻，就能使用顺序 I/O 了。 页：16k，IO的最小单位，表中的记录存储在「数据页」里面
行：
COMPACT 行格式 1. 变长字段长度列表 # 变长字段的真实数据占用的字节数会按照列的顺序逆序存放
2. NULL 值列表 # 如果存在允许 NULL 值的列，则每个列对应一个二进制位（bit），二进制位按照列的顺序逆序排列。
3. 记录头信息 # delete_mask ：标识此条数据是否被删除。从这里可以知道，我们执行 detele 删除记录的时候，并不会真正的删除记录，只是将这个记录的 delete_mask 标记为 1。 next_record：下一条记录的位置。从这里可以知道，记录与记录之间是通过链表组织的。在前面我也提到了，指向的是下一条记录的「记录头信息」和「真实数据」之间的位置，这样的好处是向左读就是记录头信息，向右读就是真实数据，比较方便。 record_type：表示当前记录的类型，0表示普通记录，1表示B+树非叶子节点记录，2表示最小记录，3表示最大记录 4. 隐藏字段 # row_id：6B，没有主键或唯一约束的情况下 trx_id：事务id roll_pointer：上版本指针 Compact 行格式针对行溢出的处理是这样的：当发生行溢出时，在记录的真实数据处只会保存该列的一部分数据，而把剩余的数据放在「溢出页」中，然后真实数据处用 20 字节存储指向溢出页的地址，从而可以找到剩余数据所在的页。</description></item><item><title>3 索引</title><link>https://latnx.github.io/docs/notes/d-205f3859abe469be/d-01bd224b3773410f/d-6bdd8a9456cf7add/n-727ad98bb874c526/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://latnx.github.io/docs/notes/d-205f3859abe469be/d-01bd224b3773410f/d-6bdd8a9456cf7add/n-727ad98bb874c526/</guid><description>索引本质上是一种帮助数据库快速查询数据的数据结构，MySQL InnoDB 默认使用 B+Tree 实现索引。 有了索引后，数据库不需要全表扫描，而是可以快速定位数据位置，从而提高查询效率。 不过索引也有代价，会占用额外空间，并且会降低 INSERT、UPDATE、DELETE 的性能，因为写数据时还需要维护索引结构。 结构 # 索引的就是帮助存储引擎快速获取数据的一种数据结构 InnoDB 是在 MySQL 5.5 之后成为默认的 MySQL 存储引擎，B+Tree 索引类型也是 MySQL 存储引擎采用最多的索引类型。
B+ Tree 索引优势 # B+Tree 相比于 B 树和二叉树来说，最大的优势在于查询效率很高，因为即使在数据量很大的情况，查询一个数据的磁盘 I/O 依然维持在 3-4次。
B树： 叶子节点不存放数据，相比之下 B+ Tree IO次数少，可以范围查找 二叉树：IO次数多 Hash：不能范围查找 分类 # 分类1： 主键索引：唯一不空 唯一索引：唯一 常规索引：无限制 前缀索引：指对字符类型字段的前几个字符建立的索引，而不是在整个字段上建立的索引
分类2
聚集索引（主键索引）：
如果有主键，默认会使用主键作为聚簇索引的索引键（key）； 如果没有主键，就选择第一个不包含 NULL 值的唯一列作为聚簇索引的索引键（key）； 在上面两个都没有的情况下，InnoDB 将自动生成一个隐式自增 id 列作为聚簇索引的索引键（key）； 二级索引：
「回表查询」，也就是说要查两个 B+ Tree 才能查到数据 二级索引的 B+ Tree 就能查询到结果的过程就叫作「覆盖索引」，也就是只需要查一个 B+ Tree 就能找到数据。 分类3： 单列索引 联合索引：使用联合索引时，存在最左匹配原则，也就是按照最左优先的方式进行索引的匹配。在使用联合索引进行查询的时候，如果不遵循「最左匹配原则」，联合索引会失效，这样就无法利用到索引快速查询的特性了。</description></item><item><title>4 事务</title><link>https://latnx.github.io/docs/notes/d-205f3859abe469be/d-01bd224b3773410f/d-6bdd8a9456cf7add/n-f1e955d59d87acea/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://latnx.github.io/docs/notes/d-205f3859abe469be/d-01bd224b3773410f/d-6bdd8a9456cf7add/n-f1e955d59d87acea/</guid><description> 事务四大特性 ACID # 原子性（Atomicity）：一个事务中的所有操作，要么全部完成，要么全部不完成。
一致性（Consistency）：是指事务操作前和操作后，数据库保持一致性状态。
隔离性（Isolation）：有若干并发事务，事务在不受外部并发操作的影响下的独立环境运行的。
持久性（Durability）：事务处理结束后，对数据的修改就是永久的。
持久性是通过 redo log （重做日志）来保证的； 原子性是通过 undo log（回滚日志） 来保证的； 隔离性是通过 MVCC（多版本并发控制，使用了undolog） 和锁机制来保证的； 一致性则是通过持久性+原子性+隔离性来保证；</description></item><item><title>5 锁</title><link>https://latnx.github.io/docs/notes/d-205f3859abe469be/d-01bd224b3773410f/d-6bdd8a9456cf7add/n-6bde637b36da2ecc/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://latnx.github.io/docs/notes/d-205f3859abe469be/d-01bd224b3773410f/d-6bdd8a9456cf7add/n-6bde637b36da2ecc/</guid><description>全局锁 # 全局锁主要应用于做全库逻辑备份，这样在备份数据库期间，不会因为数据或表结构的更新，而出现备份文件的数据与预期的不一样。 数据库一致性备份
表级锁 # 表锁： 1. 表共享读锁 2. 表独占写锁
元数据锁（MDL）：MDL 是为了保证当用户对表执行 CRUD 操作时，防止其他线程对这个表结构做了变更。 1. 对一张表进行 CRUD 操作时，加的是 MDL 读锁； 2. 对一张表做结构变更操作的时候，加的是 MDL 写锁；
意向锁：意向共享锁和意向独占锁是表级锁，不会和行级的共享锁和独占锁发生冲突，而且意向锁之间也不会发生冲突，只会和共享表锁（lock tables &amp;hellip; read）和独占表锁（lock tables &amp;hellip; write）发生冲突。 1. 在使用 InnoDB 引擎的表里对某些记录加上「共享锁」之前，需要先在表级别加上一个「意向共享锁」； 2. 在使用 InnoDB 引擎的表里对某些纪录加上「独占锁」之前，需要先在表级别加上一个「意向独占锁」；
AUTO-INC 锁
行级锁 # 共享锁，排他锁
Record Lock，记录锁，也就是仅仅把一条记录锁上，RC，RR，RR Gap Lock，间隙锁，锁定一个范围，但是不包含记录本身 Next-Key Lock：Record Lock + Gap Lock 的组合，锁定一个范围，并且锁定记录本身，RR</description></item><item><title>6 日志</title><link>https://latnx.github.io/docs/notes/d-205f3859abe469be/d-01bd224b3773410f/d-6bdd8a9456cf7add/n-9006a937bdb5f099/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://latnx.github.io/docs/notes/d-205f3859abe469be/d-01bd224b3773410f/d-6bdd8a9456cf7add/n-9006a937bdb5f099/</guid><description>undo log（回滚日志）：是 Innodb 存储引擎层生成的日志，实现了事务中的原子性，主要用于事务回滚和 MVCC。 redo log（重做日志）：是 Innodb 存储引擎层生成的日志，实现了事务中的持久性，主要用于掉电等故障恢复； binlog （归档日志）：是 Server 层生成的日志，主要用于数据备份和主从复制；
RedoLog: 持久性 # 刷新脏页到磁盘中时，发生错误恢复时使用。 在进行buffer_pool修改的时候进行记录，并写入磁盘 因为前面的操作时随机磁盘IO，日志是顺序磁盘IO，WAL（先写日志）
redo log buffer 什么时候刷盘
MySQL 正常关闭时； 当 redo log buffer 中记录的写入量大于 redo log buffer 内存空间的一半时，会触发落盘； InnoDB 的后台线程每隔 1 秒，将 redo log buffer 持久化到磁盘。 每次事务提交时都将缓存在 redo log buffer 里的 redo log 直接持久化到磁盘 innodb_flush_log_at_trx_commit 当设置该参数为 0 时，表示每次事务提交时 ，还是将 redo log 留在 redo log buffer 中 ，该模式下在事务提交时不会主动触发写入磁盘的操作。 当设置该参数为 1 时，表示每次事务提交时，都将缓存在 redo log buffer 里的 redo log 直接持久化到磁盘，这样可以保证 MySQL 异常重启之后数据不会丢失。 当设置该参数为 2 时，表示每次事务提交时，都只是缓存在 redo log buffer 里的 redo log 写到 redo log 文件，注意写入到「 redo log 文件」并不意味着写入到了磁盘 redo log 文件组：两个大小相同的文件 重做日志文件组是以循环写的方式工作的，从头开始写，写到末尾就又回到开头，相当于一个环形。</description></item><item><title>7 事务隔离级别 MVCC</title><link>https://latnx.github.io/docs/notes/d-205f3859abe469be/d-01bd224b3773410f/d-6bdd8a9456cf7add/n-2903218579256756/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://latnx.github.io/docs/notes/d-205f3859abe469be/d-01bd224b3773410f/d-6bdd8a9456cf7add/n-2903218579256756/</guid><description>并发存在的问题 # 脏读：一个事务「读到」了另一个「未提交事务修改过的数据」 不可重复度：在一个事务内多次读取同一个数据，如果出现前后两次读到的数据不一样的情况，就意味着发生了「不可重复读」现象。 幻读：在一个事务按照条件查询数据时，没有对应的数据行，但在插入数据后，这行数据又存在。 事务的隔离级别 # 读未提交：脏读，不可重复读，幻读 读提交：不可重复读，幻读 可重复读：幻读 串行化：
当前读：读取的是最新版本 快照读：读取的是记录数据的可见版本 mvcc实现 # 隐藏字段 # trx_id，当一个事务对某条聚簇索引记录进行改动时，就会把该事务的事务 id 记录在 trx_id 隐藏列里； roll_pointer，每次对某条聚簇索引记录进行改动时，都会把旧版本的记录写入到 undo 日志中，然后这个隐藏列是个指针，指向每一个旧版本记录，于是就可以通过它找到修改前的记录。
undolog # undo log版本链
readView # 如果记录的 trx_id 值小于 Read View 中的 min_trx_id 值，表示这个版本的记录是由&amp;quot;创建 Read View 之前就已经提交的事务&amp;quot;生成的，所以该版本对当前事务可见。
如果记录的 trx_id 值大于等于 Read View 中的 max_trx_id 值，表示这个版本的记录是由&amp;quot;创建 Read View 之后才启动的事务&amp;quot;生成的，所以该版本对当前事务不可见。
如果记录的 trx_id 值介于 min_trx_id 和 max_trx_id 之间(即 min_trx_id &amp;lt;= trx_id &amp;lt; max_trx_id)，说明该事务的 id 在 Read View 创建之前就已经分配出去了，此时需要进一步判断 trx_id 是否在 m_ids 列表中:</description></item></channel></rss>