MySQL:InnoDB 存储引擎(后台线程、缓冲池、重做日志缓冲、额外内存池)

asdsInnoDB 是 事务安全 的MySQL 存储引擎,设计上采用了类似于Oracle 数据库的架构。通常来说, InnoDB 存储引擎是OLTP应用中核心表的首选存储引擎。同时,也正是因为InnoDB 的存在,才使MySQL 数据库变得更有魅力。本章将详细介绍InnoDB 存储引擎的体系架构及其不同于其他存储引擎的特性。

asdsadasdasdasdsadasdasdasdsadassdasdsadasdasdsadasdsadassadasdas————《MySQL技术内幕INNODB存储引擎》


lnnoDB 存储引擎概述

sssaInnoDB 存储引擎是第一个完整支持 ACID 事务的MySQL 存储引擎

sdsssa注:BDB 是第一个支持事务的MySQL 存储引擎。

sssa特点:行锁设计、支持MVCC 、支持外键、提供一致性非锁定读,同时被设计用来最有效地利用以及使用内存和CPU 。

lnnoDB 存储引擎的版本

sssaInnoDB 存储引擎被包含于所有MySQL 数据库的二进制发行版本中。早期其版本随着 MySQL 数据库的更新而更新。从 MySQL 5.1 版本时, MySQL 数据库允许存储引擎开发商 以动态方式加载引擎,这样存储引擎的更新可以不受MySQL 数据库版本的限制。
在这里插入图片描述
sdsssa注:InnoDB 1.0.x以及老版本,由于不支持多回滚段,InnoDB Plugin 支持的最大支持并发事务数量也被限制在1023 。

lnnoDB 体系架构

在这里插入图片描述
ss InnoDB 存储引擎: 有多个内存块,可以认为这些内存块组成了一个大的内存池,负责如下工作:

sssa①、维护所有进程/线程需要访问的多个内部数据结构

sssa②、缓存磁盘上的数据,方便快速地读取,同时在对磁盘文件的数据修改之前在这里缓存。

sssa③、重做日志(redo log) 缓冲等

ss后台线程:

sssa①、 主要作用是负责刷新内存池中的数据,保证缓冲池中的内存缓存的是最近的数据,此外将已修改的数据文件刷新到磁盘文件。
sssa②、同时保证在数据库发生异常的情况下InnoDB 能恢复到正常运行状态。

后台线程

sssaInnoDB 存储引擎是多线程的模型,因此其后台有多个不同的后台线程,负责处理不同的任务。

sssa①、Master Thread: 是一个非常核心的后台线程,主要负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性(包括脏页的刷新、合并插入缓冲(INSERT BUFFER) 、UNDO 页的回收等)。

sssa②、I/O Thread: 在 lnnoDB 存储引擎中大量使用了 AIO ( Async IO) 来处理写IO 请求,这样可以极大提高数据库的性能。而 IO Thread 的工作主要是负责这些IO 请求的回调(call back)处理。

sdsssa注:InnoDB 1.0 版本之前共有 4 个IO Thread, 分别是write 、read 、insert buffer 和log IO thread。在 Linux 平台下, IO Thread 的数量不能进行调整,但是在Windows 平台下可以通过参数 innodb_file_ io threads 来增大IO Thread。从InnoDB 1.0.x 版本开始, read thread 和write thread 分别增大到了4 个,并且不再使用 innodb_ file _io_threads 参数,而是分别使用 innodb_read_ io threadsinnodb_write_ io _ threads 参数进行设置,可以通过命令SHOW ENGINE INNODB STATUS 来观察InnoDB 中的IO Thread: (注意:读线程的ID 总是小于写线程)

mysql> SHOW ENGINE INNODB STATUS;
......
SHOW ENGINE INNODB STATUS
--------
FILE I/O
--------
I/O thread 0 state: wait Windows aio (insert buffer thread)
I/O thread 1 state: wait Windows aio (log thread)
I/O thread 2 state: wait Windows aio (read thread)
I/O thread 3 state: wait Windows aio (read thread)
I/O thread 4 state: wait Windows aio (read thread)
I/O thread 5 state: wait Windows aio (read thread)
I/O thread 6 state: wait Windows aio (write thread)
I/O thread 7 state: wait Windows aio (write thread)
I/O thread 8 state: wait Windows aio (write thread)
I/O thread 9 state: wait Windows aio (write thread)
......
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
......

