InnoDB 介绍
书接上回,我们基本说完了mysql的逻辑架构与物理架构。今天我们来说说当下比较火的存储引擎 InnoDb。MySQL 5.5 以前 InnoDB 引擎是需要手动通过 Plugin 方式引入,内置的是MyISAM。MySQL 5.5以后随着数据库默认引擎的更换,InnobaseOy的InnoDB就开始大放异彩(以下我们将用 MySQL5.7 举例)。我们先说说它有什么样的特性让诸位英雄都说香:
-
免费:这个在当时 Oracle,DB2,Sybase MSSQL 垄断的时代,有一款免费还相对稳定的的数据库,再加上后期社区非常活跃,能不香吗? -
完善的崩溃恢复机制:不管是服务器硬件故障还是挖掘机挖断电线导致的关机,重启数据库后都无需执行任何操作(根据设置参数有关) 崩溃恢复会自动完成崩溃之前已提交的所有更改,并撤消正在处理但尚未提交的所有更改。只需重新启动,然后从上次中断的地方继续即可。 -
高效的缓存查询机制:通过一些算法和设计(后面会说到)让常用的数据最大可能的基于内存 Buffer 处理减少 IO,通常这块 Buffer 将高达数据库服务器内存的 80%分配给它。 -
完整性外键机制:将数据拆分到不同数据表中使用外键机制会自动更新或删除关联表中的数据(现在基本没有人这么玩了,一旦设计不全面, 数据的修改将不受程序的控制,数据耦合度太强,适用于一些强制保证数据完整规范的系统,例如工作流这类的) -
数据完整性校验机制:数据在磁盘或内存中损坏,则校验机制会在使用前提醒您注意虚假数据 -
自动引入主键列:有适当数据库主键列时会自动在 Where 子句,Order By 子句,Group By 字句后自动引入主键。 -
基于数据库缓存 Buffer 写数据:不仅允许对同一表的并发读写访问,而且还缓存更改的数据以减少磁盘 IO。 -
自适应哈希索引 :当频繁的从巨表中查询相同行的数据时候就会使用此方式,让它们像从 Hash 表中查出来的一样。 -
表和索引压缩:这个不用大多说了,它借助于 zlib 库,采用 L777 压缩算法。就是让你在原来两个数据页中的数据现在一页就能显示下,但是过度压缩会带来重组页的影响, 而且不是对所有类型数据列都有良好的压缩比。 -
动态创建或删除索引,保证数据的可用性(这个后面单独说) -
截断表空间非常快,并且可以释放空间给系统重用。 -
支持存储引擎混用,最常见的就是我们关联表查询的时候产生的中间临时表,默认就是使用 MEMORY 引擎来实现的。 更多特点请参考阅读最下方:Benefits of Using InnoDB Tables
InnoDB 内存结构
接下来我们来看一张 InnoDB 的逻辑架构图:
Buffer Pool
InnoDB 使用 malloc()操作在启动时为整个缓冲池分配缓存,缓冲池存储着常访问的表数据和索引,以页面为单位(一个页面可能包含多个行 )组成一个链接列表, 使用 LRU 的变体算法将缓冲池作为列表管理。当需要空间将新页面加入到缓冲池时,将淘汰掉最近最少使用的页面,将其加入到列表的中间, 通过此插入点将列表分为两段:最前面的一段为最新访问过的页面,末尾是最近访问的旧页面,如下图:该算法大致如下:
默认情况下,3/8 的缓冲池专用于旧的数据页列表(Old SubList),当然也可以(通过 innodb_old_blocks_pct 参数默认是 37,5-95 范围值)调整其比例大小。列表的中点(Midpoint)是新数据页(New SubList)列表的尾部与旧数据页(Old Sublist)列表的头相交的边界。当一个页面读入缓冲池时, 它首先会插入旧列表(Old Sublist)的头部如果再次读取到旧列表(Old Sublist)的数据页时(在一定时间内,由参数 innodb_old_blocks_time 来决定, 默认 1000ms)将其移动到新列表的的头部。如果是用户启动的操作需要读取页面,立即将其“年轻化”如果是预读操作读取该页面第一次,不发生任何变化。随着数据库的逐渐运行,中点数据的不断插入,新老页面都会随着其他页面的更新而老化,而最终未被使用到的页面将到达旧列表的尾部被淘汰掉。
默认情况下,查询读取的页面会立即移入新的列表,它们在缓冲池中停留时间更长。例如,针对 mysqldump 操作或不带 WHERE 的 SELECT 子句执行的表扫描可以将大量数据载入缓冲池, 并老化同等数量的旧数据,即使不再使用新数据也是如此。预读后台线程加载且仅访问一次的页面将移到新列表的开头。这些情况可能会将热数据挤压到旧的列表或者淘汰。我们可以通过以下几种方法来优化:
-
调整 innodb_old_blocks_pct 参数:为较小的值可以使仅读取一次的数据不会占用缓冲池的很大一部分。扫描小型表时,在缓冲池中移动页面的开销较小,可以设置为较大值。 -
防止缓冲池被预读搅动:可以避免由于表或索引扫描而引起的类似问题,它们的特征是:通常快速连续地访问数据页面几次,而再也不会被访问。调整 innodb_old_blocks_time (第一次访问数据页的时间窗口,单位 MS,在该时间窗口内可以访问页面而不将其移到 New SubList 的头部),增加此值会使越来越多的块从缓冲池中更快地老化。 -
线程预读:根据顺序访问的缓冲池中的页面来预测很快需要哪些页面。调整触发异步读取请求所需的顺序页面访问数(innodb_read_ahead_threshold)参数来执行预读操作。InnoDB 仅当读取当前扩展区的最后一页时,才计算是否对下一个扩展区发出异步预取请求。如果扩展区顺序读取的页面数大于或等于此参数则启动整个后续扩展区的异步读取操作。设值范围为 0-64 之间,默认为 56,。值越高访问检查越严格。 -
随机预读:根据缓冲池中已有的页面来预测何时可能需要该页面,而不管这些页面的读取顺序如何。需要设置 innodb_random_read_ahead 为 on。
如果服务器资源利用率不高也可以通过调整 Buffer Pool 参数来提高资源利用率这里我们就大致说几种(有兴趣的话我们单独开一篇说说 mysql 内存怎么使用):
-
增加缓冲池实例大小:在大内存机器上调整 innodb_buffer_pool_instances 来提高并发性。
-
增加缓冲池大小:太小会造成数据搅动,太大会造成争用内存而导致交换。通常建议将 innodb_buffer_pool_size 其配置为系统内存的 50%到 75%。缓冲池大小必须始终等于 innodb_buffer_pool_chunk_size* innodb_buffer_pool_instances 的倍数否则 Buffer Pool 会自动调整为此规则。
-
调整刷盘函数:在某些 Linux/Unix 版本的系统使用 fsync()刷盘时性能异常差,存在这样的问题可以使用 innodb_flush_method 参数设置为进行基准测试 O_DSYNC。
-
noop 调度或时间调度程序与本机 AIO 一起使用:innodb*use_native_aio(关于系统调度程序请参考本文尾部资料:*调整 Linux I/O 调度器优化系统性能_)
-
配置缓冲池刷新:缓冲池刷新是由页面清理程序线程执行的。清理程序线程的数量由 innodb_page_cleaners 变量控制默认 4。当脏页百分比达到 innodb_max_dirty_pages_pct_lwm 变量定义的低水位标记值时,启动缓冲池刷新。默认低水位为 0,这会禁用早期的刷新。尽量提高脏页刷盘的利用率。还有 innodb_flush_neighbors 参数是指:从缓冲池刷新页面是否也刷新其他脏页面,0 为不刷新,默认为 1 刷新与其连续的脏页,2 为刷新相同 extend 上的脏页。
-
配置 innodb_lru_scan_depth 参数:针对每个缓冲池实例指定页面清理线程扫描以查找要刷新的脏页面的缓冲池 LRU 列表的下行距离。
我们可以通过 SHOW ENGINE INNODB STATUS 来查看 Buffer Pool 的状态:
----------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 2198863872
Dictionary memory allocated 776332
Buffer pool size 131072
Free buffers 124908
Database pages 5720
Old database pages 2071
Modified db pages 910
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 4, not young 0
0.10 youngs/s, 0.00 non-youngs/s
Pages read 197, created 5523, written 5060
0.00 reads/s, 190.89 creates/s, 244.94 writes/s
Buffer pool hit rate 1000 / 1000, young-making rate 0 / 1000 not 0 / 1000
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 5720, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
提示:基于 InnoDB 上次打印输出以来经过的时间每秒平均值
Change Buffer
仅非主键索引的二级索引页不在 Buffer Pool 中时,会更改这部分缓存,将合并后读入 Buffer Pool 再刷到磁盘。先来看个图吧:
非主键索引中的插入顺序相对随机,删除和修改可能会影响不相邻的非主键索引页,最后通过异步线程将受影响的页读入 Buffer Pool 时批量合并更改并写入磁盘, 这将减少每次更改读取 IO 的消耗。(如果索引包含降序索引列或主键包含降序索引列,则非主键索引不会进 Change Buffer)。通常情况下当我们在表上操作 Insert 时非主键索引列的值通常都是未排序的,需要大量 IO 保证最新状态。引入 Change Buffer 后 非主键索引的更改将进入其中, 而不是立即刷新到磁盘。当页面加载到 Buffer Pool 时会合并更改然后刷盘(适合大量的 MDL 操作:批量插入等)。当然对于合并率较低的库库表操作我们可以通过参数 innodb_change_buffering 选择关闭或者调整 Change Buffer 来提升性能。它的设值有以下几种:
all:默认值:缓冲区插入,删除标记操作和清除。 none:关闭缓冲任何操作。 inserts:缓冲区插入操作。 deletes:缓冲区删除标记操作。 changes:缓冲插入和删除标记操作。 purges:缓冲在后台发生的物理删除操作。
当然根据库表的使用情况,通过 innodb_change_buffer_max_size 来设置 Change Buffer 总大小的百分比,默认 25,最大 50。当有大量写入的操作时还可以调整 innodb_change_buffer_max_size,否则就出现合并更改跟不上新产生的的条目,从而导致 Change Buffer 内存达到上限。(可以根据实际情况动态设置)我们可以通过如下命名查看 Change Buffer 的状态:
mysql> SHOW ENGINE INNODB STATUSG
Change Buffer 状态位于 INSERT BUFFER AND ADAPTIVE HASH INDEX 标题下,内容大致如下
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 0, seg size 2, 0 merges
merged operations:insert 0, delete mark 0, delete 0
discarded operations:insert 0, delete mark 0, delete 0
Hash table size 4425293, used cells 32, node heap has 1 buffer(s)
13577.57 hash searches/s, 202.47 non-hash searches/s
Log Buffer
保存要写入磁盘上的日志文件的数据。日志缓冲区大小由 innodb_log_buffer_size 变量定义 。默认大小为 16MB。Log Buffer 的内容会定期刷新到磁盘。较大的日志缓冲区使大型事务可以运行,而无需在事务提交之前将 redo log 数据写入磁盘。如果您有很多行的写事务,则增加 Log Buffer 的大小可以节省磁盘 的 IO 开销。innodb_flush_log_at_trx_commit 变量控制如何将 Log Buffer 区的内容写入并刷新到磁盘。该 innodb_flush_log_at_timeout 变量控制日志刷新频率。
Adaptive Hash Index
该功能可以在 InnoDB 不牺牲事务功能或可靠性的情况下,在工作线程 Buffer Pool 有足够内存的的系统上充当内存数据库使用,使用 innodb_adaptive_hash_index 变量启用。自适应哈希索引是根据观察者模式查找由索引关键字前缀构建的哈希索引,该前缀可以是任何长度,可以是 Hash Tree 索引中仅 B Tree 中的某些值出现。它是根据经常访问的索引页的需求而建立的 ,模糊查找与范围查找不适用,在并发情况下回造成资源争抢。由于很难预先预测自适应哈希索引功能是否适合特定的系统和工作负载, 因此请考虑启用和禁用该功能的基准测试。
参考阅读资料:
-
调整 Linux I/O 调度器优化系统性能: https://www.ibm.com/developerworks/cn/linux/l-lo-io-scheduler-optimize-performance/index.html
-
Benefits of Using InnoDB Tables : https://dev.mysql.com/doc/refman/5.7/en/innodb-benefits.html
原文始发于微信公众号(架构拾遗)
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/21443.html