InnoDB 内存管理机制 --- Buffer Pool

InnoDB Buffer Pool

是一块连续的内存,用来存储访问过的数据页面

innodb_buffer_pool_size 参数用来定义 innodb buffer pool 的大小

MySQL 中拥有最大的内存的模块

Innodb 中,数据的访问是按照页/块(默认为 16KB)的方式从数据文件中读取到

buffer pool 中,然后在内存中用同样大小的内存空间做一个映射

为了提高访问速度(也就是尽可能多地把数据文件中的页/块放到 buffer pool 中),

扫描二维码关注公众号,回复: 10293908 查看本文章

MySQL 预先就分配/准备了许多这样的空间,为的就是与 MySQL 数据文件中的页做交换,

来把数据文件中的页放到事先准备好的内存中。

buffer pool 按照最少使用算法(LRU),来管理 buffer pool ,去搜了一下这个算法:

[ LRULeast recently used),缓存淘汰算法。比较简单的一个算法,就是根据

的数据访问记录来对 buffer 中的数据进行淘汰。核心思想:如果某个数据最近被访问

过,则将来被访问的概率更高

最常见的是使用链表保存缓存数据,这也是 innodb buffer pool 的管理方式。因此,

最常访问的放在链表头,最不常访问的放在链表尾。

【命中率】

当存在热点数据的时候,LRU 的效果很好;但是偶然性或者周期性的访问会导致 LRU

的命中率急剧下降。]

buffer pool 满了的时候,就用新的数据代替 buffer pool 末尾的数据。

root@localhost][(none)][02:40:28]> show engine innodb status\InnoDB 为缓冲区和控制结构保留了额外的内存,因此总分配的空间大约比指定的缓冲池大

小大 10%

Buffer Pool 有两个区域,一个是 sublist_of_new_block 区域(热数据),一个是

sublist_of_old_clocks 区域(不经常访问的)。当访问的时候,如果没有相应数据则从磁

盘中先读入数据到 sublist of old blocks 区域,然后移动到 sublist of new blocks 区域(实际上所谓的 热数据区域和 old 数据区域都是在 LRU list 里面,只是 sublist 0f new

blocks 在链表的前面,sublist of old blocks 在链表的尾部)。

PS

Pages made young:从 old 区移动到 new 区有多少个页

Pages made not young:因为 innodb_old_blocks_time 的设置而导致页没有从

old 部分启动到 new 部分的操作。

Buffer pool hit rate:表示缓冲池的命中率,通常这个值不应该小于 95%,如果小

95%,则应该看看是不是由于全表扫描而导致 LRU 列表有污染。

同时,show engine innodb status 显示的不是当前的实时状态。

为了避免某些全表扫描的数据进入 sublist of new blocks 区域,从 5.5.x 开始,

innodb_old_blocks_pct 参数可以控制进入 sublist of old blocks 区域的百分比,默认是

37%(即 sublist of old blocks 占整个 LRU list 的百分比,如上图所示:

189598/514065=36.88%,接近 37%)。---当全表扫描或者 dump 时,可以将

innodb_old_blocks_pct 设置的小些。

这个过程还会设计 innodb_old_blocks_time 参数,比如 sublist of old blocks 中的某个

数据块被访问到了,他就会等待 innodb_old_blocks_time (毫秒),然后再移动到 sublist

of new blocks 中。一般不会去动这个参数

5.7.4 之前,如果想要更改 innodb_buffer_pool_size 的大小,只能 kill 掉进程才行,

但是 5.7.5 开始,不需要停止进程就可以动态更改 innodb_buffer_pool_size 的大小。

同时,如果 innodb_buffer_pool_size 的大小超过 1G,则需要通过调整

innodb_buffer_pool_instances=N(即开启多个内存缓冲池,每个缓冲池的大小相同,把

需要缓冲的数据 hash 到不同的缓冲池中,这样就可以进行并行的内存读写),把 bufferpool 分成若干个 instance 可以提高 MySQL 处理请求的并发能力,因为 buffer pool

过链表管理页,同时为了保护页,需要在存取的时候对链表加锁,因此多线程下,并发去读

buffer pool 需要锁的竞争和等待。

