热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

闲聊MySQL:(四)深入分析InnoDB之内存架构

本篇,我们聊聊In

前言

在前两篇中,我们对MySQL的存储引擎InnoDB进行了简要分析,可以点击这里查看:

闲聊MySQL:(三)深入分析InnoDB之多版本控制MVCC

闲聊MySQL:(二)存储引擎之InnoDB浅析

本篇,我们继续对InnoDB的进行分析,来了解一下InnoDB的内存架构组成。

InnoDB架构模型

首先,我们来看一下MySQL 官方文档中给出的InnoDB的架构模型:

InnoDB架构模型

从上图中我们可以看到,InnoDB的架构可以分为两大部分,内存结构和磁盘结构。下面,我们来一起了解一下内存结构的各部分组成。

内存架构

缓冲池(buffer pool)

InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页(page)的方式进行管理。但是由于CPU速度和磁盘速度之间的鸿沟,基于磁盘的数据库系统通常使用缓冲池记录来提高数据库的的整体性能。

缓冲池是主存储器中的一个区域,用于在访问时缓存表和索引数据。缓冲池允许直接从内存中处理常用数据,从而加快处理速度。在专用服务器上,通常会将最多80%的物理内存分配给缓冲池。

为了提高大容量读取操作的效率,缓冲池被分成可以容纳多行的页(page)。为了提高缓存管理的效率,缓冲池被实现为链接的页链表(linked list of pages);使用LRU算法的变体,很少使用的数据在缓存中老化(aged out)。

在数据库中进行读取操作,首先将从磁盘中读到的页放在缓冲池中,下次再读相同的页中时,首先判断该页是否在缓冲池中。若在缓冲池中,称该页在缓冲池中被命中,直接读取该页。否则,读取磁盘上的页。

具体来看,缓冲池中缓存的数据页类型有:索引页、数据页、undo页、更改缓冲区(change buffer)、自适应HASH索引(adaptive hash index)、锁信息(lock info)、数据字典信息等。

InnoDB内存数据对象

不可以简单的认为,缓冲池只是缓存了索引页和数据页,它们只是占缓冲池很大的一部分而已。

下面,我们对这些区域进行依次的介绍。

. 缓冲池LRU算法

上面我们了解InnoDB的内存结构组成,那么InnoDB是如何对缓冲池中的数据进行管理的呢?

缓冲池使用了LRU(Latest Recent Used,最近最少使用)的变体算法对其页(page)进行管理,当需要空间将新页(page)添加到缓冲池时,最近最少使用的页被淘汰掉,将新的页加入到页列表的中间。此插入策略将页列表视为两个列表:

  • 在头部,是最近访问过的新页的子列表

  • 在尾部,是最近访问的旧页的子列表

此算法保留新子列表中最频繁查询的页。旧子列表包含较少查询的页面,这些页会优先被淘汰。

缓冲池LRU算法

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

  • 3/8的缓冲池专用于旧子列表。

  • 列表的中点是新子列表的尾部与旧子列表的头部相交的边界。


  • 当InnoDB将页读到缓冲池时,它最初将其插入中点(旧子列表的头部)。页的被读取操作,可以由用户指定的操作(如SQL查询)所触发,或者是由InnoDB自动执行的预读操作。


  • 访问旧子列表中的页,可以使其“变年轻”,该操作称为page made young,会将其移动到缓冲池的头部(新子列表的头部)。如果因为需要而读取页,则会立即进行第一次访问,并使页变得“年轻”。如果由于预读而读取了页,则不会重复执行上面的操作。


  • 随着数据库的运行,在缓冲池的较少被访问的页会向列表的尾部移动。新旧子列表中的页随着其他页的变化而变旧。旧子列表中的页也会随着页插入中点而老化。最终,仍然未使用的页到达旧子列表的尾部并被淘汰。

默认情况下,查询读取的页会立即移动到新的子列表中,这意味着它们会更长时间地保留在缓冲池中。

表扫描(例如为mysqldump操作执行,或者SELECT带有no WHERE子句的语句)可以将大量数据带入缓冲池并逐出相同数量的旧数据,即使新数据从未再次使用过。