sssa③、Purge Thread: 事务被提交后,其所使用的 undo log 可能不再需要,因此需要 PurgeThread 来回收已经使用并分配的 undo 页。
sdsssa注:在 InnoDB 1.1 版本之前, purge 操作仅在InnoDB 存储引擎的 Master Thread 中完成。而从 InnoDB 1.1 版本开始, purge 操作可以独立到单独的线程中进行,以此来减轻 Master Thread 的工作,从而提高 CPU 的使用率以及提升存储引擎的性能。 在 InnoDB 1.1 版本中,即使将innodb_purge_ threads 设为大于1, InnoDB 存储引擎启动时也会将其设为1。InnoDB 1.2 版本开始, InnoDB 支持多个 Purge Thread, 这样做的目的是为了进一步加快undo 页的回收。同时由于 Purge Thread 需要离散地读取undo 页,这样也能更进一步利用磁盘的随机读取性能。

sssa④、Page Cleaner Thread: Page Cleaner Thread 是在InnoDB 1.2.x 版本中引入的。其作用是将之前版本中脏页的刷新操作都放入到单独的线程中来完成 而其目的是为了减轻原Master Thread 的工作及对于用户查询线程的阻塞,进一步提高InnoDB 存储引擎的性能。

内存
sd缓冲池

sssaInnoDB 存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理 因此可将其视为基于磁盘的数据库系统。在数据库系统中,由于CPU速度与磁盘速度之间的鸿沟,基于磁盘的数据库系统通常使用 缓冲池技术 来提高数据库的整体性能。

sssa缓冲池简单来说就是一块内存区域,通过内存的速度来 弥补 磁盘速度较慢对数据库性能的影响。

dssssa①、在数据库中进行读取页的操作,首先将从磁盘读到的页存放在缓冲池中,这个过程称为将页"FIX" 在缓冲池中。下一次再读相同的页时,首先判断该页是否在缓冲池中。若在缓冲池中,称该页在缓冲池中被命中,直接读取该页。否则,读取磁盘上的页。

dssssa②、对于数据库中页的修改操作,则首先修改在缓冲池中的页,然后再以一定的频率刷新到磁盘上。通过一种称为 Checkpoint 的机制刷新回磁盘。同样,这也是为了提高数据库的整体性能。

sdsssa注:缓冲池的大小直接影响着数据库的整体性能. 对于InnoDB 存储引擎而言,其缓冲池的配置通过参数innodb_buffer_pool_ size 来设置。

mysql>SHOW VARIABLES LIKE'innodb buffer pool size'\G;
*************************** 1. row***************************
Variable_name: innodb_buffer_ pool_size
Value: 16106127360
1 row in set (0.00 sec}

dssaInnoDB 存储引擎中内存的结构:
在这里插入图片描述
sssa具体来看,缓冲池中缓存的数据页类型有:索引页、数据页、undo 页、插入缓冲 、自适应哈希索引 、InnoDB 存储的锁信息 、数据字典信息等。

sssaInnoDB 1.0.x 版本开始,允许有多个缓冲池实例每个页根据哈希值平均分配到不同缓冲池实例中。这样做的好处是减少数据库内部的资源竞争,增加数据库的并发处理能力。可以通过参数 innodb_ buffer _pool_instances 来进行配置,该值默认为1 。从MySQL 5.6 版本开始,还可以通过 information_schema 架构下的表 INNODB_ BUFFER_POOL_STATS 来观察缓冲的状态。

mysql> SHOW VARIABLES LIKE ' innodb buffer pool instances ' \G ;
*************************** 1. row***************************
Variable name : innodb buffer pool instances
Value : 1
1 row in set (0 . 00 sec)
sdLRU List 、Free List 和Flush List

sssa①、InnoDB 存储引擎是怎么对这么大的内存区域进行管理的呢?(优化的LRU算法)

ssssa 数据库中的缓冲池是通过LRU (Latest Recent Used, 最近最少使用)算法来进行管理的即最频繁使用的页在LRU 列表的前端,而最少使用的页在LRU 列表的尾端。当缓冲池不能存放新读取到的页时,将首先释放LRU 列表中尾端的页。

ssssa在InnoDB 存储引擎中, 缓冲池中页的大小默认为 16KB, 同样使用LRU 算法对缓冲池进行管理。稍有不同的是 InnoDB 存储引擎对传统的LRU 算法做了一些优化。

ssssa优化:InnoDB 的存储引擎中, LRU 列表中还加入了 midpoint 位置。新读取到的页,虽然是最新访问的页,但并不是直接放入到LRU 列表的首部,而是放入到LRU 列表的 midpoint 位置。这个算法在InnoDB 存储引擎下称为 midpoint insertion strategy 。在默认配置下,该位置在LRU 列表长度的5/8 处。midpoint 位置可由参数 innodb_old_ blocks _pct 控制,如:

mysql> SHOW VARIABLES LIKE'innodb old blocks pct'\G;
*************************** 1. row***************************
Variable name: innodb old blocks pct
Value : 37
1 row in set (0.00 sec) 
#参数innodb_old_ blocks pct 默认值为37, 表示新读取的页插入到LRU 列表尾端的37% 的位置(差不多3/8 的位置)

