MySQL技术内幕 InnoDB存储引擎 笔记 第一二章

date
Jul 27, 2022
slug
MySQL技术内幕 InnoDB存储引擎 笔记
status
Published
tags
读书笔记
summary
MySQL技术内幕 InnoDB存储引擎 笔记
type
Post

第一章 MySQL体系结构和存储引擎

  • 数据库:物理操作系统文件或其他形式文件类型的集合
  • 实例:真正操作数据库文件的。MySQL数据库由后台线程以及一个共享内存区组成。MySQL数据库实例在系统的表现上就是一个进程
MySQL数据库启动时,可以没有配置文件,在这种情况下,MySQL会按照编译时的默认参数设置启动实例。
notion image
MySQL由以下部分组成
  • 连接池组件
  • 管理服务和工具组件
  • SQL接口组件
  • 查询分析器组件
  • 优化器组件
  • 缓冲组件
  • 插件式存储引擎
  • 物理文件
存储引擎是基于表的,而不是数据库。

InnoDB存储引擎

  • 支持事务,主要面向在线事务处理(OLTP)的应用
  • 特点是行锁设计、支持外键,支持非锁定读
  • 通过多版本并发控制(MVCC)来获得高并发性,实现了SQL标准的4种隔离级别,默认为REPEATABLE级别。使用next-key-locking的策略来避免幻读现象的产生。
  • 提供了插入缓冲、二次写、自适应哈希索引、预读等高性能和高可用的功能
  • 对于表中数据的存储,InnoDB采用了聚集的方式,因此每张表的存储都是按主键的顺序进行存放。如果没有显式的指定主键,InnoDB存储引擎会为每一行生成一个6字节的ROWID,以此作为主键

MyISAM 存储引擎

  • 不支持事务、表锁设计,支持全文索引,主要面向一些OLAP数据库应用
  • 缓冲池只缓存索引文件,而不缓存数据文件
  • MyISAM存储引擎表由MYD和MYI组成,MYD用来存放数据文件,MYI用来存放索引文件。

第二章 InnoDB存储引擎

notion image
 

后台线程

后台线程的主要作用是负责刷新内存池中的数据,保证缓冲池中的内存缓存是最近的数据。将已修改的数据文件刷新到磁盘文件,同时保证在数据库发生异常的情况下InnoDB能恢复到正常运行状态。
InnoDB是多线程的模型,所以有多种后台线程:
  • Master Thread。是非常核心的后台线程。主要负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性,包括脏页的刷新、合并插入缓冲、UNDO页的回收
  • IO Thread。InnoDB使用了大量的AIO来处理写IO请求,而IO Thread的工作主要是负责这些IO请求的回调处理。
  • Purge Thread。事务被提交后,其所使用的undolog可能不再需要,因此需要Purge Thread来回收已经使用并分配的undo页。在配置文件中添加“innodb_purge_threads=1”来启用独立的Purge Thread
  • Page Cleaner Thread。将之前版本中脏页的刷新操作都放入到单独的线程中来完成

缓冲池

  • 在数据库进行读取页的操作,首先将从磁盘读到的夜放在缓冲池中,下一次读,先看缓冲池,没有再去磁盘
  • 对于页的修改操作,首先修改在缓冲池中的页,然后再以一定的频率刷新到磁盘上,通过Checkpoint机制刷新回磁盘
      notion image
缓冲池的大小通过参数“innodb_buffer_pool_size“进行设置
多个缓冲池实例配置:innodb_buffer_pool_instances进行配置

LRU List、Free List和Flush List

InnoDB存储引擎中,缓冲池页大小默认为16KB,使用优化过的LRU算法对缓冲池进行管理。
新读取到的页,虽然是最新访问的页,但并不是直接放入到LRU列表的首部,而是放入到LRU列表的midpoint位置。默认位置是LRU列表长度的5/8处。位置可由参数innodb_old_blocks_pct控制。
把midpoint之后的列表称为old列表,之前的列表称为new列表,new列表中的页都是最为活跃的热点数据。
参数innodb_old_blocks_time,表示页读取到mid位置后需要等待多久才会被加入到LRU列表的热端。
当数据库刚启动时,LRU列表是空的,即没有任何的页。这时页都存放在Free列表中,当需要从缓冲池中分也时,首先从Free列表中查找页,有则将该页从Free删除,放入LRU列表中。否则,根据LRU算法,淘汰LRU列表末尾的页,将该内存空间分配给新的页。当页从LRU列表的old部分加入到new部分时,称此操作为page made young,而因为innodb_old_blocks_time的设置而导致页没有从old部分移动到new 的操作称为page not made young。可以通过SHOW ENGINE INNODB STATUS来观察LRU列表及Free列表的情况。Buffer pool hit rate,表示缓冲池的命中率,通常该值不应该小于95%。若发生小于95%这种情况,需要观察是否时由于全表扫描引起的LRU列表被污染的问题。
LRU列表中的页被修改后,称该页为脏页,脏页既存在于LRU列表中,也存在于Flush列表中。

