MySQL InnoDB中一个update语句从执行到提交的全过程(1)

目录

一、开启事务

二、sql解析、查询计划生成

三、查询要修改的数据

1、读buffer pool的过程

buffer pool的结构组成

1)Free List (空闲链表)

2)LRU List (最近最少使用链表)

3)Flush List (刷新链表)

三条链表之间的关系

2、怎么样在buffer pool中找到我们想要的页?

3、解析页的过程

以数据页为例,页的结构


一、开启事务

默认情况下autocommit=1,如果我们没有显式开启事务,而是直接执行update,那么MySQL就会隐式地为我们开启一个事务,并在这条update执行结束后自动提交。

如果set autocommit=0,或者先执行了begin;/start transaction;显式开启事务,那么update执行完成后不会自动提交,需要我们手动发起commit;或者rollback;。

不管用哪种方式开启事务后,MySQL都会在事务内第一次执行增删改操作时,给事务分配一个事务号。假定我们这条update所在的事务事务号为T1024。

二、sql解析、查询计划生成

在Mysql8.0之前,有查询缓存模块。

MySQL需要监测查询缓存涉及的每张表,只要这张表发生了任何变化,都要使其缓存失效。

查询缓存生效的条件是查询语句完全一致,如果新的语句多了一个空格,或者大小写发生变化,查询缓存都不能生效。

查询缓存在MySQL 5.7版本已经默认禁用了,在MySQL8.0版本更是直接被删除。

三、查询要修改的数据

在执行UPDATE之前,InnoDB需要先定位需要修改的记录。这通常涉及到对索引的查找。

对磁盘和bufferpool的读取都是以页为单位:表空间号+页号。

传入页号这个参数到buffer pool,然后buffer pool判断这个页是否在buffer pool中。如果该记录的页已经在buffer pool中,那么InnoDB直接从buffer pool中读取,直接返回我们想要的页。如果不在,InnoDB会从磁盘上加载相应的页到buffer pool中,再返回。

1、读buffer pool的过程

我们还不清楚buffer pool内部是怎么获取、保存、管理页的。

buffer pool的结构组成

在 MySQL InnoDB 存储引擎中,Buffer Pool 是管理数据库页(page)的核心组件,负责在内存中缓存数据页以加速数据库操作。为了高效地管理和调度这些页,InnoDB 使用了三条主要的链表:Free ListLRU ListFlush List。这些链表各自负责不同的管理任务,并相互协作来确保内存使用的高效性和数据的一致性。

1)Free List (空闲链表)

描述:空闲链,主要负责管理未被使用的缓冲池空间。

Free List 维护的是已经从 Buffer Pool 中释放出来的、当前不再被使用的内存页。它充当了内存池的角色,当新的页需要加载到 Buffer Pool 中时,如果没有空闲空间,InnoDB 会从 Free List 中获取空闲的页来加载新数据。

作用:为即将载入 Buffer Pool 的新页提供空闲空间。

与 LRU List 的关系:当页被淘汰出 LRU List 后,如果不再需要使用,该页会被加入到 Free List 中。

2)LRU List (最近最少使用链表)

描述:最近最少使用链,主要负责在缓冲池满时淘汰缓冲页。

LRU List (Least Recently Used) 是 Buffer Pool 的核心链表,负责管理已经载入内存但尚未被淘汰的页。页的访问顺序决定了它们在 LRU List 中的位置。最近被访问的页会移动到 LRU List 的热端(热链表),而较少访问的页会逐渐滑向冷端(冷链表)。

作用:用于决定哪些页可以保留在 Buffer Pool 中,以提高缓存的命中率。最近访问频繁的页保留在热端,而不常访问的页可能会被淘汰,腾出空间给新页。

buffer pool采用LRU(Least Recently Used)算法来管理页。如果buffer pool已满,则会根据LRU策略淘汰最久未使用的页,以腾出空间来加载新的页。

与 Free List 的关系:当页在 LRU List 中到达冷端并且不再使用时,可能会被从 LRU List 中移除并放入 Free List

3)Flush List (刷新链表)

描述:脏链,主要负责管理要被刷新到磁盘的页。

Flush List 维护了所有脏页的列表,这些页已经在内存中被修改,但还没有将修改后的数据写回磁盘。当需要释放 Buffer Pool 的内存或在适当的时间点时,脏页会从 Flush List 中被取出,并被刷写到磁盘以保证数据的一致性。

作用:负责将脏页刷新到磁盘,以确保事务提交时的数据一致性和持久性。

与 LRU List 的关系:当一个页在 Buffer Pool 中被修改后,它会同时存在于 LRU List 和 Flush List 中。脏页仍然会按照 LRU 规则进行管理,但它们同时在 Flush List 中等待被刷新到磁盘。

三条链表之间的关系

1)加载新页:当从磁盘加载一个新页到 Buffer Pool 时,InnoDB 会优先查看 Free List 中是否有可用的空闲页。如果有,则从 Free List 中分配;如果没有,则从 LRU List 的冷端淘汰较少使用的页,并将其放入 Free List

2)访问页:当某个页在 Buffer Pool 中被访问时,该页会被移动到 LRU List 的热端,以防止它被淘汰。