ssssaInnoDB 存储引擎中,把 midpoint 之后的列表称为 old 列表,之前的列表称为 new 列表。可以简单地理解为new列表中的页都是最为活跃的热点数据。
在这里插入图片描述
sssa②、为什么不采用朴素的 LRU 算法,直接将读取的页放入到LRU 列表的首部呢??

ssssa 因为若直接将读取到的页放入到 LRU 的首部,那么某些 SQL 操作可能会使缓冲池中的页被刷新出,从而影响缓冲池的效率。而如果这些页通常来说又仅在这次操作中需要,并不是活跃的热点数据。如果页被放入LRU 列表的首部,那么非常可能将所需要的热点数据页从LRU 列表中移除,而在下一次需要读取该页时, InnoDB 存储引擎需要再次访问磁盘

ssssa 为了解决这个问题, InnoDB 存储引擎引入了另一个参数来进一步管理LRU 列表,这个参数是 innodb_old_blocks_time, 用于表示页读取到mid 位置后需要等待多久才会被加入到LRU 列表的热端。

mysql> SET GLOBAL innodb_old_blocks_time=lOOO;
Query OK, 0 rows affected (0.00 sec)
# data or index scan operation
...
mysql> SET GLOBAL innodb_old_ blocks_time=O;
Query OK, 0 rows affected (0.00 sec)

ssssaLRU 列表用来管理已经读取的页,但当数据库刚启动时, LRU 列表是空的,即没有任何的页。 这时页都存放在 Free 列表中。当需要从缓冲池中分页时,首先从Free 列表中查找是否有可用的空闲页,若有则将该页从Free 列表中删除,放入到 LRU 列表中。
ssssa否则,根据 LRU 算法,淘汰 LRU 列表末尾的页,将该内存空间分配给新的页。当页从 LRU 列表的 old 部分加入到 new 部分时,称此时发生的操作为 page made young, 而因为 innodb_old_ blocks_ time 的设置而导致页没有从 old 部分移动到new 部分的操作称为page not made young.

sdsssa注:可以通过命令 SHOW ENGINE INNODB STATUS 来观察 LRU 列表及Free 列表的使用情况和运行状态。

mysql> SHOW ENGINE INNODB STATUS \G;
*************************** 1. row***************************
Type: InnoDB
Name:
Status:
120725 22:04:25 INNODB MONITOR OUTPUT
Per second averages calculated from the last 24 seconds
Buffer pool size 327679	 		#缓冲池的页数
Free buffers 0				    #Fre列表中的页数
Database pages 307717    		#LRU列表中的页数
Old database pages 113570
Modified db pages 24673
Pending reads 0
Pending writes: LRU 0, flush list 0, single page O
Pages made young 6448526, not young 0    
#显示了LRU 列表中页移动到前端的次数,因为该服务器在运行阶段没有改变innodb_old_blocks_time 的值,因此not young 为0。
48.75 youngs/s, 0.00 non-youngs/s
Pages read 5354420 , created 239625, written 3486063
55.68 reads / s, 81.74 creates/s, 955.88 writes/s
Buffer pool hit rate 1000 / 1000, young-making rate O / 1000 not O / 1000  #缓冲池的命中率
...

sssa通过命令 SHOW ENGINE INNODB STATUS 可以看到:当前 Buffer pool size 共有 327 679 个页,即 327679*16K, 总共 5GB 的缓冲池。Free buffers 表示当前 Free 列表中页的数量, Database pages 表示 LRU 列表中页的数量。可能的情况是 Free buffersDatabase pages 的数量之和不等于 Buffer pool size (说明Free列表和LRU列表基本包好了缓冲池中所有的页)(因为缓冲池中的页还可能会被分配给自适应哈希索引、Lock 信息、Insert Buffer 等页,而这部分页不需要LRU 算法进行维护,因此不存在于 LRU 列表中)。
sdsssapages made young 显示了LRU 列表中页移动到前端的次数,因为该服务器在运行阶段没有改变 innodb_old_blocks_time 的值,因此 not young 为0
sdsssaBuffer pool hit rate, 表示缓冲池的命中率,这个例子中为100%, 说明缓冲池运行状态非常良好。通常该值不应该小于95% 。若发生Buffer pool hit rate 的值小于95% 这种情况 ,用户需要观察是否是由于全表扫描引起的LRU 列表被污染的问题
sdsssa执行命令SHOW ENGINE INNODB STATUS 显示的不是当前的状态, 而是过去某个时间范围内InnoDB 存储引擎的状态。从上面的例子可以发现, Persecond averages calculated from the last 24 seconds 代表的信息为过去24 秒内的数据库状态。

