InnoDB体系结构

下图显示了构成InnoDB存储引擎体系结构的内存和磁盘结构。
在这里插入图片描述

1. 内存结构

InnoDB内存包含Buffer PoolChange BufferAdaptive Hash IndexRedo Log Buffer

1.1 Buffer pool

Buffer pool是内存中的一个主要区域,用于在访问时缓存表和索引数据。

Buffer pool 允许直接从内存中处理经常使用的数据,从而加快处理速度, 在专用MySQL服务器上,通常会将最多80%的物理内存分配给Buffer pool。

为了提高高容量读取操作的效率,buffer pool 被分成可以容纳多行的page。
为了提高buffer管理的效率,buffer pool 被实现为链接的页面列表;
使用LRU算法的变体,很少使用的数据在cache中被淘汰。

1.1.1 Buffer Pool LRU 算法

使用最近最少使用(LRU)算法的变体将buffer pool 以列表进行管理。 该算法采用“中点插入法”:当插入一个新page时,移除表尾最近最少使用的page,在中点插入新page。

这个中点将链表分为两部分:

  • 靠近表头的一部分,为young区,这里的page是最近使用的节点

  • 靠近表尾的一部分,为old区,这里的page是最近少使用的

innodb-buffer-pool-list

该算法通过链表中的page的使用热度来维持各page的位置,其中old区的page为链表满的时候移除的候选区。

默认情况下,算法操作如下:

  • 链表的3/8被设置为old区

  • 中点不是链表的中间点,而是old区的表头节点,即old区与young区的相邻的那个节点

  • 当读取的数据不在缓冲池里的时候,读取到的page需要插入到链表中,插入点为中点,但是插入的新节点为old区的节点,如果此时old区满了得话,移除表尾的page(LRU节点)

  • 当读取old区的page时,该节点将变成“young”节点:此节点移动到young区的表头(young区的头部那里)

  • 在数据库操作中,被访问的节点将移除到young的表头,这样一来,在young区中的未被访问的节点将逐渐往表尾移动,当移动过中点,将变为old区的节点。而old区的节点若被访问到将变为young节点移动到表头,而old区中的为被访问的节点依旧往表尾移动,当表满时,表尾那个page将会被淘汰掉

1.1.2 配置 Buffer Pool

参考博客如何在MySQL中分配innodb_buffer_pool_size

1.2 Change Buffer

change buffer是一种特殊的数据结构,当要修改的辅助索引页不在buffer pool中时,用来cache对辅助索引页的修改。对辅助索引页的操作可能是insert、update和delete操作。等到相关的索引页被读入buffer pool中后,才会使用change buffer中的内容对辅助索引页进行修改(即merge操作)。
innodb-change-buffer

和聚集索引不同,辅助索引通常是不唯一的,插入辅助索引通常也是随机的。同样,对辅助索引的删除、更新也通常是不连续的。等到相关的索引页被读入buffer pool中后,才会使用change buffer中的内容对辅助索引页进行修改(即merge操作)可以避免大量的磁盘随机访问I/O。

间歇性的,在系统空闲或关闭过程中,会执行purge操作,将新的索引页写入磁盘。purge操作一次写多个索引值会比每次修改后就立即写入磁盘的效率高。

如果被更新的辅助索引行比较多,对change buffer的merge可能需要好几个小时。在merge过程中,磁盘的I/O会增加,可能会引起其他查询的性能的降低。merge操作也可能发生在事务提交后。事实上,即使在实例重启后,还会可能发生merge操作。

在内存中,change buffer会占用buffer pool的空间;在物理磁盘上,change buffer是system tablespace的一部分,所以对索引的修改在数据库重启后仍然存在change buffer中。

change buffer中缓存的数据类型由innodb_change_buffering变量控制。

如果索引包含降序索引列或主键包含降序索引列,则不支持对辅助索引进行更改缓冲。

1.2.1 配置 change buffer

可以通过参数innodb_change_buffering来控制是否启用change buffer。

`all`:  默认值。开启buffer inserts、delete-marking operations、purges
`none`: 不开启change buffer
`inserts`:  只是开启buffer insert操作
`deletes`:  只是开delete-marking操作
`changes`:  开启buffer insert操作和delete-marking操作
`purges`:   对只是在后台执行的物理删除操作开启buffer功能
1.2.2 配置 change buffer 最大大小

