InnoDB架构

打印 上一主题 下一主题

主题 985|帖子 985|积分 2955

首先,先给出官网的一张InnoDB的架构图。InnoDB存储引擎重要由两个部分组成,分别是内存架构和磁盘架构

内存布局

在InnoDB中内存架构重要包括四个部分,分别是Buffer pool、Change buffer、自顺应哈希索引、和Log Buffer。
Buffer Pool

https://dev.mysql.com/doc/refman/8.4/en/innodb-buffer-pool.html
InnoDB 存储引擎是基于磁盘存储的,利用InnoDB时,所有数据,包括列上的任何和所有索引,都存储在磁盘上的Page中。由于 CPU 速度与磁盘速度之间的差距,基于磁盘的数据库体系通常利用内存缓冲区技术来提高数据库的团体性能。
当MySQL实行和完成查询时,它需要能够将这些数据的子集加载到RAM中,以便更快地访问。MySQL利用的就是这个叫做Buffer Pool的东西,Buffer Pool是主存中的一个区域,InnoDB在访问表和索引数据时将其缓存在该区域。Buffer Pool允许直接从内存访问频繁利用的数据,从而加快了处理速度。在一些数据专用服务器上,高达80%的物理内存通常分配给Buffer Pool。
Buffer Pool的大小可以利用innodb_buffer_pool_size配置选项进行配置。当MySQL运行并读取行和索引时,它会从磁盘中读取Page并将其放入RAM的这个区域。
了解如何利用缓冲池将频繁访问的数据保存在内存中是MySQL调优的一个重要方面。
通常,这个池越大数据库的性能就越好,直到池大小=数据集的大小。如果数据的大小大于池中的可用空间,MySQL终极将不得不从缓冲池中删除Page,为加载新的Page腾出空间。MySQL为此利用了一种改进的LRU算法。
由于这种布局,当MySQL开始实行B树索引查找时,最初必须从磁盘加载许多Page,这个过程是一种缓存预热。然而,随着它处理越来越多的查询,这些Page中的许多终极可能会驻留在Buffer Pool中,这意味着每个B树节点的访问和比较实行得更快,因为它重要是在内存中利用而不是磁盘。
Change Buffer

https://dev.mysql.com/doc/refman/8.4/en/innodb-change-buffer.html
InnoDB 从 1.0.x 版本开始引入了 Change Buffer。Change Buffer重要的功能是纪录数据库的数据修改利用的结果的,目的是为了提高数据库的写性能。因为 InnoDB 的辅助索引不同于聚集索引的序次插入,如果每次修改二级索引都直接写入磁盘,则会有大量频繁的随机 IO。
   基本工作原理
  Change Buffer会将对非唯一辅助索引Page的利用缓存下来,如果辅助索引页已经在缓冲区了,则直接修改;如果不在,则先将对这些Page的更改保存到 Change Buffer。当对应辅助索引页读取到缓冲区时,才将 Change Buffer 的数据归并到真正的辅助索引页中,以此减少辅助索引的随机 IO,并到达利用归并的效果。缓冲的更改可能是由INSERT、UPDATE或DELETE利用(DML)引起的,稍后当页面通过其他读取利用加载到缓冲池中时,这些更改会被归并。

下面我们就来详细分析一下,数据修改利用的步调:

  • 第一步:修改一条数据时,首先判断该条数据是否存在于Buffer Pool之中。
    如果在,直接修改Buffer Pool中的相干数据。
    如果不在,首先在磁盘中读取该条数据到Change Buffer之中,而后在Change Buffer中修改该数据,同时为了防止数据丢失,会同步写入Redo Log之中,等下一次查询该条数据时,归并至Buffer Pool中。
  • 第二步:Change Buffer中数据修改之后进行数据归并
    什么时间归并数据呢?
    第一种方式:当修改的这条数据被查询的时间,归并到Buffer Pool。
    第二种方式:MySQL 数据库中的Master Thread归并(周期默认:10s)。
    第三种方式:当 MySQL 数据库关闭时,通过Redo Log归并到磁盘中。
Change Buffer之所以如许设计,是因为对于高速运转的 MySQL 数据库来讲,如果每一次修改都修改磁盘同时又修改Buffer Pool中的内容的话,对于 MySQL 数据库来讲代价太大了,磁盘的 IO 也会非常高,终极会导致 MySQL 数据库运行缓慢。那么,修改数据时利用Change Buffer就相称于在内存中修改数据,并且保存在内存中,当数据库空闲时才会写入磁盘,如许既能够到达修改数据的目的,又能够降低数据库对于体系的性能要求,进而提高数据库的性能。
在 MySQL 5.5 之前 Change Buffer 其实叫 Insert Buffer,最初只支持 INSERT 利用的缓存,随着支持利用类型的增长,改名为 Change Buffer,现在 InnoDB 存储引擎可以对 INSERT、DELETE、UPDATE 都进行缓冲,对应着:Insert Buffer、Delete Buffer、Purge buffer。
自顺应哈希索引 Adaptive Hash Index

