在5.7.5中提供了一个新功能,能够动态的对buffer pool size进行调整。
对应的changelog entry:
从功能来看,当前只支持总体bp size的调整,并不支持bp instance调整,因此实现会比较简单,表是根据space id模 bp instance来选择的,如果允许动态调整bp instance,还需要处理bp实例间的迁移。
另外在resize的过程中,buffer pool是不可用的,用户请求将会被堵塞住一会,这意味着还无法做到真正的online。不过幸好这种resize操作,通常都还算比较快,偷偷在半夜业务负载低的时候操作下就行了。。。
其大约实现思路为:
0.background
buffer pool新成员变量 (buf_pool_t)
volatile ulint n_chunks
number of buffer pool chunks
volatile ulint n_chunks_new
new number of buffer pool chunks
buf_chunk_t* chunks_old
old buffer pool chunks to be freed after resizing buffer pool
ulint old_size
previous pool size in pages
ulint withdraw_target
target length of withdraw block list, when withdrawing
ut_list_base_node_t(buf_page_t) withdraw;
base node of the withdraw block list. it is only used during shrinking buffer pool size, not to reuse the blocks will be removed
新的全局变量
volatile bool buf_pool_resizing
true when resizing buffer pool is in the critical path
volatile bool buf_pool_withdrawing
true when withdrawing buffer pool pages might cause page relocation
volatile ulint buf_withdraw_clock;
the clock is incremented every time a pointer to a page may become obsolete;
if the withdrwa clock has not changed, the pointer is still valid in buffer
pool. if changed, the pointer might not be in buffer pool any more
1.发起请求
设置变量innodb_buffer_pool_size时,触发函数innodb_buffer_pool_size_update,在必要的检查后(例如避免重复发送请求,或者resize的太小),发送信号量srv_buf_resize_event.然后立刻返回
因此设置变量成功,不等于bp 的size已经调整好了,只是出发了 一个resize请求而已.
不过我们可以通过如下status变量来监控resize的过程,例如:
root@(none) 09:47:43>set global innodb_buffer_pool_size = 42949672960; show status like ‘innodb_buffer_pool_resize_status';
query ok, 0 rows affected (0.00 sec)
+———————————-+————————————+
| variable_name | value |
| innodb_buffer_pool_resize_status | withdrawing blocks to be shrunken. |
1 row in set (0.00 sec)
在新的逻辑里,所有的buffer pool内存被划分为chunk单位,每个chunk的大小默认为128m,这种方式给增加/减少bp size带来了极大的便利,使得实现更简单了。
2.执行请求
新的独立线程buf_resize_thread专门用于后台进行buffer pool的size调整,它会监听用户发出的请求事件srv_buf_resize_event
当收到resize请求后,会进行如下动作(函数buf_pool_resize)
a) 计算调整后,每个bp instance的内存大小
b)更新每个bp instance的相关变量:
buf_pool->curr_size = new_instance_size;
buf_pool->n_chunks_new = new_instance_size * univ_page_size
/ srv_buf_pool_chunk_unit;
其中srv_buf_pool_chunk_unit为只读变量,默认为,受参数innodb_buffer_pool_chunk_size控制。resize都是以chunk为单位进行的。
c)禁止自适应hash
btr_search_disable()
清楚所有索引的index->search_info->ref_count,设置所有的block->index = null,以及清理btr_search_sys->hash_index
d)如果降低buffer pool size,则计算需要缩小的chunk数目并设置:
buf_pool->withdraw_target = withdraw_target; ——>需要缩小的block数
设置buf_pool_withdrawing为true
e)当需要缩小bp size时,将withdraw_target 这么多的block转移到新的区域,需要首先搜集对应的page,到buf_pool->withdraw链表.
函数buf_pool_withdraw_blocks 的流程大概为:
(1)buf_buddy_condense_free(buf_pool);
合并所有空闲的buddy,主要是用于压缩表的.
(2)扫描buffer pool
…首先扫描free list,将需要withdraw的block(buf_block_will_withdrawn) 从free list上取出,加入到buf_pool->withdraw链表
…然后刷一次lru list,以期望刷出更多的空闲page (buf_flush_do_batch),flush lru长度受限
…重新为withdraw区域的block/buddies分配到无需withdraw的block上.从lru的第一个block开始
上述过程在buf_pool->withdraw长度达到buf_pool->withdraw_target时结束,是个循环重复的过程
f).完成搜集需要缩减的block加入到buf_pool->withdraw后,设置buf_pool_withdrawing为false;
注意在这之前还是允许并发负载的,但随后进入的区域是不允许用户进行任何buffer pool操作的。
g)设置buf_pool_resizing为true.
获取所有的buffer pool mutex, 即buffer pool page_hash的x lock.
h)进入增加/删除chunk阶段
…当缩小bp size时,删除多余的chunk.释放相应的资源,清空buf_pool->withdraw
…重新分配buf_pool->chunks
…如果增长bp size的话,初始化新的chunk(buf_chunk_init(buf_pool, chunk, unit)),分配新的内存区域,初始化新的block等..
…最后设置buf_pool->curr_size为新的size
i)重置每个bp instance的read_ahead_area 和curr_pool_size
j) 如果bp size的改动超过上次的一半或2倍,则重设page hash,和zip hash
调用函数buf_pool_resize_hash(buf_pool)
k)设置buf_pool_resizing为false,并释放所有的buffer pool锁, 释放老的chunk
l)如果bp size的改动超过上次的一半或2倍,则重置lock sys ,ahi及数据词典的大小
2648 /* normalize lock_sys */
2649 srv_lock_table_size = 5 * (srv_buf_pool_size / univ_page_size);
2650 lock_sys_resize(srv_lock_table_size);
2651
2652 /* normalize btr_search_sys */
2653 btr_search_sys_resize(
2654 buf_pool_get_curr_size() / sizeof(void*) / 64);
2655
2656 /* normalize dict_sys */
2657 dict_resize();
m)更新change buffer的大小并开启ahi
ibuf_max_size_update(srv_change_buffer_max_size);
当然过程远没这么简单,还涉及到大量相关的代码路径,感兴趣的可以看看以下的rev:
worklog:
<a href="http://dev.mysql.com/worklog/task/?id=6117">http://dev.mysql.com/worklog/task/?id=6117</a>
官方文档:
<a href="http://dev.mysql.com/doc/refman/5.7/en/innodb-buffer-pool-online-resize.html">http://dev.mysql.com/doc/refman/5.7/en/innodb-buffer-pool-online-resize.html</a>
<a href="http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/8479">http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/8479</a>
<a href="http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/8383">http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/8383</a>
总的来说,这个功能没有让人感到特别惊喜,如果bp instance也能调整了,才是真正的resize,当然那样代码会复杂的很多很多。