从5.6.2开始,参数innodb_change_buffer_max_size设置了change buffer可以占用buffer pool的百分比,默认是25,最大可以设置为50。

mysql> show variables like '%change%';
+-------------------------------+-------+
| Variable_name                 | Value |
+-------------------------------+-------+
| innodb_change_buffer_max_size | 25    |
| innodb_change_buffering       | all   |
| session_track_state_change    | OFF   |
+-------------------------------+-------+
3 rows in set (0.00 sec)

1.2.3 监控 change buffer
mysql> SHOW ENGINE INNODB STATUS\G
...
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 11, seg size 13, 573 merges
merged operations:
 insert 304, delete mark 411, delete 0
discarded operations:
 insert 0, delete mark 0, delete 0
Hash table size 664091, node heap has 0 buffer(s)
Hash table size 664091, node heap has 0 buffer(s)
Hash table size 664091, node heap has 0 buffer(s)
Hash table size 664091, node heap has 0 buffer(s)
Hash table size 664091, node heap has 0 buffer(s)
Hash table size 664091, node heap has 57 buffer(s)
Hash table size 664091, node heap has 0 buffer(s)
Hash table size 664091, node heap has 0 buffer(s)
15000.00 hash searches/s, 43000.00 non-hash searches/s
...

information_schema.innodb_metrics表提供了change buffer的统计信息名称和说明:

mysql> SELECT NAME, COMMENT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME LIKE '%ibuf%'
    -> ;
+-----------------------------------------+-------------------------------------------------------------+
| NAME                                    | COMMENT                                                     |
+-----------------------------------------+-------------------------------------------------------------+
| buffer_page_read_index_ibuf_leaf        | Number of Insert Buffer Index Leaf Pages read               |
| buffer_page_read_index_ibuf_non_leaf    | Number of Insert Buffer Index Non-Leaf Pages read           |
| buffer_page_read_ibuf_free_list         | Number of Insert Buffer Free List Pages read                |
| buffer_page_read_ibuf_bitmap            | Number of Insert Buffer Bitmap Pages read                   |
| buffer_page_written_index_ibuf_leaf     | Number of Insert Buffer Index Leaf Pages written            |
| buffer_page_written_index_ibuf_non_leaf | Number of Insert Buffer Index Non-Leaf Pages written        |
| buffer_page_written_ibuf_free_list      | Number of Insert Buffer Free List Pages written             |
| buffer_page_written_ibuf_bitmap         | Number of Insert Buffer Bitmap Pages written                |
| ibuf_merges_insert                      | Number of inserted records merged by change buffering       |
| ibuf_merges_delete_mark                 | Number of deleted records merged by change buffering        |
| ibuf_merges_delete                      | Number of purge records merged by change buffering          |
| ibuf_merges_discard_insert              | Number of insert merged operations discarded                |
| ibuf_merges_discard_delete_mark         | Number of deleted merged operations discarded               |
| ibuf_merges_discard_delete              | Number of purge merged  operations discarded                |
| ibuf_merges                             | Number of change buffer merges                              |
| ibuf_size                               | Change buffer size in pages                                 |
| innodb_ibuf_merge_usec                  | Time (in microseconds) spent to process change buffer merge |
+-----------------------------------------+-------------------------------------------------------------+
17 rows in set (0.01 sec)

information_schema.innodb_buffer_page提供了buffer pool中每个页的元数据,包含change buffer 索引页和change buffer位图页。change buffer页中page_type.ibuf_index表示change buffer索引页;page_type.ibuf_bitmap表示change buffer位图页。

提醒:查看innodb_buffer_page会有很大的性能开销。最好是在空闲时间或测试环境执行。

mysql> SELECT (SELECT COUNT(*) FROM INFORMATION_SCHEMA.INNODB_BUFFER_PAGE
    ->        WHERE PAGE_TYPE LIKE 'IBUF%') AS change_buffer_pages, 
    ->        (SELECT COUNT(*) FROM INFORMATION_SCHEMA.INNODB_BUFFER_PAGE) AS total_pages,
    ->        (SELECT ((change_buffer_pages/total_pages)*100)) 
    ->        AS change_buffer_page_percentage;