https://dev.mysql.com/doc/refman/8.4/en/innodb-adaptive-hash.html
InnoDB的索引是B+树布局,B+树索引查找比对所有元素进行线性扫描要快得多。掷中索引的话查找大概时O(logn),并不是O(1),因为一次查找通常需要多次比较和页面加载。那么什么数据布局支持O(1)的查询呢?哈希表。MySQL创建索引时可以选择是利用hash索引,但是InnoDB其实是不支持hash索引的。
  1. CREATE INDEX alias_index ON user(username) USING HASH;
复制代码
实际上创建的还是BTree索引。
自顺应哈希索引是MySQL的一个优化方式,在内存中进行一个hash查找。如果hash索引里有,则不走B+树查找了

此功能可用于加速已经很快的B树查找,从而加速将这些索引作为查询筹划一部分的查询的性能。这充当MySQL实行和内存缓冲池之间的一层。
自顺应哈希索引并不是人为去创建的,而是InnoDB存储引擎通过索引监控机制去自动创建的,无法人为控制。同时自顺应哈希索引是在运行时构建的,它的利用会根据工作负载的特点进行调解。如果MySQL观察到在B树索引中反复查找特定值,则可以利用完备值或值的前缀创建自顺应哈希索引中的条目。对于将来查找此相同值(这是可能的,因为MySQL观察到它的重复利用),它将利用自顺应哈希索引而不是B+树。自顺应哈希索引的键是基础索引的值(或值前缀)。这些值是指针,指的是该值的数据在InnoDB缓冲池中的位置。

   优缺点
  

  • 不支持范围查询
  • 不能人工干预
  • 并不是任何情况下都可以利用,例如:like ‘%xxx’,这是因为 like 前置百分号查询本身就需要全表扫描,所以用与不用索引的结果都是一样的,用索引反而会多此一举,因此这种情况下不需要创建自顺应哈希索引。
  • 一致性:自顺应哈希索引相称于将Buffer Pool的数据又缓存了一部分,更新时会涉及到一致性题目,所以MySQL内部一定是包管了自顺应哈希索引的数据和B+树上数据的同步更新的,会增长性能开销
  • 维护自顺应哈希索引也会有开销
   利用场景
  对于某些工作负载,哈希索引查找的加速远远凌驾了监控索引查找和维护哈希索引布局的额外工作。在繁重的工作负载下,对自顺应哈希索引的访问有时会成为争用的泉源,例如多个并发连接。利用LIKE运算符和%通配符的查询也往往不会受益。对于不受益于自顺应哈希索引的工作负载,关闭它可以减少不必要的性能开销。因为很难提前推测自顺应哈希索引是否适合特定的体系和工作负载,所以考虑在启用和禁用它的情况下运行基准测试。
   关闭自顺应哈希索引
  自顺应哈希索引中的指针仅指向缓冲池中的数据。因此Buffer Pool需要足够大,以便自顺应哈希索引能够启动。如果Buffer Pool很小,并且发生了许多驱逐,则不值得利用。MySQL能够根据在缓冲池中观察到的行为自动调解其对自顺应哈希索引的利用,也就是自顺应这3个字的作用。如果利用条件不符合(重复查找次数少、Buffer Pool小等),MySQL将减少或消除其利用。
虽然它可以加快查询速度,但维护这个特殊的哈希索引会有一些开销。该功能可以通过innodb_adapter_hash_index配置选项启用或禁用。默认情况下,它通常处于启用状态,可以利用innodb_adapter_hash_index=0禁用它。
或者启动mysql server端时通过参数--innodb-adaptive-hash-index进行
Log Buffer

如果在Change Buffer修改完数据之后,仅仅保存在内存中,那么如果这个时间数据库宕机,也就意味着我们刚刚修改的数据也随即丢失,而这一点是不能被允许的。
怎么解决这个题目呢?
MySQL 给我们提供了一种写日志的方案,也就是说,修改完的数据会保存到一个叫Redo Log的日志中。它是一个物理日志,当数据宕机时,它会将数据直接保存在磁盘之上;当数据库开启时,自动写入到数据库的磁盘中,以至于数据不会丢失。
如果把大量的数据直接写进磁盘,还是会导致数据库性能低下,我们用一个Log Buffer来保存需要写入Redo log的数据,如许有利于提高数据库的性能。
磁盘布局

MySQL是基于磁盘存储的数据库,所有数据终极都会存放在磁盘上
System Tablespace

体系表空间的重要作用是存储InnoDB数据字典、Double Write Buffer、Change Buffer以及 undo log等。体系表空间一般存放于 MySQL 数据库目次中,名称为:ibdata1。
  1. mysql> SHOW VARIABLES LIKE 'datadir';
  2. +---------------+-----------------+
  3. | Variable_name | Value           |
  4. +---------------+-----------------+
  5. | datadir       | /var/lib/mysql/ |
  6. +---------------+-----------------+
复制代码
在数据目次文件夹下可以看到ibdata1文件