类似地,由预读后台线程加载,然后仅访问一次的页面会移动到新页列表的头部。这些情况下,会将经常使用的页面推送到旧页的子列表中,从而导致它们会被淘汰。

而真正的热点数据,再次被访问时,InnoDB需要再次访问磁盘,将数据重新加载回缓冲区。

. 缓冲池配置

在上个小节,我们对缓冲池的LRU淘汰算法进行了了解,也发现了缓冲池对于特定场景下的一些问题,我们可以通过配置缓冲池的各个方面以提高性能。

1、理想情况下,可以将缓冲池的大小设置为尽可能大的值,从而无需过多的分页。缓冲池越大,InnoDB内存数据就越多,从磁盘读取数据一次,然后在后续读取期间从内存中访问数据。可以通过参数 innodb_buffer_pool_size 进行配置缓冲池大小。

2、在具有内存足够的64位系统上,可以将缓冲池拆分为多个部分,以最大限度地减少并发操作之间内存结构的争用。可以通过参数  innodb_buffer_pool_instances 进行配置,默认值为1。

3、可以将频繁访问的数据保留在内存中,避免上一节中提到的表扫描操作,会将大量不经常访问的数据带入缓冲池,而将频繁访问的数据淘汰掉。可以通过参数  innodb_old_blocks_time 进行配置,该配置用于表示页读取到mid位置后需要等待多久才会被加入到LRU列表的新页列表中去,默认是0。

4、可以控制何时、以及如何执行预读请求,以异步方式将页预读取到缓冲池中,可以通过参数 innodb_read_ahead_threshold 进行配置,默认值为56,关于innoDB的预读机制,这里暂不过多介绍。

5、可以控制何时发生后台刷新以及是否根据工作负载动态调整刷新率。

这里对于后台刷新的机制,需要进行简要说明,在LRU列表中的页被修改后,称该页为脏页(dirty page),即缓冲池中的页和磁盘上的数据产生了不一致的情况。此时数据库会通过CHECKPOINT机制将脏页刷回磁盘,而这个过程,是存储引擎在后台任务执行的操作。InnoDB对于此过程,采用自适应刷新机制,来控制刷新的速率,可以通过参数 innodb_adaptive_flushing 配置是否开启此功能,默认为ON,即开启。

更改缓冲区(change buffer)

更改缓冲区是一种特殊的数据结构,当这些页不在缓冲池中时,它会更改二级索引页。缓冲的变化,可能导致INSERT、UPDATE或者DELETE操作(DML)会被在这些页被其他读操作加载到缓冲池的操作后,合并执行。

更改缓冲区

在InnoDB引擎上进行插入操作时,一般需要按照主键顺序进行插入,这样才能获得较高的插入性能。

但是与主键聚簇索引不同,二级索引通常不是唯一的,并且插入二级索引的顺序相对随机。同样,删除和更新可能会影响不在索引树中相邻的二级索引页。

当受影响的页被其他操作读入缓冲池时,合并缓存的更改,避免了从磁盘读取二级索引页到缓冲池所需的大量随机访问I/O。

系统大部分空闲时或在慢速关闭期间运行的清除操作会定期将更新的索引页写入磁盘。与每个值立即写入磁盘相比,清除操作可以更有效地为一系列索引值写入磁盘块。

对表执行任何INSERT、UPDATE和DELETE操作时,索引列的值(特别是二级索引的值)通常按未排序顺序排列,需要大量I/O才能使二级索引更新。

更改缓冲区缓存了二级索引元素的改变,当其相关的页数据不在缓冲池中时,由此可以避免大量的I/O操作,不会立刻从硬盘中加载对应的页数据。

当页数据被加载到缓冲池中时,将合并缓冲的更改,稍后将更新的页刷新到磁盘。InnoDB主线程在服务器几乎空闲时以及在慢速关闭期间合并缓冲区的更改。

因为它可以减少磁盘读取和写入,所以更改缓冲区功能对于I/O绑定的工作负载最有价值,例如具有大量DML操作的应用程序(如批量插入)。

. 更改缓冲区配置