+---------------------+-------------+-------------------------------+
| change_buffer_pages | total_pages | change_buffer_page_percentage |
+---------------------+-------------+-------------------------------+
|                  57 |      163820 |                        0.0348 |
+---------------------+-------------+-------------------------------+
1 row in set (0.93 sec)

performance_schema还为高级性能监控提供了change buffer mutex的等待指令:

mysql> SELECT * FROM performance_schema.setup_instruments
    ->        WHERE NAME LIKE '%wait/synch/mutex/innodb/ibuf%';
+-------------------------------------------------------+---------+-------+
| NAME                                                  | ENABLED | TIMED |
+-------------------------------------------------------+---------+-------+
| wait/synch/mutex/innodb/ibuf_bitmap_mutex             | YES     | YES   |
| wait/synch/mutex/innodb/ibuf_mutex                    | YES     | YES   |
| wait/synch/mutex/innodb/ibuf_pessimistic_insert_mutex | YES     | YES   |
+-------------------------------------------------------+---------+-------+
3 rows in set (0.00 sec)

1.3 Adaptive Hash Index

Adaptive Hash Index功能使InnoDB能够在系统上执行更多类似于内存数据库的工作,并为缓冲池提供适当的工作负载和足够的内存,而不会牺牲事务功能或可靠性。自适应哈希索引功能由innodb_adaptive_hash_index变量启用,或在服务器启动时由--skip-innodb_adaptive_hash_index关闭。

在这里插入图片描述

Adaptive Hash Index 的限制:

  • 只能用于等值比较,例如=, <=>,in

  • 无法用于排序

  • 有冲突可能

  • MySQL自动管理,人为无法干预。

1.4 Redo Log Buffer

InnoDB存储引擎首先将重做日志信息放入到redo log buffer,然后按一定频率将其刷新到 redo log file中。

日志缓冲区大小由innodb_log_buffer_size变量定义。 默认大小为16MB。

日志缓冲区的内容会定期刷新到磁盘上的日志文件中。 大型日志缓冲区使大型事务能够运行,而无需在事务提交之前将重做日志写入磁盘。 因此,如果您有更新,插入或删除许多行的事务,则使日志缓冲区更大可以节省磁盘I / O.

redo log在下列三种情况会将redo log buffer中的内容刷新到外部磁盘的redo log file中:

  • Master Thread 每秒将redo log buffer刷新到 redo log file中
  • 每个事务提交时会将redo log buffer 刷新到 redo log file中
  • 当redo log buffer 剩余空间小于1/2时,redo log buffer 将刷新到 redo log file中

innodb_flush_log_at_trx_commit变量控制如何写入日志缓冲区的内容并刷新到磁盘。 innodb_flush_log_at_timeout变量控制日志刷新频率,默认1s

1.4.1 优化InnoDB重做日志

参考博客MySQL redo log 与 binlog 的区别

2. 磁盘结构

2.1 Tables

参考官方文档Tables

2.2 Indexes

参考官方文档Indexes

2.3 Tablespaces

参考博客InnoDB表空间

2.4 Innodb Data Dictionary

InnoDB数据字典由内部系统表组成,其中包含用于跟踪对象(如表,索引和表列)的元数据。 元数据实际位于InnoDB系统表空间中。 由于历史原因,数据字典元数据在某种程度上与存储在InnoDB表元数据文件.frm文件中的信息重叠。

2.5 Doublewrite Buffer

doublewrite缓冲区是位于系统表空间中的存储区域,InnoDB在页面写入数据文件中的正确位置之前写入从InnoDB缓冲池刷新的页面。只有在将页面刷新并写入双写缓冲区后,InnoDB才会将页面写入其正确的位置。如果在页面写入过程中存在操作系统,存储子系统或mysqld进程崩溃,InnoDB稍后可以在崩溃恢复期间从doublewrite缓冲区中找到该页面的良好副本。

虽然数据总是写入两次,但双写缓冲区不需要两倍的I/O开销或两倍的I/O操作。数据作为一个大的顺序块写入doublewrite缓冲区本身,对操作系统进行单个fsync()调用。

在大多数情况下,默认情况下启用doublewrite缓冲区。要禁用doublewrite缓冲区,请将innodb_doublewrite设置为0。

