Bootstrap

MySQL底层概述—5.InnoDB参数优化

大纲

1.内存相关参数优化

(1)缓冲池内存大小配置

(2)配置多个Buffer Pool实例

(3)Chunk(块)大小配置

(4)InnoDB缓存性能评估

(5)Page管理相关参数

(6)Change Buffer相关参数优化

2.日志相关参数优化

(1)日志缓冲区相关参数配置

(2)日志文件参数优化

3.IO线程相关参数优化

(1)查询缓存相关的参数

(2)脏页刷盘相关的参数

(3)LRU链表相关的参数

(4)脏页刷盘相关的参数

1.内存相关参数优化(Buffer Pool参数优化)

(1)缓冲池内存大小配置

(2)配置多个Buffer Pool实例

(3)Chunk(块)大小配置

(4)InnoDB缓存性能评估

(5)Page管理相关参数

(6)Change Buffer相关参数优化

(1)缓冲池内存大小配置

一个大的日志缓冲区允许大量的事务在提交之前不写日志到磁盘,因此如果有很多增删改操作,通过设置该参数可大量减少磁盘IO次数。

专用数据库服务器上,可将缓冲池大小设置为物理内存的60% - 80%。

一.查看缓冲池大小

Buffer Pool的默认大小是128M。

# 134217728 / 1024 / 1024 = 128
mysql> show variables like '%innodb_buffer_pool_size%';
+-------------------------+-----------+
| Variable_name           | Value     |
+-------------------------+-----------+
| innodb_buffer_pool_size | 134217728 |
+-------------------------+-----------+

二.在线调整InnoDB缓冲池大小

innodb_buffer_pool_size可以动态设置,允许在不重启服务器情况下动态调整缓冲池大小。

mysql> SET GLOBAL innodb_buffer_pool_size = 268435456; -- 256M
Query OK, 0 rows affected (0.10 sec)

mysql> show variables like '%innodb_buffer_pool_size%';
+-------------------------+-----------+
| Variable_name           | Value     |
+-------------------------+-----------+
| innodb_buffer_pool_size | 268435456 |
+-------------------------+-----------+

三.监控在线调整缓冲池的进度