3)页的修改和刷新

  • 当页被修改时,它会被标记为脏页并加入到 Flush List 中。此时它仍然存在于 LRU List 中,按照访问频率进行排序。
  • 后台线程会周期性地扫描 Flush List,将脏页刷写回磁盘,以减少 Buffer Pool 中的脏页数量。
  • 淘汰页:当 LRU List 中的页达到冷端并且未被访问时,它可能会被淘汰出 Buffer Pool。如果它是脏页,则需要先将其刷新到磁盘,然后再移出 LRU List 并加入 Free List

这三条链表通过协同工作,高效地管理 Buffer Pool 中的页,使得 MySQL 能够在高并发环境下保持性能和数据的一致性。

2、怎么样在buffer pool中找到我们想要的页?

(1)通过 B+ 树索引定位页

InnoDB 使用 B+ 树索引结构来存储数据表中的数据,这种索引结构使得查找某一特定页的过程高效且快速。定位页的过程如下:

  • 索引查找:如果一个 SQL 语句通过索引来访问数据(如通过 WHERE 子句指定的条件),则 InnoDB 会先查找索引 B+ 树中的叶节点,以定位数据页或记录所在的具体位置。
  • 叶节点和页映射:B+ 树的叶节点包含了索引列的值和指向包含实际数据行的页的指针。通过这些指针,InnoDB 可以直接定位到存储目标数据的页。

(2) 检查页是否在 Buffer Pool 中

一旦 InnoDB 知道了需要访问的页的地址(page_id),它会在 buffer pool 中查找该页是否已经被加载。

buffer pool 内部使用哈希表来加速页的查找。每个数据页在 buffer pool 中都有一个对应的哈希表项。

  • 哈希表的键通常是由 page_id 构成,它唯一标识了页在 buffer pool 中的位置。
  • 哈希表项包含信息:每个哈希表项都包含页的元数据,如页所在的内存地址、页的状态(如脏页、已锁定等)、页的引用计数等。

因此,InnoDB 通过计算哈希值,快速在哈希表中找到对应的页控制块(Page Control Block)。

(3)页的状态检查

如果页在 buffer pool 中找到了,InnoDB 需要进一步检查页的状态,以确保页可以被当前事务访问:

  • 引用计数(ref_count):表示页被引用的次数。如果页的引用计数大于零,说明页当前正被其他事务或查询使用。
  • 锁状态:InnoDB 需要检查页是否被其他事务锁定,以及锁的类型(如共享锁、排他锁等)。根据当前事务的需要,决定是等待锁释放还是直接访问页。

(4)从磁盘加载页到 Buffer Pool

如果在 buffer pool 中未找到所需的页,或者页的状态不满足当前事务的需要,InnoDB 会从磁盘加载页到 buffer pool 中:

  • 从 Free List 获取空闲页:如果 buffer pool 中有可用的空闲页,InnoDB 会从 Free List 中获取一个空闲页,准备加载新数据。
  • 从磁盘读取页:InnoDB 根据 page_id,从表空间文件中读取页的数据到 buffer pool 的空闲页中。
  • 更新哈希表:新加载的页会被添加到哈希表中,并且更新其页控制块的信息。

(5)将页插入到 LRU List 中

新加载的页或被访问的页会被插入到 LRU List 的热端,表示这是最近使用的页,应该保留在内存中以加速未来的访问。

如果 buffer pool 已满,LRU List 的冷端的页可能会被淘汰。这些页要么被移入 Free List,要么被写入磁盘(如果是脏页)。

(6)返回页给事务

一旦页被成功地定位并加载到 buffer pool 中,InnoDB 就可以将页返回给当前事务,供其进行读或写操作。

(7)维护页的一致性

在页被使用过程中,InnoDB 会通过一系列机制来确保数据的一致性和完整性:

  • 锁定和并发控制:InnoDB 通过锁机制(如行锁、间隙锁等)来控制并发事务对页的访问,避免数据不一致的情况。
  • 脏页管理:修改过的页会被标记为脏页,并加入到 Flush List 中,等待后台线程将其写回磁盘。
  • 日志记录:InnoDB 还会将对页的修改记录到 redo log 和 undo log 中,以支持崩溃恢复和事务回滚。

3、解析页的过程

怎样从一个16k的数据页中找到我们想要的数据?

以数据页为例,页的结构

实际上每条数据在物理上是乱序的,逻辑上用一个单向链表连接起来。链表以infimum和supremum这两个特殊记录标记首尾。infimum指向第一条记录,最后一条记录指向supremum。

每个数据页都有一个页目录,它包含指向页中若干记录的指针(通常是记录头部)。这些指针指向的记录是页中某些分段的起点。因此,页最后的pagedirectory结构(类似数组)可以帮我们加快对页的搜索。

由于记录是有序存储的,InnoDB 使用二分查找算法快速定位到目标记录在页中的位置。

查找一个数据页时,就会在Page Directory进行二分查找每次查找都要取到槽对应的行进行主键的比较;直到最终找到数据或确认没有对应的数据。

  • InnoDB 先从页目录中找到接近目标记录的指针,再从这个指针开始顺序扫描记录,直到找到目标记录。这种方式比全表扫描更高效。
  • 在页中,记录通过双向链表链接起来。每条记录有指向前后记录的指针(next_record 和 prev_record),InnoDB 可以通过这个链表依次遍历页中的记录。
  • 一旦定位到目标记录的起始位置,InnoDB 会沿着链表遍历,通过next_record,直到找到与查询条件匹配的记录。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水w

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值