重做日志缓冲

InnoDB存储引擎首先将重做日志信息放到这个缓冲区,然后按一定频率将其刷新到重做日志文件。该缓冲不需要设置的很大,一般每一秒会将重做日志缓冲刷新到日志文件。由参数innodb_log_buffer_size控制,默认为8MB。
重做日志缓冲刷盘的情况:
  • Master Thread每一秒将重做日志缓冲刷到日志文件中
  • 每个事务提交时会将重做日志缓冲刷新到重做日志文件
  • 当重做日志缓冲池剩余空间小于1/2时,重做日志缓冲刷新到重做日志文件

Checkpoint技术

为了避免数据丢失的问题,当前事务数据库系统普遍采用了Write Ahead Log策略,即当事务提交时,先写重做日志,再修改页。当由于发生宕机而导致数据丢失时,通过重做日志来恢复数据,也是ACID中的D的要求
InnoDB存储引擎内部,有两种Checkpoint:
  • Sharp Checkpoint:发生在数据库关闭时将所有的脏页都刷新回磁盘,这是默认的工作方式,即参数innodb_fast_shutdown=1
  • Fuzzy Checkpoint:只刷新一部分脏页。细分为:
    • Master Thread Checkpoint:以每秒或者每十秒的速度将脏页按一定的比例异步刷回磁盘。
    • FLUSH_LRU_LIST Checkpoint :需要保证LRU列表中需要有差不多100个空闲页可供使用。如果没有100个,会将LRU列表尾端的页移除。如果有脏页,进行刷盘
    • Async/Sync Flush Checkpoint:如果重做日志不可用,此时需要强制将一些页刷新回磁盘。该操作是为了保证重做日志的循环使用的可用性
    • Dirty Page too much Checkpoint:脏页的数量太多,进行刷盘。目的是为了保证缓冲池中有足够可用的页。由参数innodb_max_dirty_pages_pct控制。75表示,当缓冲池中脏页的数量占据75%时,刷盘。

Master Thread 工作方式

Master Thread 具有最高的线程优先级别。由多个循环组成:
  • 主循环
  • 后台循环
  • 刷新循环
  • 暂停循环
主循环
两大部分操作:每秒的操作和每10秒的操作。通过thread sleep实现,这意味着时间间隔是不精确的。在负载很大的情况下可能会有延迟。
每秒一次的操作:
  • 日志缓冲刷新到磁盘,即使这个事务还没有提交(总是)
  • 合并插入缓冲(可能)
  • 至多刷新100个InnoDB的缓冲池中的脏页到磁盘(可能)
  • 如果当前没有用户活动,则切换到background loop(可能)
每10秒的操作:
  • 刷新100个脏页到磁盘(可能)
  • 合并至多5个插入缓冲(总是)
  • 将日志缓冲刷新到磁盘(总是)
  • 删除无用的Undo页(总是)
  • 刷新100个或者10个脏页到磁盘(总是)
若当前数据库空闲或者数据库关闭,就会切换到background loop循环。会执行以下操作:
  • 删除无用的Undo页(总是)
  • 合并20个插入缓冲(总是)
  • 跳回到主循环(总是)
  • 不断刷新100个页直到符合条件(可能,跳转到flush loop中完成)
若flush loop中也没有事情做了,InnoDB存储引擎就会切换到suspend loop,将Master Thread挂起,等待事件的发生。
Master Thread 伪代码:
 
notion image

InnoDB关键特性

  • 插入缓冲
  • 两次写
  • 自适应哈希索引
  • 异步IO
  • 刷新邻接页

插入缓冲(Insert Buffer)

InnoDB缓冲池中有Insert Buffer的信息,但Insert Buffer和数据页一样,也是物理页的一个组成部分
对于非聚集索引的插入或者更新操作,不是每一个直接插入到索引页中,而是先判断插入的非聚集索引页是否在缓冲池中,若在,则直接插入。若不在,则先放入到一个Insert Buffer中。然后以一定的频率和情况进行Insert Buffer和辅助索引页子节点的merge操作(这时通常能将多个插入操作合并到一个操作中,因为在一个索引页中)
使用Insert Buffer需要同时满足以下两个条件:
  • 索引是辅助索引
  • 索引不是唯一的