sssa可以通过表 INNODB_BUFFER_PAGE_LRU 来观察每个LRU 列表中每个页的具体信息, 例如通过下面的语句可以看到缓冲池 LRU 列表中 SPACE1 的表的页类型。

sssaInnoDB 存储引擎从1.0.x 版本开始支持压缩页的功能,即将原本16KB 的页压缩为1KB 、2KB 、4KB 和8KB 。而由于页的大小发生了变化, LRU 列表也有了些许的改变。对于非16KB 的页,是通过 unzip_LRU 列表进行管理的。

mysql> SHOW ENGINE INNODB STATUS\G;
...
Buffer pool hit rate 999 / 1000, young-making rate O / 1000 not O / 1000
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 1539, unzip_LRU len: 156
I/0 sum[O] : cur[O], unzip sum[O] :cur[O]
...

sssa可以看到 LRU 列表中一共有 1539 个页,而 unzip_LRU 列表中有 156 个页。这里需要注意的是, LRU 中的页包含了 unzip_LRU 列表中的页。

sssa③、unzip_LRU 是怎样从缓冲池中分配内存的呢??

ssssa 首先,在 unzip_LRU 列表中对不同压缩页大小的页进行分别管理。其次,通过 伙伴算法 进行内存的分配。例如对需要从缓冲池中申请页为4KB 的大小,其过程如下:

sssssa ⒈、检查 4KBunzip_LRU 列表,检查是否有可用的空闲页;

sssssa ⒉、若有,则直接使用;

sssssa ⒊、否则,检查 8KBunzip_LRU 列表;

sssssa ⒋、若能够得到空闲页,将页分成 24KB 页,存放到 4KBunzip_LRU 列表;

ss sssa⒌ 、若不能得到空闲页,从 LRU 列表中申请一个 16KB 的页,将页分为1 个8KB 的页、2 个4KB 的页,分别存放到对应的 unzip_LRU 列表中。

sssa④、什么是脏页??

ssssaLRU 列表中的页被修改后,称该页为 脏页(dirty page), 即缓冲池中的页和磁盘上的页的数据产生了不一致。这时数据库会通过 CHECKPOINT 机制 将脏页刷新回磁盘,而 Flush 列表 中的页即为 脏页列表。需要注意的是,脏页既存在于LRU 列表中,也存在于Flush 列表中LRU 列表用来管理缓冲池中页的可用性,Flush 列表用来管理将页刷新回磁盘, 二者互不影响。

sdsssa注:同 LRU 列表一样, Flush 列表也可以通过命令SHOW ENGINE INNODB STATUS 来查看。脏页同样存在于 LRU 列表中,故用户可以通过元数据表 INNODB_BUFFER_PAGE_LRU 来查看。

sd重做日志缓冲

ssssaInnoDB 存储引擎的内存区域除了有缓冲池外,还有 重做日志缓冲(redo log buffer)lnnoDB 存储引擎 首先将重做日志信息先放入到这个缓冲区,然后按一定频率将其刷新到重做日志文件。
sdsssa注:重做日志缓冲一般不需要设置得很大,因为一般情况下每一秒钟会将重做日志缓冲刷新到日志文件,因此用户只需要保证每秒产生的事务量在这个缓冲大小之内即可。 该值可由配置参数innodb_log_ buffer_ size 控制,默认为8MB:

mysql> SHOW VARIABLES LIKE'innodb_log_buffer_size'\G;
*************************** 1. row***************************
Variable_name: innodb_log_buffe r_size
Value : 8388608
1 row in set (0.00 sec)

sssa①、重做日志在下列三种情况下会将重做日志缓冲中的内容刷新到外部磁盘的重做日志文件中?

ssssa ⒈、Master Thread 每一秒将重做日志缓冲刷新到重做日志文件;

ssssa ⒉、每个事务提交时 会将重做日志缓冲刷新到重做日志文件;

ssssa ⒊、当重做日志缓冲池剩余空间小于1/2 时, 重做日志缓冲刷新到重做日志文件。

sd额外的内存池

ssssa 额外的内存池通常被DBA 忽略,他们认为该值并不十分重要,事实恰恰相反,该值同样十分重要。在InnoDB 存储引擎中,对内存的管理是通过一种称为 内存堆(heap)的方式 进行的。在对一些数据结构本身的内存进行分配时, 需要从额外的内存池中进行申请,当该区域的内存不够时,会从缓冲池中进行申请。例如,分配了缓冲池(innodb_buffer _pool), 但是每个缓冲池中的帧缓冲(frame buffer) 还有对应的缓冲控制对象(buffer control block), 这些对象记录了一些诸如LRU 、锁、等待等信息,而这个对象的内存需要从额外内存池中申请。因此,在申请了很大的InnoDB 缓冲池时,也应考虑相应地增加这个值

💖感谢各位的暴击三连~💖

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值