InnoDB存储引擎介绍-(3)InnoDB缓冲池配置详解

原文连接  http://www.ywnds.com/?p=9886html

1、InnoDB缓冲池

 InnoDB维护一个称为缓冲池的内存存储区域 ,用于缓存内存中的数据和索引。了解InnoDB缓冲池的工做原理,并利用它来保存内存中常常访问的数据,这是MySQL调优的一个重要方面。mysql

1.1 LRU(least recently used)算法

InnoDB将buffer pool做为一个list管理,基于LRU算法。当有新的页要读入到buffer pool的时候,buffer pool就将最近最少使用的页从buffer pool中驱逐出去,而且将新页加入到list的中间位置,这就是所谓的“中点插入策略”。通常状况下list头部存放的是热数据,就是所谓的young pages(最近常常访问的数据),list尾部存放的就是old pages(最近不被访问的数据)。这个算法就保证了最近常用的page信息会被保存在最近访问的sublist中,相反的不被常常访问的就会保存在old sublist,当新数据写入的时候被old sublist中的page信息会被首先驱逐的。sql

LRU算法有如下的标准算法:shell

1)3/8的list信息是做为old list,这些信息是被驱逐的对象。数据库

2)list的中点就是咱们所谓的old list头部和new list尾部的链接点,至关于一个界限。缓存

3)新数据的读入首先会插入到old list的头部。服务器

4)若是是old list的数据被访问到了,这个页信息就会变成new list,变成young page,就会将数据页信息移动到new sublist的头部。数据结构

5)在数据库的buffer pool里面,无论是new sublist仍是old sublist的数据若是不会被访问到,最后都会被移动到list的尾部做为牺牲者。并发

通常状况下,页信息会被查询语句立马查询到而被移动到new sublist,这就意味着他们会在buffer pool里面保留很长一段时间。表扫描(包括mysqldump或者没有where条件的select等操做)等操做将会刷入大量的数据进入buffer pool,同时也会将更多的buffer pool当中的信息刷出去,即便这个操做可能只会使用到一次而已。一样的,若是read-ahead(线性预读)后台进程读入大量数据的状况下也是会形成buffer pool大量高频的刷新数据页,可是这些操做是可控的,下面3,4会说获得。read-ahead操做简单说一下就是MySQL的一个后台预读进程,可以保证MySQL预读入数据进入buffer pool当中。

当你作backup或者report的时候,能够频繁的往buffer pool里面读取数据,不用有太多的顾虑。

InnoDB采用的是一种不是像LRU那么严格的方法来保证将最近访问的数据写入到buffer pool里面,而且最大可能的下降减小数据的带入量。这个语句是全表扫描或者之后这个数据将不会再被访问到,可是缓冲数据仍是会写入到buffer pool里面。

新写入的数据会被插入到LRU list的中间位置,默认会插入到从list尾部算起来的3/8的位置,当这些写入的数据在buffer pool中被第一次访问的时候,在list中的位置就会向前移动,这样其实就会在list保留两个位置,老的位置并不会被当即清除,直到老的LRU list的位置被标记为OLD的时候,才会在下一次插入数据的时候被做为牺牲者清除掉。

咱们自己是能够指定插入LRU list的位置,而且也能够设置当索引扫描或者是全表扫描的时候是否是采用这个相同的优化方法。innodb_old_blocks_pct这个参数设置的是插入的位置,默认的值是37,咱们能够设置的值是5-95之间,其他部分并不用来保存热数据。可是还有一个严重的问题就是当一个全表扫描或者索引的扫描常常被访问的时候,就会存储很大的数据到buffer pool里面,咱们都知道这是很危险的一件事。因此MySQL给咱们如下参数来设置保留在buffer pool里面的数据在插入时候没有被改变list位置的时候的保存时间innodb_old_blocks_time,单位是毫秒,这个值的默认值是1000。若是增大这个值的话,就会让buffer pool里面不少页信息变老的速度变快,这个很好理解把,由于这些数据不会很快被内存中擦除的话,就会变成热数据而挤掉原有缓存的数据。

