MySQL5.7: 动态调整buffer pool size

在5.7.5中提供了一个新功能,能够动态的对buffer pool size进行调整。

对应的changelog entry:

InnoDB: The innodb_buffer_pool_size parameter is now dynamic, allowing you to resize the buffer pool without restarting the server. The resizing operation, which involves moving pages to a new location in memory, is performed chunks. Chunk size is configurable using the new innodb_buffer_pool_chunk_size configuration option. You can monitor resizing progress using the new Innodb_buffer_pool_resize_status status variable. For more information, see Resizing the InnoDB Buffer Pool Online.
See Resizing the InnoDB Buffer Pool Online for more information.

从功能来看,当前只支持总体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:
http://dev.mysql.com/worklog/task/?id=6117

官方文档:
http://dev.mysql.com/doc/refman/5.7/en/innodb-buffer-pool-online-resize.html

http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/8479
http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/8383

总的来说,这个功能没有让人感到特别惊喜,如果bp instance也能调整了,才是真正的resize,当然那样代码会复杂的很多很多。

时间: 2024-10-29 09:39:43

MySQL5.7: 动态调整buffer pool size的相关文章

innodb buffer pool相关特性

背景 innodb buffer pool作为innodb最重要的缓存,其缓存命中率的高低会直接影响数据库的性能.因此在数据库发生变更,比如重启.主备切换实例迁移等等,innodb buffer poll 需要一段时间预热,期间数据库的性能会受到明显影响. 另外mysql 5.7以前innodb buffer pool缓存大小修改不是动态的,重启才能生效.因此innodb buffer pool的预热和innodb buffer pool大小的动态修改,对性能要求较高的应用来说是不错的特性,下面

[MySQL学习] 一个压缩Page从磁盘读入buffer pool的过程

以下是边看代码边记录的,从磁盘读取一个压缩Page到buffer pool的的全过程,以函数buf_page_get_gen作为入口 buf_page_get_gen 1.根据space和offset来计算请求的page是否已经读到了buffer pool中 fold = buf_page_address_fold(space, offset); block = (buf_block_t*) buf_page_hash_get_low(                           buf

MySQL内核月报 2015.02-MySQL · 性能优化· InnoDB buffer pool flush策略漫谈

背景 我们知道InnoDB使用buffer pool来缓存从磁盘读取到内存的数据页.buffer pool通常由数个内存块加上一组控制结构体对象组成.内存块的个数取决于buffer pool instance的个数,不过在5.7版本中开始默认以128M(可配置)的chunk单位分配内存块,这样做的目的是为了支持buffer pool的在线动态调整大小. Buffer pool的每个内存块通过mmap的方式分配内存,因此你会发现,在实例启动时虚存很高,而物理内存很低.这些大片的内存块又按照16KB

MySQL · 特性分析 · innodb buffer pool相关特性

背景 innodb buffer pool做为innodb最重要的缓存,其缓存命中率的高低会直接影响数据库的性能.因此在数据库发生变更,比如重启.主备切换实例迁移等等,innodb buffer poll 需要一段时间预热,期间数据库的性能会受到明显影响. 另外mysql 5.7以前innodb buffer pool缓存大小修改不是动态的,重启才能生效.因此innodb buffer pool的预热和innodb buffer pool大小的动态修改,对性能要求较高的应用来说是不错的特性,下面

千丝万缕的FGC与Buffer pool

1 背景 运维通知,线上系统一直在FGC,通过zabbix查看GC 的次数 再查看YGC和FGC空间占用情况 这里有几个疑问: 1:old space 空间一直很低,为什么会有频繁的FGC? 2:Eden space 回收的阈值为什么越来越低,越来越频繁? 3:从Eden space空间看一直在YGC,但是从YGC的次数看并没有过YGC? 4:FGC的越来越频繁,到最后为什么一直在FGC? 第一个问题 通过查看打印出来的ERROR日志,确定是Direct buffer 不够.在申请DirectB

MySQL buffer pool初始化内存分配

之前一直理解的是buffer pool在启动时候就被分配好,今天有同事问到这个问题,跟着代码看了下. buf_pool_init |–>buf_pool_init_instance |–>buf_chunk_init |–>os_mem_alloc_large ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | OS_MAP_ANON, -1, 0); 调用mmap之前TOP出来的VIRT虚拟内存为135M,调用之后V

谁占用了我的Buffer Pool

场景带入 双十一后,老鸟接二连三的狂轰滥炸着菜鸟:"你读过一本叫<谁动了我的奶酪>的书吗?正好,你研究下谁动了SQL Server的Buffer Pool吧?". 菜鸟又是满脸懵逼茫然状:"这谁跟谁啊?有半毛钱关系吗?".没办法,老鸟交代的任务,菜鸟还是要一丝不苟的竭尽全力. 哪些数据库占用了Buffer Pool 于是,菜鸟从大处着眼:"哪些数据库占用了SQL Server的Buffer Pool?每个库占用了多少Buffer Pool的空间

MySQL · 引擎特性 · InnoDB Buffer Pool

前言 用户对数据库的最基本要求就是能高效的读取和存储数据,但是读写数据都涉及到与低速的设备交互,为了弥补两者之间的速度差异,所有数据库都有缓存池,用来管理相应的数据页,提高数据库的效率,当然也因为引入了这一中间层,数据库对内存的管理变得相对比较复杂.本文主要分析MySQL Buffer Pool的相关技术以及实现原理,源码基于阿里云RDS MySQL 5.6分支,其中部分特性已经开源到AliSQL.Buffer Pool相关的源代码在buf目录下,主要包括LRU List,Flu List,Do

简要记录一下压缩表在buffer pool中的相关结构体:

//////////////////////////////////////////////////////////// struct buf_pool_struct{ mutex_t     zip_mutex;   //用于保护压缩page(buf_page_t) mutex_t     zip_free_mutex; mutex_t     zip_hash_mutex;   //保护zip_hash hash_table_t*   zip_hash;  //hash table of b