因此,修改为多个 instance ,每个 instance 管理自己的内存和链表,可以提升请求的并

发能力。

Buffer Pool 实现原理

server 启动后也会启动所有的内嵌引擎,Innodb 启动后会通过 buf_pool_init

始化所有的子系统(从命名来看貌似只是初始化 buffer pool 的,不确定是否里面有调用其

他的函数来初始化其他的系统)。

InnoDB 用一块内存区做 IO 缓存池,该缓存池不仅用来缓存 InnoDB 的索引块,

而且也能用来缓存 InnoDB 的数据块,这于 MyISAM 不同。

Buffer Pool 逻辑上由 freelistflush list LRU list 组成。free list 是空闲缓

存块列表,由下面说的 - FREE 链表 来管理;flush list 是需要刷新到磁盘的缓存块列表,

由下面所说的 flush_list 链表 来管理;LRU list InnoDB 正在使用的缓存块,也是

Bffer Pool 的核心。

在代码中,Buffer Pool buf_pool_t 结构体来描述,也是管理 buffer_pool

核心工具,包含四个部分:

- FREE 链表:存储这个实例中所有空闲的页面

- flush_list 链表:存储所有被修改过且需要刷到文件中的页面

- mutex:保护实例,因为同一时刻,只能被一个线程访问

- chunks:存储第一个页面的物理地址(因为 buffer_pool 中的页面都是放在一块

连续的内存中,即顺序存储,所以知道了第一个页面的地址指针,就可以通过这个指针访问

所有的其他页面)

上面说的 FREE 链表 和 lush_list 链表 又是用来管理的 buf_page_t 结构体的,

buf_page_t 结构体又被 buf_block_t 所管理。buf_page_t buf_block_t 是一一

对应的,都对应 Buffer Pool 中的一个 Page ,只是 buf_page_t 是逻辑的,而

buf_block_t 包含一部分物理的概念,比如这个页面的首地址指针 frame 等。

初始化一个 Buffer Pool 的实例空间的函数是 buf_chunk_init。一个 Buffer Pool

实例空间按数据结构来看分为两部分:buf_block_tbuffer pool 中页面控制头结构信息,

每一个控制头信息管理一物理页面,这些控制头信息的存储占用了部分空间,所以

innodb_buffer_pool_size 总是比 innodb_buffer_pool_bytes_data 大。);另外一部

分就是存储 page 的,由 buf_page_t 来管理。Buffer Pool 初始化过程:Buffer Pool 页面(buf_page_t)在这个实例池中从后

向前分配 page,每次分配一个页面,同时控制结构(buf_block_t)从前向后分配来和刚

刚分配的页面一一对应(也就是上文说的 buf_block_t buf_page_t 一一对应)。这

样前后同时分配也会导致一个问题:因为 block page 是成对的,所以很大可能也很正

常地就会剩下一些空间(不足以再分配一对 block page)。

buf_pool_t 结构体用来管理整个 buffer pool 实例,而 buf_block_t 用来管理页

buf_page_t),buf_block_t 主要包括一下四个部分:

其对应的页面的 frame

其所对应的 buf_page_t 的信息(所属表空间的 ID 号、页面号等)

保护这个页面的互斥量 mutex()

访问页面时对这个页面上的 lockread/write)等

以上,初始化页面完成后,这些页面的状态还是未使用(buf_block_not_used),

因此将页面加入到 -FREE 链表 中,以供使用。至此,缓冲池的一个实例就初始化完成了,

其余实例初始化过程相同。

对于整个 Buffer Pool 而言,各个实例之间是完全独立的,相互之间没有任何关系,

单独申请、单独管理、单独刷盘。

在具体使用中,针对同的页面,当一条 SQL 从客户端发往服务端时,会通过一个

HASH 算法,来映射到一个具体的实例中。

以上,就是整个 Buffer Pool 多实例的管理机制,通过多实例,可以减少系统运行

过程中不同页面之间一些操作的相互影响,从而很好地解决了由于页面之间的资源争抢导致

的性能低下问题。

更多好文关注马哥linux运维

image.png



猜你喜欢

转载自blog.51cto.com/5iwww/2482821