以上的两个参数都是能够动态设置的,固然也能够在my.cnf里面设置。固然设置这些前必定要对机器配置,表信息,负载状况有充分的了解才能进行设置,生产库尽可能不要随便修改。若是OLTP系统中有大量的大查询的话,设置innodb_old_blocks_time可以较大的提供系统的稳定性。若是当一个大查询很大不足够存储到buffer pool当中的时候,咱们能够指定innodb_old_blocks_pct的值小一点,以保证这些数据只会被读取一次,好比说设置为5的时候,就限制了一次读取数据最多只能被读取到buffer pool当中5%。固然一些小表而且是常常访问到的数据的话就能够适当设置较大的值,好比50。固然设置这两个值的时候必定要创建在你充分了解你的数据负载的基础上,否则千万不要乱改。

2.2 Buffer Pool

InnoDB缓冲池将表的索引和数据进行缓存,缓冲池容许从内存直接处理频繁使用的数据,这加快了处理速度。在专用数据库服务器上,一般将多达80%的物理内存分配给InnoDB缓冲池。由于InnoDB的存储引擎的工做方式老是将数据库文件按页读取到缓冲池,每一个页16k默认(innodb_page_size=16k),在MySQL 5.7中增长了32KB和64KB页面大小的支持,以前版本是不容许大于16k的;但你只能在初始化MySQL实例以前进行配置,一旦设置了一个实例的页面大小,就不能改变它,具体看innodb_page_size参数。

而后按最近最少使用(LRU)算法来保留在缓冲池中的缓存数据。若是数据库文件须要修改,老是首先修改在缓存池中的页(发生修改后,该也即为脏也),而后再按照必定的频率将缓冲池的脏也刷新到文件中。能够经过show engine innodb status来查看innodb_buffer_pool的具体使用状况(默认是8个缓冲池实例),以下:

mysql> show engine innodb status\G
Per second averages calculated from the last 38 seconds(如下信息来之过去的38秒)
----------------------
BUFFER POOL AND MEMORY
----------------------
Total memory allocated 1098907648; in additional pool allocated 0
Dictionary memory allocated 59957
Buffer pool size   65536
Free buffers  65371
Database pages  165
Old database pages  3
Modified db pages  9
..........

在Buffer pool size中能够看到内存池的使用状况:

Total memory allocated:为缓冲池分配的总内存(以字节为单位)。

Dictionary memory allocated:分配给InnoDB数据字典的总内存(以字节为单位)。

Buffer pool size:分配给缓冲池的页面总数量(数量*页面大小=缓冲池大小),默认每一个Page为16k。

Free buffers:缓冲池空闲列表的页面总数量(Buffer pool size -Database pages)。

Database pages:缓冲池LRU LIST的页面总数量(能够理解为已经使用的页面)。

Old database pages:缓冲池旧LRU SUBLIST的页面总大小(能够理解为不常常访问的页面,即将可能被LRU算法淘汰的页面)。

Modified db pages:在缓冲池中已经修改了的页数,所谓脏数据。

因此这里一共分配了63336*16/1024=1G内存的缓冲池,空闲65371个页面,已经使用了165个页面,不常常修改的数据页有3个(通常占用内存的1/3),脏页的页面有2个,这些数据能分析当前数据库的压力值。

2、配置InnoDB缓冲池大小

你能够配置InnoDB缓冲池的各个方面来提升性能。

  • 理想状况下,你将缓冲池的大小设置为尽量大的值(70%-80%)。缓冲池越大,InnoDB内存数据库的行为越多,从磁盘读取数据一次,而后在后续读取期间从内存访问数据。
  • 对于具备大内存的64位系统,你能够将缓冲池拆分红多个实例(默认8个),以最大限度地减小并发操做中内存结构的争用。

2.1 在线配置InnoDB缓冲池大小

缓冲池支持脱机和联机两种配置方式,当增长或减小innodb_buffer_pool_size时,操做以块(chunk)形式执行。块大小由innodb_buffer_pool_chunk_size配置选项定义,默认值128M。

在线配置InnoDB缓冲池大小,该innodb_buffer_pool_size配置选项能够动态使用设置SET声明,让你调整缓冲池无需从新启动服务器。例如:

mysql> SET GLOBAL innodb_buffer_pool_size=8589934592;

