热门标签 | 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的内存结构和特性



推荐阅读
  • 如果应用程序经常播放密集、急促而又短暂的音效(如游戏音效)那么使用MediaPlayer显得有些不太适合了。因为MediaPlayer存在如下缺点:1)延时时间较长,且资源占用率高 ... [详细]
  • DAO(Data Access Object)模式是一种用于抽象和封装所有对数据库或其他持久化机制访问的方法,它通过提供一个统一的接口来隐藏底层数据访问的复杂性。 ... [详细]
  • 如何在Linux服务器上配置MySQL和Tomcat的开机自动启动
    在Linux服务器上部署Web项目时,通常需要确保MySQL和Tomcat服务能够随系统启动而自动运行。本文将详细介绍如何在Linux环境中配置MySQL和Tomcat的开机自启动,以确保服务的稳定性和可靠性。通过合理的配置,可以有效避免因服务未启动而导致的项目故障。 ... [详细]
  • PTArchiver工作原理详解与应用分析
    PTArchiver工作原理及其应用分析本文详细解析了PTArchiver的工作机制,探讨了其在数据归档和管理中的应用。PTArchiver通过高效的压缩算法和灵活的存储策略,实现了对大规模数据的高效管理和长期保存。文章还介绍了其在企业级数据备份、历史数据迁移等场景中的实际应用案例,为用户提供了实用的操作建议和技术支持。 ... [详细]
  • 用阿里云的免费 SSL 证书让网站从 HTTP 换成 HTTPS
    HTTP协议是不加密传输数据的,也就是用户跟你的网站之间传递数据有可能在途中被截获,破解传递的真实内容,所以使用不加密的HTTP的网站是不 ... [详细]
  • 本文介绍如何在将数据库从服务器复制到本地时,处理因外键约束导致的数据插入失败问题。 ... [详细]
  • 本文介绍如何使用 Python 的 DOM 和 SAX 方法解析 XML 文件,并通过示例展示了如何动态创建数据库表和处理大量数据的实时插入。 ... [详细]
  • Java高并发与多线程(二):线程的实现方式详解
    本文将深入探讨Java中线程的三种主要实现方式,包括继承Thread类、实现Runnable接口和实现Callable接口,并分析它们之间的异同及其应用场景。 ... [详细]
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • 探讨如何在Go语言中高效地处理大规模切片的去重操作,特别是针对百万级数据量的场景。 ... [详细]
  • 本文详细介绍了数据库并发控制的基本概念、重要性和具体实现方法。并发控制是确保多个事务在同时操作数据库时保持数据一致性的关键机制。文章涵盖了锁机制、多版本并发控制(MVCC)、乐观并发控制和悲观并发控制等内容。 ... [详细]
  • MySQL Decimal 类型的最大值解析及其在数据处理中的应用艺术
    在关系型数据库中,表的设计与SQL语句的编写对性能的影响至关重要,甚至可占到90%以上。本文将重点探讨MySQL中Decimal类型的最大值及其在数据处理中的应用技巧,通过实例分析和优化建议,帮助读者深入理解并掌握这一重要知识点。 ... [详细]
  • PHP 各版本对比:标准版与最新顶级版的详细分析 ... [详细]
  • DVWA学习笔记系列:深入理解CSRF攻击机制
    DVWA学习笔记系列:深入理解CSRF攻击机制 ... [详细]
  • 本文深入解析了JDK 8中HashMap的源代码,重点探讨了put方法的工作机制及其内部参数的设定原理。HashMap允许键和值为null,但键为null的情况只能出现一次,因为null键在内部通过索引0进行存储。文章详细分析了capacity(容量)、size(大小)、loadFactor(加载因子)以及红黑树转换阈值的设定原则,帮助读者更好地理解HashMap的高效实现和性能优化策略。 ... [详细]
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社区 版权所有