更改缓冲区占用缓冲池的一部分,从而减少了可用于缓存数据页的内存。如果工作集几乎适合缓冲池,或者数据表具有相对较少的二级索引,则禁用更改缓冲可能是更好的选择。如果工作数据集完全适合缓冲池,则更改缓冲不会产生额外开销,因为它仅适用于不在缓冲池中的页面。

InnoDB 使用 innodb_change_buffering 配置参数控制执行更改缓冲区的范围。可以为插入、删除(当索引记录最初标记为删除时)和清除操作(物理删除索引记录时)启用或禁用缓冲区。更新操作是插入和删除的组合。

默认 innodb_change_buffering 值为 all。

innodb_change_buffering 可配置参数如下:

  • all:默认值:缓冲区插入,删除标记操作和清除。

  • none:不要缓冲任何操作

  • inserts:缓冲插入操作

  • deletes:缓冲删除标记操作

  • changes:缓冲插入和删除标记操作

  • purges:缓冲在后台发生的物理删除操作。

自适应HASH索引

HASH是一种非常快的查找方法,在一般情况下,这种查找的时间复杂度为O(1)。即一般仅需要一次查找就能定位数据。而B+树的查找次数,取决于B+树的高度,在生产环境中,B+数的高度一般为3~4层,因此需要3~4次查询。

InnoDB存储引擎会监控对表上各索引页的查询。如果观察到建立哈希索引可以带来速度提升,则建立哈希索引,称之为自适应哈希索引。自适应哈希索引是通过缓冲池的B+树页构造而来,因此建立的速度非常快,而且不需要对整张表构建哈希索引。InnoDB会自动根据访问的频率和模式来自动地为某些热点页建立哈希索引。

其有一个要求,即对这个页的连续访问模式必须是一样的,也就是说其查询的条件(WHERE)必须完全一样,而且必须是连续的。

可以通过参数 innodb_adaptive_hash_index 配置指定是否开启自适应哈希索引,默认开启。

注意,InnoDB的索引类型是不可以指定哈希索引的,其哈希索引实现是自适应的,在内部自动构建。

日志缓冲区

日志缓冲区是保存要写入磁盘上日志文件的数据的内存区域,即redo log buffer。

当缓冲池中的页的版本比磁盘要新时,数据库需要将新版本的页从缓冲池刷新到磁盘。但是如果每次一个页发送变化,就进行刷新,那么性能开发是非常大的,于是InnoDB采用了Write Ahead Log策略,即当事务提交时,先写重做日志,然后再择时将脏页写入磁盘。如果发生宕机导致数据丢失,就通过重做日志进行数据恢复。

日志缓冲区大小由 innodb_log_buffer_size 变量定义,默认大小为16MB。日志缓冲区的内容会定期刷新到磁盘。大容量的日志缓冲区使大的事务能够运行,而无需在事务提交之前将重做日志数据写入磁盘。因此,如果有更新,插入或删除许多行的事务,则增加日志缓冲区的大小可以节省磁盘I/O。

innodb_flush_log_at_trx_commit 变量控制如何写入日志缓冲区的内容并刷新到磁盘。

该参数默认值为1,表示事务提交必须进行一次fsync操作,还可以设置为0和2。0表示事务提交时不进行写入重做日志操作,该操作只在主线程中完成,2表示提交时写入重做日志,但是只写入文件系统缓存,不进行fsync操作。由此可见,设置为0时,性能最高,但是丧失了事务的一致性。

innodb_flush_log_at_timeout 变量控制日志刷新频率。

结语

本篇,我们对InnoDB的架构有了大体的了解,对其内存架构的各部分组成进行了介绍,InnoDB的内存架构与磁盘架构是紧密相关的,因此下一篇中,我们将会继续介绍其磁盘架构的组成,敬请期待!

本篇参考:

MySQL技术内幕(InnoDB存储引擎)第2版

MySQL官方文档

MySQL探秘(三):InnoDB的内存结构和特性