缓冲池大小配置必须始终等于innodb_buffer_pool_chunk_size*innodb_buffer_pool_instances的倍数。若是配置innodb_buffer_pool_size为不等于innodb_buffer_pool_chunk_size*innodb_buffer_pool_instances的倍数,则缓冲池大小将自动调整为等于或不小于指定缓冲池大小的innodb_buffer_pool_chunk_size*innodb_buffer_pool_instances的倍数。

在如下示例中, innodb_buffer_pool_size设置为8G,innodb_buffer_pool_instances设置为16,innodb_buffer_pool_chunk_size是128M,这是默认值。8G是一个有效的innodb_buffer_pool_size值,由于它是innodb_buffer_pool_instances=16乘以innodb_buffer_pool_chunk_size=128M的倍数。

mysql> select 8*1024 / (16*128);
+-------------------+
| 8*1024 / (16*128) |
+-------------------+
|            4.0000 |
+-------------------+
1 row in set (0.00 sec)

 若是innodb_buffer_pool_size设置为9G,innodb_buffer_pool_instances设置为16,innodb_buffer_pool_chunk_size是128M,这是默认值。在这种状况下,9G不是innodb_buffer_pool_instances=16*innodb_buffer_pool_chunk_size=128M的倍数 ,因此innodb_buffer_pool_size被调整为10G,这是不小于指定缓冲池大小的下一个innodb_buffer_pool_chunk_size*innodb_buffer_pool_instances的倍数。

2.2 监控在线缓冲池调整大小进度

该Innodb_buffer_pool_resize_status报告缓冲池大小调整的进展。例如:

mysql> SHOW STATUS WHERE Variable_name ='InnoDB_buffer_pool_resize_status';
+----------------------------------+-------+
| Variable_name                    | Value |
+----------------------------------+-------+
| Innodb_buffer_pool_resize_status |       |
+----------------------------------+-------+
1 row in set (0.01 sec)

2.3 配置InnoDB缓冲池块(chunk)大小

 innodb_buffer_pool_chunk_size能够在1MB(1048576字节)单位中增长或减小,但只能在启动时,在命令行字符串或MySQL配置文件中进行修改。

[mysqld]
innodb_buffer_pool_chunk_size = 134217728

修改innodb_buffer_pool_chunk_size时适用如下条件:

  • 若是新innodb_buffer_pool_chunk_size值乘以innodb_buffer_pool_instances大于初始化缓冲池大小时, innodb_buffer_pool_chunk_size则截断为innodb_buffer_pool_size / innodb_buffer_pool_instances。

例如,若是缓冲池初始化大小为2GB(2147483648字节), 4个缓冲池实例和块大小1GB(1073741824字节),则块大小将被截断为等于innodb_buffer_pool_size / innodb_buffer_pool_instances,值为:

mysql> select 2147483648 / 4;
+----------------+
| 2147483648 / 4 |
+----------------+
| 536870912.0000 |
+----------------+
1 row in set (0.00 sec)
  • 缓冲池大小必须始终等于或不小于innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances的倍数。若是更改innodb_buffer_pool_chunk_size,innodb_buffer_pool_size则会自动调整为等于或不小于当前缓冲池大小的innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances的倍数。缓冲池初始化时会发生调整。

更改时应当心innodb_buffer_pool_chunk_size,由于更改此值能够增长缓冲池的大小,如上面的示例所示。在更改innodb_buffer_pool_chunk_size以前,计算innodb_buffer_pool_size以确保生成的缓冲池大小是可接受的。

2.4 在线调整缓冲池内部大小机制

调整大小的操做由后台线程执行,当增长缓冲池的大小时,调整大小操做:

  • 添加页面chunks(chunks大小由innodb_buffer_pool_chunk_size定义)。
  • 覆盖哈希表,列表和指针以在内存中使用新的地址。
  • 将新页面添加到空闲列表中。

PS:当这些操做正在进行时,阻止其余线程访问缓冲池。

当减少缓冲池的大小时,调整大小操做:

  • 对缓冲池进行碎片整理并提取空闲页面。
  • 删除页面chunks(chunks大小由innodb_buffer_pool_chunk_size定义)。
  • 转换哈希表,列表和指针以在内存中使用新的地址。