极端情况
大量涉及到不唯一非聚集索引的插入操作,也就是使用了Insert Buffer,若此时MySQL发生宕机,会有大量的Insert Buffer没有合并到实际的索引中,这时恢复可能需要很长的时间。
目前Insert Buffer存在的一个问题是:在写密集的情况下,插入缓冲会占用过多的缓冲池内存,默认最大可以占到1/2的缓冲池内存。
SHOW ENGINE INNODB STATUS 来查看插入缓冲的信息
Change Buffer
可以对DML操作——Insert、DELETE、UPDATE进行缓冲,分别是Insert Buffer、Delete Buffer、Purge Buffer。
Change Buffer适用的对象依然是非唯一的辅助索引
对一条记录进行UPDATE操作可能分为两个过程:
  • 将记录标记为已删除(Delete Buffer)
  • 真正将记录删除(Purge Buffer)
Innodb_change_buffering,用来开启各种Buffer
Innodb_change_buffer_max_size,控制Change Buffer最大使用内存的数量,默认为25,表示最多使用1/4的缓冲池内存空间,最大有效值为50
Insert Buffer的内部实现
数据结构是一棵全局B+树,非叶子节点存放的是查询的search key,存放在共享表空间中,默认就是ibdata1中。
通过ibd文件进行数据恢复后,还需要进行REPAIR TABLE操作来重建表上所有的辅助索引,因为表的辅助索引中的数据可能还在Insert Buffer中。
当一个辅助索引要插入到页中,如果这个页不在缓冲池中,那么InnoDB存储引擎首先构造一个search key,接下来查询Insert Buffer这棵B+树,然后再将这条记录插入到Insert Buffer B+树的叶子节点中。
又一个页用来标记每个辅助索引页的可用空间,该页的类型为Insert Buffer Bitmap
Merge Insert Buffer
merge操作发生于:
  • 辅助索引页被读取到缓冲池时
  • Insert Buffer Bitmap页追踪到该辅助索引页已无可用空间时
  • Master Thread
对于Mer个中Insert Buffer页的选择,InnoDB随机选择Insert Buffer B+树的一个页,读取该页中的space及之后所需数量的页。同时,若进行merge时,要进行merge的表已经删除,此时可以直接丢弃已经被Insert/Change Buffer 的数据记录

两次写

提升数据页的可靠性
notion image
 
重做日志记录的是对页的物理操作。如果这个页本身已经发生了损坏,再对其进行重做是没有意义的。所以,在应用重做日志前,需要一个页的副本,当写入失效发生时,先通过页的副本还原该页,再进行重做,这就是doublewrite。
由两部分组成,一部分是内存中的doublewrite buffer,大小为2MB,另一部分是物理磁盘上共享表空间中连续的128个页,即2个区,大小同样为2MB。在对缓冲池的脏页进行刷新时,先将脏页复制到内存中的doublewrite buffer,之后分两次,每次1MB顺序写到共享表空间的物理磁盘上,然后马上同步磁盘。

自适应哈希索引(Adaptive Hash Index,AHI)

InnoDB存储引擎会监控对表上各索引页的查询。如果观察到建立哈希索引可以带来速度提升,则建立哈希索引,称之为自适应哈希索引,AHI是对缓冲池的B+树页构造而来。
AHI建立的要求:
  • AHI需要对这个页的连续访问模式必须时一样的,访问模式一样指的是查询的条件一样。
  • 以该模式访问了100次
  • 页通过该模式访问了N次,其中N=页中的记录*1/16
通过命令SHOW ENGINE INNODB STATUS可以看到当前AHI的使用情况
通过参数innodb_adaptive_hash_index来开关该特性

异步IO

优势:
  • 起多个IO请求,当全部IO请求发送完毕后,等待所有IO操作的完成
  • 可以进行IO Merge操作,也就是将多个IO合并为1个IO,可以提高IOPS的性能
参数innodb_user_native_aio来控制是否启用Native AIO,开启能显著提高性能。

刷新邻接页

原理为:当刷新一个脏页时,InnoDB存储引擎会检测该页所在区的所有页,如果是脏页,那么一起进行刷新
参数innodb_flush_neighbors,用来控制是否启用该特性。对于机械硬盘建议启用该特性,对于固态硬盘建议关闭该特性。
 

© 李工 2021 - 2025