天天看点

MySQL: 20 生产经验:通过chunk来支持数据库运行期间的Buffer Pool动态调整

1. Buffer Pool不能在运行期间动态调整

Buffer Pool并不能在运行期间动态的调整大小。

因为想要动态调整Buffer Pool大小,比如Buffer Pool本来是8G,运行期间调整为16G。这一实现是需要向操作系统申请一块新的16GB的连续内存,然后把现在的Buffer Pool中的所有缓存页、描述数据块、各种链表,都拷贝到新的16GB的内存中去,这个过程是极为耗时的,性能很低下,是不可以接受的。

就目前所了解到的一整套原理,buffer pool 是绝对不能支持运行期间动态调整大小的。

2.基于chunk机制把buffer pool给拆小

MySQL对上面的问题做了优化。设计了一个chunk机制,也就是说buffer pool 是由很多chunk组成的,它的大小是 innodb_buffer_pool_chunk_size参数控制的,默认值就是128MB。

案例:比如现在给Buffer Pool设置一个总大小是8GB,然后有4个buffer Pool,那么每个Buffer Pool就是2GB,此时每个buffer pool是由一系列的128MB的chunk组成的也就是说每个buffer pool会有16个chunk。

每个buffer pool里的每个chunk里就是一系列的描述数据块和缓存页,每个buffer pool里的多个chunk共享一套free、flush、lru这些链表。

MySQL: 20 生产经验:通过chunk来支持数据库运行期间的Buffer Pool动态调整

如上图所示,每个buffer pool里已经有了多个chunk,每个chunk就是一系列的描述数据块和缓存页,这样的话,就把buffer pool按照chunk为单位,拆分为一系列的小数据块,但是每个buffer pool是共用一套free、flush、lru的链表的。

3.基于chunk机制在运行期间动态调整buffer pool大小

基于chunk机制实现的动态调整Buffer Pool原理如下:

假设现在Buffer Poool总大小是8GB,计划要动态加到16GB,那么此时只要申请一系列的128MB大小的chunk就可以了,只要每个chunk是连续的128MB内存就行了。然后把这些申请到的chunk内存分配给buffer pool就行了。

基于以上的chunk机制,就不需要额外申请16GB的连续内存空间,然后还要把已有的数据进行拷贝了。

4. 如何避免频繁清空缓存页

问题:如何避免执行crud的时候,频繁的发现缓存页都用完了,完了还得先把一个缓存页刷入磁盘腾出一个空闲缓存页,然后才能从磁盘读取一个自己需要的数据页到缓存页里来。

如果频繁这么搞,那么很多crud操作,每次都要执行两次磁盘IO,一次是缓存页刷入磁盘,一次是数据页从磁盘里读取出来,性能会降低的。

由于在使用缓存页的过程中,有一个后台线程会定时把LRU链表冷数据区域的一些缓存页刷入磁盘中。所以本质上缓存页一边被使用,一边会被后台线程定时的释放掉一批。

如果缓存页使用的很快,而后台线程释放缓存页的速度很慢,就必然导致发现缓存页被使用完了。

由于缓存页被使用的速度是无法控制的,是由Java系统访问数据库的并发程度来决定的,越是高并发访问数据库,缓存页必然使用的越快。而释放缓存页过于频繁,就会导致后台线程执行磁盘IO过于频繁,从而影响数据库的性能。

这里的关键在于,Buffer Pool的大小。

如果Buffer Pool足够大,也就是内存很大,空闲缓存页就会很多很多,即使空间缓存页会逐步的减少,但也是需要较长时间才会发现缓存页用完,然后才会出现一次curd操作执行的时候,先刷缓存页到磁盘,再读取数据页到缓存页来,这种情况是不会出现的太频繁的。

而一旦数据库访问高峰过去,缓存页被使用的速率下降了很多很多,然后后台线程会基于flush链表和lru链表不停的释放缓存页,此时空闲缓存页的数量会在数据库低峰的时候慢慢的增加了。