推荐阅读
  • MySQL数据库锁机制及其应用(数据库锁的概念)
    本文介绍了MySQL数据库锁机制及其应用。数据库锁是计算机协调多个进程或线程并发访问某一资源的机制,在数据库中,数据是一种供许多用户共享的资源,如何保证数据并发访问的一致性和有效性是数据库必须解决的问题。MySQL的锁机制相对简单,不同的存储引擎支持不同的锁机制,主要包括表级锁、行级锁和页面锁。本文详细介绍了MySQL表级锁的锁模式和特点,以及行级锁和页面锁的特点和应用场景。同时还讨论了锁冲突对数据库并发访问性能的影响。 ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • 基于事件驱动的并发编程及其消息通信机制的同步与异步、阻塞与非阻塞、IO模型的分类
    本文介绍了基于事件驱动的并发编程中的消息通信机制,包括同步和异步的概念及其区别,阻塞和非阻塞的状态,以及IO模型的分类。同步阻塞IO、同步非阻塞IO、异步阻塞IO和异步非阻塞IO等不同的IO模型被详细解释。这些概念和模型对于理解并发编程中的消息通信和IO操作具有重要意义。 ... [详细]
  • 本文讨论了在数据库打开和关闭状态下,重新命名或移动数据文件和日志文件的情况。针对性能和维护原因,需要将数据库文件移动到不同的磁盘上或重新分配到新的磁盘上的情况,以及在操作系统级别移动或重命名数据文件但未在数据库层进行重命名导致报错的情况。通过三个方面进行讨论。 ... [详细]
  • 在Oracle11g以前版本中的的DataGuard物理备用数据库,可以以只读的方式打开数据库,但此时MediaRecovery利用日志进行数据同步的过 ... [详细]
  • 本文介绍了使用数据库管理员用户执行onstat -l命令来监控GBase8s数据库的物理日志和逻辑日志的使用情况,并强调了对已使用的逻辑日志是否及时备份的重要性。同时提供了监控方法和注意事项。 ... [详细]
  • OpenMap教程4 – 图层概述
    本文介绍了OpenMap教程4中关于地图图层的内容,包括将ShapeLayer添加到MapBean中的方法,OpenMap支持的图层类型以及使用BufferedLayer创建图像的MapBean。此外,还介绍了Layer背景标志的作用和OMGraphicHandlerLayer的基础层类。 ... [详细]
  • 2021最新总结网易/腾讯/CVTE/字节面经分享(附答案解析)
    本文分享作者在2021年面试网易、腾讯、CVTE和字节等大型互联网企业的经历和问题,包括稳定性设计、数据库优化、分布式锁的设计等内容。同时提供了大厂最新面试真题笔记,并附带答案解析。 ... [详细]
  • 数据库锁的分类和应用
    本文介绍了数据库锁的分类和应用,包括并发控制中的读-读、写-写、读-写/写-读操作的问题,以及不同的锁类型和粒度分类。同时还介绍了死锁的产生和避免方法,并详细解释了MVCC的原理以及如何解决幻读的问题。最后,给出了一些使用数据库锁的实际场景和建议。 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • 本文介绍了Redis中RDB文件和AOF文件的保存和还原机制。RDB文件用于保存和还原Redis服务器所有数据库中的键值对数据,SAVE命令和BGSAVE命令分别用于阻塞服务器和由子进程执行保存操作。同时执行SAVE命令和BGSAVE命令,以及同时执行两个BGSAVE命令都会产生竞争条件。服务器会保存所有用save选项设置的保存条件,当满足任意一个保存条件时,服务器会自动执行BGSAVE命令。此外,还介绍了RDB文件和AOF文件在操作方面的冲突以及同时执行大量磁盘写入操作的不良影响。 ... [详细]
  • MySQL中的MVVC多版本并发控制机制的应用及实现
    本文介绍了MySQL中MVCC的应用及实现机制。MVCC是一种提高并发性能的技术,通过对事务内读取的内存进行处理,避免写操作堵塞读操作的并发问题。与其他数据库系统的MVCC实现机制不尽相同,MySQL的MVCC是在undolog中实现的。通过undolog可以找回数据的历史版本,提供给用户读取或在回滚时覆盖数据页上的数据。MySQL的大多数事务型存储引擎都实现了MVCC,但各自的实现机制有所不同。 ... [详细]
author-avatar
黄ll明雪_742
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有