一、多个Buffer Pool优化并发能力
- 多个线程并发访问一个Buffer Pool必然是要加锁的,让一个线程先完成一系列操作,比如加载数据页到缓存页、更新free链表、更新LRU链表、释放锁,接着下一个线程再执行一系列操作。
- Mysql设置多个Buffer Pool来优化并发能力。
一般来说,Mysql默认规则是,如果给Buffer Pool分配的内存小于1GB,那么最多只有1个Buffer Pool。
如果机器内存很大,那么必然会给Buffer Pool分配较大的内存,比如8GB,那么可以设置多个Buffer Pool,如下面的Mysql服务器端配置
[server]innodb_buffer_pool_size = 858993592innodb_buffer_pool_instances = 4
通过以上配置,Mysql在运行的时候就会有4个Buffer Pool。每个Buffer Pool负责管理一部分的缓存页和描述数据块,有自己独立的free,flush,LRU等链表。
如上图,多个线程并发访问的性能就会得到成倍的提升,因为多个线程可以在不同的Buffer Pool中加锁和执行自己的操作。
二、Buffer Pool大小优化并发能力
2.1 chunk机制动态调整Buffer Pool大小
Mysql设计了一个chunk机制,也就是说Buffer Pool是由很多个chunk组成的,它的大小由参数控制,默认值128MB。每个chunk里就是一系列的描述数据块和缓存,每个Buffer Pool里的多个chunk共享一套free,flush,LRU等链表,如下图。
基于chunk机制如何支持运行期间,动态调整Buffer Pool大小?
答:比如Buffer Pool现在总大小是8GB,现在要动态加到16GB,那么此时只要申请一系列128MB大小的chunk就可以了,只要每个chunk是连续的128MB内存就行了。然后把这些申请到chunk内存分配给Buffer Pool就行了。有了这个chunk机制,不需要额外申请16GB的连续内存空间,也不需要把已有数据进行拷贝。
2.2 Buffer Pool大小优化性能
问:如何避免执行CRUD的时候,频繁的发现缓存页都用完了,完了还得先把一个缓存页刷入磁盘腾出空闲缓存页,然后才能从磁盘读取一个自己需要的数据页到缓存页里来?毕竟如果频繁这么操作,那么很多CRUD操作,每次都要执行2次磁盘IO,一次是缓存页刷入磁盘,一次是数据页从磁盘读取出来,性能很不高。
答:关键是避免缓存页频繁的被使用完毕。实际使用缓存页的过程中,本质是一边会被使用,一边会被后台线程定时释放掉一批。如果使用的很快,后台线程释放很慢,必然导致频繁发现缓存页被使用完。但缓存页被使用的速度是无法控制的,是由Java系统访问数据库的并发程度决定的;而后台线程定时释放缓存页的过程也很难优化,因为要是释放的过于频繁了,那么后台线程执行磁盘IO过于频繁,也会影响Mysql性能。
所以关键点在于Buffer Pool的大小。如果数据库要抗高并发的访问,机器必然要配置很大的内存空间,起码是32GB以上,甚至是64GB或者128GB。此时就可以给Buffer Pool设置很大的内存空间,比如20GB,48GB甚至80GB。
三、Buffer Pool大小设置
Buffer Pool内存大小:通常来说给Buffer Pool设置机器内存的50%~60%左右,剩下的留给OS和其他来用比较合理。
Buffer Pool数量:Buffer Pool总大小=(chunk大小 × Buffer Pool数量)的倍数。
假设32GB的机器内存,那么Buffer Pool设置为20GB,默认chunk大小是128MB,假设Buffer Pool的数量是16个,此时 chunk大小 × Buffer Pool数量 = 16 × 128MB=2480MB,Buffer Pool总大小20GB是2048MB的10倍,这是符合规则的。当然设置32个也是可以的。
四、查看Buffer Pool配置
mysql> show engine innodb status;
----------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 137363456
Dictionary memory allocated 451835
Buffer pool size 8192
Free buffers 6776
Database pages 1411
Old database pages 540
Modified db pages 0
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 1269, created 142, written 162
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
No buffer pool page gets since the last printout
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 1411, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
| Mysql参数 | 含义 |
|---|---|
| Total large memory allocated | Buffer Pool 最终总大小 |
| Dictionary memory allocated | 数据词典占用的data+index的总字节数 |
| Buffer pool size | Buffer Pool 一共能容纳多少个缓存页 |
| Free buffers | free链表中一共有多少个空闲缓存页是可用的 |
| Database pages | LRU链表中一共有多少个缓存页 |
| Old database pages | 冷数据区域里的缓存页数量 |
| Modified db pages | flush链表中的缓存页数量 |
| Pending reads | 等待从磁盘加载进缓存页的数量 |
| Pending writes | 即将从LRU链表/flush链表中刷入磁盘的数量 |
| Pages made young | LRU冷数据区域里访问之后转移到热数据区域的缓存页的数量 |
| Pages made not young | LRU冷数据区域里1s内被访问了没进入热数据区域的缓存页的数量 |
| youngs/s | 每秒从冷数据区域进入热数据区的缓存页的数量 |
| non-youngs/s | 每秒在冷数据区域里被访问了但是不能进入热数据区域的缓存页的数量 |
| Pages read 1269, created 142, written 162 | 已经读取、创建和写入的缓存页数量 |
| 0.00 reads/s, 0.00 creates/s, 0.00 writes/s | 每秒读取、创建和写入的缓存页数量 |
| Buffer pool hit rate xx/1000 | 每1000次访问,有多少次是直接命中了Buffer pool中的缓存的 |
| young-making rate xx/1000 not xx/1000 | 每1000次访问,有多少次访问让缓存页从冷数据区域移动到了热数据区域,以及没移动的缓存页数量 |
| LRU len | LRU链表里的缓存页的数量 |
| I/O sum | 最近50s读取磁盘页的总数 |
| I/O cur | 正在读取磁盘页的数量 |
五、Buffer Pool的预热机制
这种机制实际上是想让重启后的MySQL快速适应大规模的流量请求。
InnoDB 在服务器关闭时为每个缓冲池保存一部分最近高频使用的页面,并在服务器启动时恢复这些页面。保存多大比例的缓存页由参数 innodb_buffer_pool_dump_pct 控制。
在启动时还原缓冲池,实际上会缩短预热的时间。你可以通过下面的方式配置该参数:
# 通过命令
SET GLOBAL innodb_buffer_pool_dump_pct=40;
# 通过文件
[mysqld]
innodb_buffer_pool_dump_pct=40
参数 innodb_buffer_pool_dump_at_shutdown 控制MySQL关闭时保存缓冲池的状态,默认on。
参数 innodb_buffer_pool_load_at_startup 控制启动MySQL的时候恢复缓冲池中的状态,默认on。
