22、生产经验:如何通过chunk来支持数据库运行期间的Buffer Pool动态调整?
1、Buffer Pool这种大块头,能在运行期间动态调整大小吗?
上一篇文章和大家一起学习了Buffer Pool在多线程并发访问的时候的一些问题,以及通过多个Buffer Pool是如何优化多线程并发访问性能的。
那么这一片文章我们接着下次下一个问题,那就是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这些链表,此时的话,看起来可能大致如下图所示:
在上面的图里,可以清晰的看到,每个Buffer Pool里已经有了多个chunk,每个chunk就是一系列的描述数据块和缓存页,这样的话,就是把Buffer Pool按照chunk为单位,差费为了一系列的小数据块,但是每个Buffer Pool是公用一套free、flush、LRU链表的
3、基于chunk机制是如何支持运行期间,动态调整Buffer Pool大小的?
那么现在有了上面的这套chunk机制,就可以支持动态调整Buffer Pool大小了。
比如我们Buffer Pool现在总大小是8Gb,现在要动态加到16GB,那么此时只要申请一系列的128MB大小的chunk就可以了,只要每个chunk是连续的128MB内存就行了。然后把这些申请到的chunk内存分配给Buffer Pool就行了。
有了这个chunk机制,此时并不需要额外申请16GB的连续内存空间,然后还要把已有的数据进行拷贝。
和大家一起学习这个chunk机制,不是让大家在数据库运行的时候动态调整Buffer Pool大小,其实这不是重点,重点是大家要了解数据库的Buffer Pool的真实的数据结构,是可以由多个Buffer Pool组成的,每个Buffer Pool是多个chunk组成的,然后只要知道他运行期间可以支持动态调整大小就可以了
4、思考题解答
上两篇文章有一个思考题,到底如何避免你执行CRUD的时候,频繁的发现缓存页都用完了,需要把一个缓存页刷入磁盘腾出一个空闲缓存页,然后才能从磁盘读取一个自己需要的数据页到缓存页里阿里。
如果频繁这么搞,那么很多CRUD操作,每次都要执行两次磁盘IO,一次是缓存页刷入磁盘,一次是数据页从磁盘里读取出来,性能不高。
结合我们了解到的Buffer Pool的运行原理就可以知道,如果要避免上述问题,说白了就是避免缓存页频繁的被使用完毕,那么我们知道实际上你在使用缓存页的过程中,有一个后台线程会定时把LRU链表冷数据区域的一些缓存页刷入磁盘中。
所以本质上缓存页一边会被你使用,一边会被后台线程定时的释放掉一批。
所以如果你的缓存页使用的很快,然后后台线程释放缓存页的速度很慢,那么必然导致你频繁发现缓存页被使用完了。但是缓存页被使用的速度你是没法控制的,因为那是由你的java系统访问数据库的并发程度来决定的,你高并发访问数据库,缓存页必然使用的很快了。
然后你后台线程定时释放一批缓存页,这个过程也很难去优化,因为你要是释放的过于频繁了,那么后台线程执行磁盘IO过于频繁,也会影响数据库的性能。
所以这里的关键点就在于,你的Buffer Pool有多大。
如果你的数据库要抗高并发的访问,那么你的机器必然要配置很大的内存空间,起码是32GB以上的,甚至64GB或者128GB。此时你就可以给你的Buffer Pool设置很大的内存空间,比如20GB、48GB,甚至80GB。
这样的话,你会发现高并发场景下,数据库的Buffer Pool缓存页频繁的被使用,但是你后台线程也在定时适当一些缓存页,那么综合下来,空闲的缓存页还是会以一定的速度逐步逐步的减少。
因为你的Buffer Pool内存很大,所以空闲缓存页是很多很多的,即使你的空闲缓存页逐步的减少,也可能需要较长时间才会发现缓存页用完了,此时才会出现一次CRUD操作执行的时候,先刷缓存页到磁盘,在读取数据页到缓存页来。 这种情况是不会出现的太频繁的。
而一旦你的数据库高峰过去,此时缓存页被使用的速率下降了很多很多,然后后台线程会定时基于flush链表和LRU链表不停的释放缓存页,那么你的空闲缓存页的数量又会在数据库低峰的时候慢慢的增加了。
所以线上的MySQL在生产环境中,Buffer Pool的大小、Buffer Pool的数量,这都是要用心设置和优化的,因为多MySQL的性能和并发能力,都会有较大的影响