体系表空间一般不一定只有一个,也可能有多个,体系表空间的大小和数量由innodb_data_file_path控制。具体如下:
  1. mysql> SHOW VARIABLES LIKE 'innodb_data_file_path';
  2. +-----------------------+------------------------+
  3. | Variable_name         | Value                  |
  4. +-----------------------+------------------------+
  5. | innodb_data_file_path | ibdata1:12M:autoextend |
  6. +-----------------------+------------------------+
复制代码
  留意
  InnoDB 数据字典在 MySQL 8.0 版本以后归并至 MySQL 数据字典中了,不再存储在体系表空间
File-Per-Table Tablespace

File-Per-Table表空间包含单个InnoDB表的数据和索引,并存储在文件体系中的单个数据文件中。

对于innodb存储引擎来说,通常创建数据表的时间,会在 MySQL 数据目次中创建两个文件,分别是.ibd和.frm两个文件。.ibd文件重要用来存储表数据,而.frm文件重要用来存储索引。MySQL8.0以后没有.frm了,元数据都存在体系表空间里。
这种做法可以将所有的数据表分开管理,也能够实现快速数据迁移,当数据出现故障之时也可以提高数据恢复的成功率。不外如许的做法又会增长磁盘的碎片,对体系处理表文件的性能有一定的影响。
   innodb_file_per_table
  InnoDB创建表时默认情况下将表创建在文件表空间,即一个表对应一个文件。这个行为可以通过配置innodb_file_per_table变量来修改
  1. mysql> SELECT @@innodb_file_per_table;
  2. +-------------------------+
  3. | @@innodb_file_per_table |
  4. +-------------------------+
  5. |                       1 |
  6. +-------------------------+
  7. 1 row in set (0.00 sec)
复制代码
禁用innodb_file_per_table会导致innodb在体系表空间中创建表,也就是表数据会放到ibdata1文件中
innodb_file_per_table设置可以在选项文件中指定,如下所示:
  1. [mysqld]
  2. innodb_file_per_table=ON
复制代码
也可以在运行时利用SET GLOBAL语句进行配置:SETGLOBAL innodb_file_per_table=ON;,在运行时更改设置需要足够的权限来设置全局体系变量。
General Tablespace

https://dev.mysql.com/doc/refman/8.4/en/general-tablespaces.html
翻译为通用表空间,通用表空间是利用CREATE tablespace语法创建的共享InnoDB表空间。其具体文件在 MySQL 数据库的数据目次中是以.ibd结尾的文件。跟体系表空间雷同,它支持所有 MySQL 数据库中的数据表的布局,它是将数据库的一些元数据保存在内存之中,进而能够减少独立表空间对于内存的消耗。
Undo Tablespace

Undo 表空间重要是用来保存撤销日志Undo Log的表空间。它默认情况下存储在 MySQL 数据库的根目次。我们可以通过以下方式来查看:
  1. mysql> SHOW VARIABLES LIKE 'innodb_undo_directory';
  2. +-----------------------+-------+
  3. | Variable_name         | Value |
  4. +-----------------------+-------+
  5. | innodb_undo_directory | ./    |
  6. +-----------------------+-------+
  7. 1 row in set (0.00 sec)
复制代码
在MySQL 8.0版本之后,undo 表空间会在 MySQL 数据库的数据根目次天生 undo_001 和 undo002 共两个文件,如下所示

临时表空间

临时表空间重要是用来保存数据库会话中的临时数据的。在 MySQL 数据库的数据根目次中保存以ibtmp1定名的文件。最重要的是我们在利用 join 连表查询的时间,会在临时表空间内创建临时数据表用来辅助查询。我们可以通过以下方式来查看临时表空间的配置:
  1. mysql> SELECT @@innodb_temp_data_file_path;
  2. +------------------------------+
  3. | @@innodb_temp_data_file_path |
  4. +------------------------------+
  5. | ibtmp1:12M:autoextend        |
  6. +------------------------------+
  7. 1 row in set (0.00 sec)
复制代码
在数据目次可以看到临时表空间文件

Double Write Buffer 文件

当发生数据库宕机时,可能存储引擎正在写入某个Page到表中,而这个页只写了一部分,比如 16KB 的页,只写了前 4KB,之后就发生了宕机。虽然可以通过日志进行数据恢复,但是如果这个页本身已经发生了粉碎,再对其进行重新写入是没故意义的。因此 InnoDB 引入 Double Write Buffer 解决数据页的半写题目。
Double Write Buffer 大小默认为 2M,即 128 个数据页。此中分为两部分,一部分留给batch write,提供批量刷新脏页的利用,另一部分是single page write,留给用户线程发起的单页刷脏利用。
在对缓冲池的脏页进行刷新时,脏页并不是直接写到磁盘,而是会通过memcpy()函数将脏页先复制到内存中的 Double Write Buffer 中,如果 Double Write Buffer 写满了,那么就会调用fsync()体系调用,一次性将 Double Write Buffer 所有的数据写入到磁盘中,因为这个过程是序次写入,开销几乎可以忽略。在确保写入成功后,再利用异步 IO 把各个数据页写回自己的表空间中。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

欢乐狗

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表