在这些操做中,只有对缓冲池进行碎片整理和撤销页面才容许其余线程同时访问缓冲池。

InnoDB在调整缓冲池大小以前,应完成经过API执行的活动事务和操做。启动调整大小操做时,在全部活动事务完成以前,操做都不会启动。一旦调整大小操做进行中,须要访问缓冲池的新事务和操做必须等到调整大小操做完成,可是容许在缓冲池进行碎片整理时缓冲池的并发访问。缓冲池大小减小时页面被撤销,容许并发访问的一个缺点是在页面被撤回时可能会致使可用页面暂时不足。

3、配置多个缓冲池实例

对于具备多GB级缓冲池的系统,将缓冲池划分为单独的实例能够经过减小不一样线程读取和写入缓存页面的争用来提升并发性。此功能一般适用于缓冲池大小在千兆字节范围内的系统。使用innodb_buffer_pool_instances配置选项配置多个缓冲池实例,你也能够调整该 innodb_buffer_pool_size值。

当InnoDB缓冲池大时,能够经过从内存检索来知足许多数据请求。你可能会遇到多个请求一次访问缓冲池的线程的瓶颈。你能够启用多个缓冲池以最小化此争用。使用散列函数,将缓冲池中存储或读取的每一个页面随机分配给其中一个缓冲池。每一个缓冲池管理本身的空闲列表,刷新列表,LRU和链接到缓冲池的全部其余数据结构,并由其本身的缓冲池互斥锁保护。

要启用多个缓冲池实例,请将innodb_buffer_pool_instances配置选项设置为大于1(默认值)高达64(最大值)的值。此选项仅在设置innodb_buffer_pool_size为1GB或更大的大小时生效。你指定的总大小在全部缓冲池之间分配,为了得到最佳效率,指定的组合innodb_buffer_pool_instances和innodb_buffer_pool_size,使得每一个缓冲池实例是至少为1GB。

4、配置InnoDB缓冲池预读

InnoDB在io的优化上有个比较重要的特性为预读,预读请求是一个i/o请求,它会异步地在缓冲池中预先回迁多个页面,预计很快就会须要这些页面,这些请求在一个范围内引入全部页面。InnoDB以64个page为一个extent,那么InnoDB的预读是以page为单位仍是以extent?

这样就进入了下面的话题,InnoDB使用两种预读算法来提升I/O性能:线性预读(linear read-ahead)和随机预读(randomread-ahead)

为了区分这两种预读的方式,咱们能够把线性预读放到以extent为单位,而随机预读放到以extent中的page为单位。线性预读着眼于将下一个extent提早读取到buffer pool中,而随机预读着眼于将当前extent中的剩余的page提早读取到buffer pool中。

线性预读(linear read-ahead):它能够根据顺序访问缓冲池中的页面,预测哪些页面可能须要很快。经过使用配置参数innodb_read_ahead_threshold,经过调整触发异步读取请求所需的顺序页访问数,能够控制Innodb执行提早读操做的时间。在添加此参数以前,InnoDB只会计算当在当前范围的最后一页中读取整个下一个区段时是否发出异步预取请求。

线性预读方式有一个很重要的变量控制是否将下一个extent预读到buffer pool中,经过使用配置参数innodb_read_ahead_threshold,能够控制Innodb执行预读操做的时间。若是一个extent中的被顺序读取的page超过或者等于该参数变量时,Innodb将会异步的将下一个extent读取到buffer pool中,innodb_read_ahead_threshold能够设置为0-64的任何值,默认值为56,值越高,访问模式检查越严格。

mysql> show global variables like '%innodb_read_ahead_threshold%';
+-----------------------------+-------+
| Variable_name               | Value |
+-----------------------------+-------+
| innodb_read_ahead_threshold | 56    |
+-----------------------------+-------+
1 row in set (0.00 sec)

 

例如,若是将值设置为48,则InnoDB只有在顺序访问当前extent中的48个pages时才触发线性预读请求,将下一个extent读到内存中。若是值为8,InnoDB触发异步预读,即便程序段中只有8页被顺序访问。你能够在MySQL配置文件中设置此参数的值,或者使用SET GLOBAL须要该SUPER权限的命令动态更改该参数。