在这里插入图片描述

doublewrite由两部分组成,一部分是内存中的doublewrite,大小为2MB,另一部分是物理磁盘上共享表空间中连续128个页,即2个区(extent),大小同样为2MB。在对缓冲池的脏页进行刷新时,并不直接写磁盘,而是会通过memcpy函数将脏页先复制到内存中的doublewrite buffer,之后通过doublewrite buffer再分两次,每次1MB顺序地写入共享表空间的物理磁盘上,然后马上调用fsync函数,同步磁盘,避免缓冲写带来的问题。在这个过程中,因为doublewrite页是连续的,因此这个过程是顺序写的,开销并不是很大。在完成doublewrite页的写入后,再将doublewrite buffer的页写入各个表空间文件中,此时的写入则是离散的。

2.6 Redo Log

参考博客InnoDB表空间

2.7 Undo log

参考博客InnoDB表空间

3. 后台线程

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

3.1 Master Thread

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

Master thread具有最高的线程优先级别,其内部由多个循环(loop)组成:主循环(loop),后台循环(background loop),刷新循环(flush loop),暂停循环(suspend loop)。

InnoDB 1.0.x版本之前的Master Thread的操作如下:
每一秒的操作如下:

  • 日志缓冲刷新到磁盘,即使这个事务还没有提交(总是)
  • 合并change buffer(可能)
  • 至多刷新100个脏页到磁盘
  • 如果当前没有用户活动,则切换到 background loop(可能)

每十秒的操作如下:

  • 刷新100个脏页到磁盘(可能的情况)
  • 合并至多5个change buffer(总是)
  • 将日志缓冲刷新到磁盘(总是)
  • 删除无用的Undo页(总是)
  • 刷新100个或者10个脏页到磁盘(总是)

background loop会执行以下操作:
若当前没有用户活动(数据库空闲)或者数据库关闭(shutdown),就会切换到这个循环。

  • 删除无用的Undo页(总是)
  • 合并20个插入缓冲(总是)
  • 跳回到主循环(总是)
  • 不断刷新100个页直到符合条件(可能,跳转到flush loop中完成)
    若flush loop中也没有什么事情可以做了,InnoDB存储引擎会切换到suspend_loop,将Master Thread挂起,等待事件的发生。若用户启用了InnoDB存储引擎,却没有使用任何InnoDB表,那么Master Thread总是处于挂起的状态。

InnoDB 1.2.x版本之前的Master Thread的改进操作如下:

  • 在合并change buffer时,合并插入缓冲的数量为innodb_io_capacity值的5%。
  • 在缓冲区刷脏页时,刷新脏页的数量为innodb_io_capacity
  • 增加参数innodb_adaptive_flushing(自适应地刷新),该值影响每秒刷新脏页的数量。通过函数buf_flush_get_desired_flush_rate(判断产生redo log的速度来决定最合适的刷新脏页数量。)来判断需要刷新脏页的数量。
  • 引入参数innodb_purge_batch_size,控制每次full purge回收undo页的数量。

InnoDB 1.2.x版本的Master Thread的改进操作如下:

  • srv_active
  • srv_shutdown
  • srv_idle
  • 分离单独线程Page Cleaner Thread,用于清理undo页

3.2 IO Thread

在InnoDB存储引擎中大量使用了AIO(Async IO)来处理写IO请求,这样可以极大提高数据库的性能。

根据参数innodb_read_io_threadsinnodb_write_io_threads来设置读写进程。

3.3 Purge Thread

事务提交后,其所用的undolog可能不再需要,因此需要purge thread来回收已经使用并分配的undo页。

通过参数innodb_purge_threads来控制是否启用独立的Purge thread,如果不启用则Purge操作在Master thread线程完成。(>0表示启用,如需要4个Purge thread,可以设置为4).

3.4 Page Cleaner Thread

Page Cleaner Thread 是在InnoDB 1.2.x版本引入的,其作用是将之前版本中脏页刷新操作都放入到单独的线程来完成。

目的也是为了减轻Master Thread的工作及对于用户查询线程的阻塞,进一步提高InnoDB存储引擎的性能。

通过参数innodb_page_cleaners控制启用多个线程来刷新脏块。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值