mysql> SHOW STATUS WHERE Variable_name='InnoDB_buffer_pool_resize_status';
+----------------------------------+----------------------------------------------------------------------+
| Variable_name                    | Value                                                        |
+----------------------------------+----------------------------------------------------------------------+
| Innodb_buffer_pool_resize_status | Size did not change (old size = new size = 268435456. Nothing to do. |
+----------------------------------+----------------------------------------------------------------------+

(2)配置多个Buffer Pool实例

当Buffer Pool的大小是GB级别时:可以将一个Buffer Pool分割成几个独立的实例,这样能降低多个线程同时读写缓存页的竞争性而提高并发性。

通过innodb_buffer_pool_instances参数可以调整实例个数。如果有多个实例,则缓存的数据页会随机放置到任意的实例中,且每个实例都有独立的Buffer Pool所有的特性。

Buffer Pool可以存放多个Instance,每个Instance由多个Chunk组成。Instance的数量范围和Chunk的总数量范围分别为1-64,1-1000。

innodb_buffer_pool_instances的默认值是1,最大可调整成64,需要在初始化数据库时完成。

mysql> show variables like 'innodb_buffer_pool_instances';
+------------------------------+-------+
| Variable_name                | Value |
+------------------------------+-------+
| innodb_buffer_pool_instances | 1     |
+------------------------------+-------+

(3)Chunk(块)大小配置

增大或减小缓冲池大小时,将以Chunk为单位进行操作,Chunk的大小是由参数innodb_buffer_pool_chunk_size决定的。引入Chunk是为了方便在线修改缓冲池大小,修改时以Chunk为单位拷贝Buffer Pool。

mysql> show variables like 'innodb_buffer_pool_chunk_size';
+-------------------------------+-----------+
| Variable_name                 | Value     |
+-------------------------------+-----------+
| innodb_buffer_pool_chunk_size | 134217728 | 
+-------------------------------+-----------+

缓冲池大小innodb_buffer_pool_size:必须始终等于或者是chunk_size * instances的倍数,如果不等于则MySQL会自动调整。

假设innodb_buffer_pool_chunk_size=128M,而且innodb_buffer_pool_instances=16,那么可以计算出:innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances=2G。如果设置innodb_buffer_pool_size=9G,则会被自动调整为2G的倍数10G。

(4)InnoDB缓存性能评估

当前配置的innodb_buffer_pool_size是否合适,可通过分析InnoDB缓冲池的缓存命中率来验证,以下公式可以计算InnoDB Buffer Pool命中率:

命中率 = innodb_buffer_pool_read_requests / (innodb_buffer_pool_read_requests + innodb_buffer_pool_reads) * 100

参数1:innodb_buffer_pool_reads

表示缓冲池无法满足的请求数,要从磁盘读取。

参数2:innodb_buffer_pool_read_requests

表示从缓冲池中读取页的请求数,如果命中率低于90%,则可考虑增加innodb_buffer_pool_size。

mysql> show status like 'innodb_buffer_pool_read%';
+---------------------------------------+-------+
| Variable_name                         | Value |
+---------------------------------------+-------+
| Innodb_buffer_pool_read_ahead_rnd     | 0     |
| Innodb_buffer_pool_read_ahead         | 0     |
| Innodb_buffer_pool_read_ahead_evicted | 0     |
| Innodb_buffer_pool_read_requests      | 12701 |
| Innodb_buffer_pool_reads              | 455   |
+---------------------------------------+-------+

-- 此值低于90%,则可以考虑增加innodb_buffer_pool_size;
mysql> select 12701 / (455 + 12701) * 100 ;
+-----------------------------+
| 12701 / (455 + 12701) * 100 |
+-----------------------------+
|                     96.5415 |
+-----------------------------+

(5)Page管理相关参数

innodb_page_size只能在初始化MySQL实例之前配置,不能在之后修改。如果没有指定值,则使用默认页面大小初始化实例。

查看Page页的大小(默认16K)如下:

mysql> show variables like '%innodb_page_size%';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| innodb_page_size | 16384 |
+------------------+-------+

查看Page页管理状态的相关参数如下:

mysql> show global status like '%innodb_buffer_pool_pages%';
+----------------------------------+-------+
| Variable_name                    | Value |
+----------------------------------+-------+
| Innodb_buffer_pool_pages_data    | 515   |
| Innodb_buffer_pool_pages_dirty   | 0     |
| Innodb_buffer_pool_pages_flushed | 334   |
| Innodb_buffer_pool_pages_free    | 15868 |
| Innodb_buffer_pool_pages_misc    | 0     |
| Innodb_buffer_pool_pages_total   | 16383 |
+----------------------------------+-------+

# pages_data:缓冲池中包含数据的页数,包括脏页和干净页
# pages_dirty:内存中修改但未写入文件的缓冲池数据页数量
# pages_flushed:表示从InnoDB缓冲池中刷新脏页的请求数
# pages_free:显示InnoDB缓冲池中的空闲页面
# pages_misc:用于管理或哈希索引而不能用作普通页的数目
# pages_total:缓存池的页总数目,单位是Page

一.优化建议

innodb_page_size的官方描述:

MySQL5.7增加了对32K和64K页面大小的支持,所以MySQL5.7支持最小4K最大64K的页面大小设置。

默认的16K或更大的页面大小适用于各种工作负载,特别是涉及表扫描的查询和涉及批量更新的DML操作。对于涉及许多小写操作的OLTP工作负载,较小的页面大小可能更有效。

二.Page大小对于行存储的影响

对于4K、8K、16K和32K的页大小,最大行大小略小于页大小的一半。当然,不包括存储在页外的任何可变长度的列。

三.Page大小对于索引的影响

如果在创建MySQL实例时通过指定innodb_page_size选项,将InnoDB页面大小减少到8K或4K,则索引键的最大长度将按比例降低。这是基于16K页面大小的3072字节限制。也就是说,当页面大小为8K时,最大索引键长度为1536字节。当页面大小为4K时,最大索引键长度为768字节。

(6)Change Buffer相关参数优化

Change Buffer是MySQL5.5加入的新特性,Change Buffer是Insert Buffer的加强。Insert Buffer只针对insert有效,Change Buffer对insert、delete、update、purge都有效。

一.配置Change Buffer使用模式

innodb_change_buffering配置参数说明:

mysql> show variables like '%innodb_change_buffering%';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| innodb_change_buffering | all   |
+-------------------------+-------+

| 选项    | 说明                         |
| ------- | --------------------------- |
| inserts | 插入缓冲                     |
| deletes | 删除标记缓冲                  |
| changes | 更新缓冲,由两个缓冲区组成       |
| purges  | 缓冲在后台发生的物理删除操作     |
| all     | 表示启用上面所有配置(默认)      |
| none    | 表示不启用任何配置             |

二.配置Change Buffer大小

Change Buffer占用Buffer Pool空间,默认占25%,最大允许占50%。可以根据读写业务量来调整innodb_change_buffer_max_size,在写多读少+更新后数据不会被立即查询的场景,更适合用Change Buffer。

mysql> show variables like 'innodb_change_buffer_max_size';
+-------------------------------+-------+
| Variable_name                 | Value |
+-------------------------------+-------+
| innodb_change_buffer_max_size | 25    |
+-------------------------------+-------+
1 row in set (0.00 sec)

三.查看Change Buffer的工作状态

-- 查看Change Buffer的工作状态
-------------------------------------
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


# size:表示已经合并到辅助索引页的数量
# free list len:表示空闲列表长度
# seg size:表示当前Change Buffer的大小
# merges:表示合并的次数
# merged operations:每个操作合并次数
# insert:表示插入操作
# delete mark:表示删除标记操作
# delete:表示物理删除操作

2.日志相关参数优化

(1)日志缓冲区相关参数配置

(2)日志文件参数优化

(1)日志缓冲区相关参数配置

日志缓冲区的大小,一般默认值16M都够用了。但如果事务中含有BLOB/TEXT等大字段,这个缓冲区会被很快填满会引起额外的IO负载。可以根据情况配置更大的日志缓冲区,从而有效提高InnoDB的效率。

一.通过参数innodb_log_buffer_size查看日志缓冲区大小

mysql> show variables like 'innodb_log_buffer_size';
+------------------------+----------+
| Variable_name          | Value    |
+------------------------+----------+
| innodb_log_buffer_size | 16777216 |
+------------------------+----------+

二.通过参数innodb_log_files_in_group查看日志组文件个数

日志组根据需要来创建,日志组的成员则需要至少2个,以实现循环写入并作为冗余策略。

mysql> show variables like 'innodb_log_files_in_group';
+---------------------------+-------+
| Variable_name             | Value |
+---------------------------+-------+
| innodb_log_files_in_group | 2     |
+---------------------------+-------+

三.通过参数innodb_log_file_size查看redo日志文件大小

参数innodb_log_file_size用于设定InnoDB日志组中每个日志文件的大小。参数innodb_log_file_size默认48M,参数innodb_log_file_size是一个全局的静态参数,不能动态修改。每组的文件大小不能超过512G,所以每个日志文件的大小不能超过256G。

mysql> show variables like 'innodb_log_file_size';
+----------------------+----------+
| Variable_name        | Value    |
+----------------------+----------+
| innodb_log_file_size | 50331648 |
+----------------------+----------+

(2)日志文件参数优化

首先看日志文件大小设置对性能的影响。

一.设置过小

影响一:参数innodb_log_file_size设置太小,会导致redo log日志文件频繁切换。频繁触发数据库的检查点checkpoint,导致刷新脏页到磁盘的次数增加,从而影响性能。

影响二:处理大事务时,将所有的日志文件写满了,事务内容还没有写完,这样就会导致日志不能切换。

二.设置过大

参数innodb_log_file_size如果设置太大,虽然可以提升IO性能。但是当数据库意外宕机时,二进制日志文件可能会很大,那么恢复的时间就必然很长,而且恢复时间不可控,受多方面因素影响。

三.优化建议

如何设置合适的日志文件大小,根据实际生产的优化经验,一般是计算一段时间内生成的redo log大小。InnoDB的日志文件的大小最少应该承载一个小时的业务日志量,需要估算出当前系统的一小时内产生的日志数量。

步骤1:获取一分钟内的redo log日志数据量

想要估计redo log的大小,就需要抓取一段时间内Log Sequence Number的数据来计算。自系统修改开始,就不断修改页面,不断生成redo日志。为了记录一共生成了多少日志,InnoDB设计了全局变量Log Sequence Number,简称LSN。LSN不是从0开始的,而是从8704字节开始。

-- pager分页工具, 只获取sequence的信息
mysql> pager grep sequence;
PAGER set to 'grep sequence'

-- 查询状态,并倒计时一分钟
mysql> show engine innodb status\G select sleep(60);
Log sequence number 5399154
1 row in set (0.00 sec)

1 row in set (1 min 0.00 sec)

-- 一分时间内所生成的数据量 5406150
mysql> show engine innodb status\G select sleep(60);
Log sequence number 5406150

-- 关闭pager
mysql> nopager;
PAGER set to stdout

步骤2:根据一分钟的redo log日志数据量,推算一小时内的日志数据量

select (5406150 - 5399154) / 1024 as kb_per_min;
+------------+
| kb_per_min |
+------------+
|     6.8320 |
+------------+

select (5406150 - 5399154) / 1024 * 60 as kb_per_min;
+------------+
| kb_per_min |
+------------+
|   409.9219 |
+------------+

3.IO线程相关参数优化

(1)查询缓存相关的参数

(2)脏页刷盘相关的参数

(3)LRU链表相关的参数

(4)脏页刷盘相关的参数

数据库属于IO密集型的应用程序,其主要职责就是数据的管理及存储工作。从内存中读取一个数据库数据的时间是微秒级别,从一块普通硬盘上读取一个IO的时间是毫秒级别。要优化数据库,IO操作是必须要优化的,尽可能将磁盘IO转化为内存IO。

(1)查询缓存相关的参数

查询缓存Query Cache会保存SQL查询返回的完整结果。当查询命中查询缓存Query Cache时,会跳过解析、优化和执行阶段,立刻返回结果。查询缓存Query Cache会跟踪查询中涉及的每个表,如果这些表发生变化,那么和这些表相关的查询缓存都将失效。

一.查看查询缓存Query Cache是否开启

-- 查询是否支持查询缓存
show variables like 'have_query_cache';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| have_query_cache | YES   |
+------------------+-------+

-- 查询是否开启查询缓存 默认关闭
show variables like '%query_cache_type%';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| query_cache_type | OFF   |
+------------------+-------+

二.在my.ini中添加参数开启查询缓存Query Cache

query_cache_size=128M
query_cache_type=1

# query_cache_type=0,缓存禁用;
# query_cache_type=1,缓存所有的结果;
# query_cache_type=DENAND,只缓存在select语句中通过SQL_CACHE指定需要缓存的查询;

三.测试能否缓存查询

mysql> show status like '%Qcache%';
+-------------------------+---------+
| Variable_name           | Value   |
+-------------------------+---------+
| Qcache_free_blocks      | 1       |
| Qcache_free_memory      | 1031832 |
| Qcache_hits             | 0       |
| Qcache_inserts          | 0       |
| Qcache_lowmem_prunes    | 0       |
| Qcache_not_cached       | 1       |
| Qcache_queries_in_cache | 0       |
| Qcache_total_blocks     | 1       |
+-------------------------+---------+

# Qcache_free_blocks:缓存中目前剩余的block数量
# Qcache_free_memory:空闲缓存的大小
# Qcache_hits:命中缓存次数
# Qcache_inserts:未命中然后进行正常查询
# Qcache_lowmem_prunes:查询因内存不足而被移除出查询缓存记录数
# Qcache_not_cached:没有被缓存的查询数量
# Qcache_queries_in_cache:当前缓存中缓存的查询数量
# Qcache_total_blocks:当前缓存的block数量

四.优化建议

开启查询缓存适用于读多写少的场景,如果写比较多,那么更新查询缓存会损耗性能。

查询缓存的开启主要需要两个参数配合:也就是query_cache_size和query_cache_type。如果数据变化不多(写少),一般缓存大小query_cache_size设置为256M,当然也可以通过计算Query Cache的命中率来进行调整:

Qcache_hits / ( Qcache_hits + Qcache_inserts ) * 100

(2)脏页刷盘相关的参数

一.参数innodb_max_dirty_pages_pct的作用

该参数是InnoDB用来设置Buffer Pool中脏页的百分比,默认是75。当脏页数量占比超过该参数设置的值时,InnoDB会启动刷脏页的操作。

-- innodb_max_dirty_pages_pct 参数可以动态调整,最小值为0,最大值为99.99,默认值为75
show variables like 'innodb_max_dirty_pages_pct';
+----------------------------+-----------+
| Variable_name              | Value     |
+----------------------------+-----------+
| innodb_max_dirty_pages_pct | 75.000000 |
+----------------------------+-----------+

二.优化建议

该参数比例值越大,从内存到磁盘的写入操作就会相对减少,所以该参数比例值越大越能一定程度减少写入操作的磁盘IO。但是如果这个比例值过大,当数据库Crash后重启的时间可能会很长,因为会有大量的事务数据需要从日志文件恢复出来写入数据文件中。最大不建议超90,一般重启恢复的数据如果超1G,启动速度就会变慢。

(3)LRU链表相关的参数

一.两个相关参数的作用

参数innodb_old_blocks_pct是用来确定LRU链表中冷数据区域所占比例,参数innodb_old_blocks_pct默认37,表示冷数据区域默认占用37%。

mysql> show variables like '%innodb_old_blocks_pct%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| innodb_old_blocks_pct | 37    |
+-----------------------+-------+

参数innodb_old_blocks_time是用来控制冷数据区域中Page的转移策略,参数innodb_old_blocks_time默认值是1秒。新的Page页在进入LRU链表时,会先插入到冷数据区域的头部。然后Page需要在冷数据区域中停留innodb_old_blocks_time后,下一次对该Page的访问才会使其移动到热数据区域的头部。

mysql> show variables like '%innodb_old_blocks_time%';
+------------------------+-------+
| Variable_name          | Value |
+------------------------+-------+
| innodb_old_blocks_time | 1000  |
+------------------------+-------+

二.优化建议

在没有大表扫描的情况下,并且数据多为频繁使用的数据时,可以增加innodb_old_blocks_pct的值,减小innodb_old_blocks_time的值,让数据页能够更快和更多的进入的热点数据区。

(4)与脏页刷盘相关的参数

一.两个相关参数的作用

InnoDB1.0.x版本开始提供innodb_io_capacity参数,它的作用在两个方面:

作用1:合并插入缓冲时,每秒合并插入缓冲数量为innodb_io_capacity值的5%,默认200*5%=10。

作用2:从缓冲区刷新脏页时(Checkpoint),每秒刷新脏页数量为innodb_io_capacity的值,默认是200。若用户使用了SSD类的磁盘,或者将几块磁盘做了RAID,即当存储设备拥有更高的IO时,可将innodbio_capacity_max的值调高,更好利用磁盘IO的吞吐量。

mysql> show variables like '%innodb_io_capacity%';
+------------------------+-------+
| Variable_name          | Value |
+------------------------+-------+
| innodb_io_capacity     | 200   |
| innodb_io_capacity_max | 2000  |
+------------------------+-------+

二.优化建议

在有频繁写入的操作时,可以对该参数进行调整。该参数设置的大小取决于硬盘的IOPS,即每秒的输入输出量(或读写次数),什么样的磁盘配置应该设置innodb_io_capacity参数的值是多少。下面仅供参考,建议通过sysbench或其他基准测试工具对磁盘吞吐量测试。

;