在没有该变量以前,当访问到extent的最后一个page的时候,Innodb会决定是否将下一个extent放入到buffer pool中。

随机预读(randomread-ahead):随机预读方式则是表示当同一个extent中的一些page在buffer pool中发现时,Innodb会将该extent中的剩余page一并读到buffer pool中,因为随机预读方式给Innodb code带来了一些没必要要的复杂性,同时在性能也存在不稳定性,在5.5中已经将这种预读方式废弃。要启用此功能,请将配置变量设置innodb_random_read_ahead为ON。

mysql> show global variables like '%innodb_random_read_ahead%';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_random_read_ahead | OFF   |
+--------------------------+-------+
1 row in set (0.01 sec)

在监控Innodb的预读时候,咱们能够经过SHOW ENGINE INNODB STATUS命令显示统计信息,经过Pages read ahead和evicted without access两个值来观察预读的状况,或者经过两个状态值,以帮助您评估预读算法的有效性。

mysql> show global status like '%Innodb_buffer_pool_read_ahead%';
+---------------------------------------+-------+
| Variable_name                         | Value |
+---------------------------------------+-------+
| Innodb_buffer_pool_read_ahead_rnd     | 0     |
| Innodb_buffer_pool_read_ahead         | 0     |
| Innodb_buffer_pool_read_ahead_evicted | 0     |
+---------------------------------------+-------+
3 rows in set (0.00 sec)

而经过SHOW ENGINE INNODB STATUS获得的Pages read ahead和evicted without access则表示每秒读入和读出的pages:Pages read ahead 1.00/s, evicted without access 9.99/s。

当微调innodb_random_read_ahead设置时,此信息可能颇有用 。

5、配置InnoDB缓冲池刷新

InnoDB会在后台执行某些任务,包括从缓冲池刷新脏页(那些已更改但还没有写入数据库文件的页)。

InnoDB当缓冲池中脏页的百分比达到定义的低水位设置时,其实就是当缓冲池中的脏页占用比达到innodb_max_dirty_pages_pct_lwm的设定值的时候,就会自动将脏页清出buffer pool,这是为了保证buffer pool当中脏页的占有率,也是为了防止脏页占有率超过innodb_max_dirty_pages_pct的设定值,当脏页的占有率达到了innodb_max_dirty_pages_pct的设定值的时候,InnoDB就会强制刷新buffer pool pages。

InnoDB采用一种基于redo log的最近生成量和最近刷新频率的算法来决定冲洗速度,这样的算法能够保证数据库的冲洗不会影响到数据库的性能,也能保证数据库buffer pool中的数据的脏数据的占用比。这种自动调整刷新速率有助于避免过多的缓冲池刷新限制了普通读写请求可用的I/O容量,从而避免吞吐量忽然降低,但仍是对正常IO有影响。

咱们知道InnoDB使用日志的方式是循环使用的,在重用前一个日志文件以前,InnoDB就会将这个日志这个日志记录相关的全部在buffer pool当中的数据刷新到磁盘,也就是所谓的sharp checkpoint,和sqlserver的checkpoint很像。当一个插入语句产生大量的redo信息须要记录的日志,当前redo log文件不可以彻底存储,也会写入到当前的redo文件当中。当redo log当中的全部使用空间都被用完了的,就会触发sharp checkpoint,因此这个时候即便脏数据占有率没有达到innodb_max_dirty_pages_pct,仍是会进行刷新。

内部基准测试显示,该算法随着时间的推移能够显著提升总体吞吐量。这种算法是经得住考验的,因此说千万不要随便设置,最好是默认值。可是咱们从中也就会知道为何redo log不可以记录两个事物的redo信息了。由于有这么多的好处,因此innodb_adaptive_flushing的值默认就是true的,默认开启自适应刷新策略。

6、微调InnoDB缓冲池刷新

配置选项innodb_flush_neighbors, innodb_lru_scan_depth可让你微调缓冲池刷新过程的某些方面,这些选项主要是帮助写密集型的工做负载。若是DML操做较为严重,若是没有较高的值,则刷新可能会降低,会致使缓冲池中的内存过多。或者,若是这种机制过于激进,磁盘写入将会使你的I/O容量饱和,理想的设置取决于你的工做负载,数据访问模式和存储配置(例如数据是否存储在HDD或SSD设备上)。

