天天看点

MySQL5.7: 动态调整buffer pool size

在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,当然那样代码会复杂的很多很多。

继续阅读