InnoDB对于具备不断繁重工做负载的系统或者工做负载波动很大的系统,可使用下面几个配置选项来调整表的刷新行为:

  • innodb_adaptive_flushing_lwm:默认值10,指定重作日志容量的“ 低水位 ”百分比,当该阈值越过期,InnoDB即便没有开启innodb_adaptive_flushing选项也会自动启用自适应刷新。
  • innodb_max_dirty_pages_pct_lwm:默认值0,InnoDB尝试从缓冲池中刷新数据,以使脏页面的百分比不超过该值innodb_max_dirty_pages_pct。默认值为75。该innodb_max_dirty_pages_pct_lwm选项是用来指定“ 低水位 ”值,其表示使用预冲洗来控制脏页比例的百分比,防止脏页的百分比达到innodb_max_dirty_pages_pct的值,innodb_max_dirty_pages_pct_lwm默认0,禁用“ 预冲洗”行为。
  • innodb_io_capacity_max,默认2000,若是刷新动做远远落后,InnoDB能够比指定的innodb_io_capacity刷新动做更积极。innodb_io_capacity_max表示在这种紧急状况下使用的I/O容量的上限,以便I/O中的尖峰消耗不到服务器的全部容量。
  • innodb_flushing_avg_loops,默认30;定义了innodb保留先前计算的刷新状态快照的迭代次数, 它控制了自适应刷新对此前负载更改的响应速度。为innodb_flushing_avg_loops设置高值意味着innodb保留之前计算的快照的时间更长,所以自适应刷新的响应速度更慢,高值还能够减小前台和后台工做之间的正面反馈。可是,在设置高值时,确保innodb重作日志利用率不达到75% (异步刷新开始时的硬编码限制) 和innodb_max_dirty_pages_pct设置将脏页的数量保持为适合于工做负荷的级别是很重要的。

上面提到的大多数选项最适用于长时间运行写入繁重工做负载的服务器。

7、保存和恢复缓冲池状态

7.1 在关闭时保存缓冲池状态并在启动时恢复缓冲池状态

能够配置在MySQL关闭以前,保存InnoDB当前的缓冲池的状态,以免在服务器从新启动后,还要经历一个预热的暖机时间。经过innodb_buffer_pool_dump_at_shutdown(服务器关闭前设置)来设置,当设置这个参数之后MySQL就会在机器关闭时保存InnoDB当前的状态信息到磁盘上。

当启动MySQL服务器时要恢复服务器缓冲池状态,请在启动服务器时开启innodb_buffer_pool_load_at_startup参数。我的认为这个值仍是须要配置一下的,MySQL 5.7.6版本以前这两个值默认是关闭的,但从MySQL 5.7.7版本开始这两个值就默认为开启状态了。这些数据是从磁盘从新读取到buffer pool当中的,这会花费一些时间,而且恢复时新的DML操做是不可以进行操做的。这些数据是怎么恢复呢?其实INNODB_BUFFER_PAGE_LRU表(INFORMATION_SCHEMA)会记录缓存的tablespace ID和page ID,经过这个来恢复。另外缓冲池状态保存文件默认在数据目录下,名为”ib_buffer_pool”,可使用innodb_buffer_pool_filename参数来修改文件名和位置。

7.2 配置缓冲池页面保存的百分比

在加载数据进入buffer pool以前,能够经过设置innodb_buffer_pool_dump_pct参数来决定恢复buffer pool中多少数据。MySQL 5.7.6版本以前的默认值是100,恢复所有,从MySQL 5.7.7版本以后默认调整为25了。能够动态设置此参数:

mysql> SET GLOBAL innodb_buffer_pool_dump_pct = 40;

7.3 在线保存和恢复缓冲池状态

要在运行MySQL服务器时保存缓冲池的状态,请发出如下语句:

mysql> SET GLOBAL innodb_buffer_pool_dump_now=ON;

要在MySQL运行时恢复缓冲池状态,请发出如下语句:

相关文章
相关标签/搜索