innodb的缓冲池(buffer pool)

参数:innodb_buffer_pool_size。我们线上基本上设置50-75%。设过80,发现OOM有点严重。

不过这个值肯定是越大,性能越好。

如何在缓冲池中标记一个页?

(space,page_num)

space指的是你ibd文件的编号。在information_shcema中 innodb_sys_tablespaces中,就有标注表的space id。

同样innodb_buffer_page中。就有当前缓冲池中有哪些页。

另外,线上不要看这个表,你用来学习没问题。

 

那么问题来了。大家都知道,在disk中,mysql是用b+树来管理数据的。

那么如果我要查询的数据,就在内存中,但是此时内存中有1w个页同时存在。怎么快速定位。或者说,内存中,mysql是怎么管理page的。

答案是hash表。用(space,page_num)做的索引列。

 

而缓冲池中,不仅仅只有数据页,还有其他东西要存放。

比如:

数据页,change buffer(这么写肯定不专业,因为change buffer包括很多,比如insert buffer,delete buffer,purge buffer不过我懒。),自适应hash,锁(没错,mysql的锁信息是保存在内存中的,和oracle不一样)。

另外关于锁的部分有一点要补充。5.5版本前,你要向内存中存放锁信息,其实是向bf申请16k的页,不够继续要,也就是说,你可能加锁失败。5.5之后就好很多了,直接找操作系统要,底层代码是直接malloc。

 

上次我们有聊过compressed压缩表。比如说你disk中,页大小是8k。

那么加载到内存中后,会先解压,并且同时在内存中持有一个8k页和一个16k页。并且在二次读取的时候,直接读16k页,不用重复解压。

那么如果要对这个页上的row做dml怎么办?

实际上是会对16k的页直接改数据,然后在8k的数据页上写redo log。如果8k的页写redo log写满了,mysql会对16k的页进行压缩,如果压缩不到8k,那么会再申请一个8k页和16k的解压页。至于落盘,则是直接落8k。

记录日志的原因就是不用每次写入都进行解压和压缩。节约cpu。所以压缩表是用cpu换IO,这是优点。缺点就是占bf。如果你线上表要做报表,也就是olap,那么就不要开压缩表了。join很占内存的。

 

然后我们来谈谈innodb_buffer_pool_instance.这个参数一般是cpu核数的数量。我是建议用物理核数,哪怕你开启了ht,也建议用物理核数,别用逻辑核数。

为什么会有这个参数的引入。

因为在对bp操作时。其实bp是有锁的。当然不是指的lock,这个太重了。这里指的是latch。

所以,同样大小的bp,分散在不同的instance下。latch的粒度自然也就降低了,并发自然上升了。

这个参数对性能的影响我个人觉得仅次于bpsize。

 

 

然后我们来谈谈bp的管理。

buffer_pool包括:

  free list

  lru list(LRU,unzip_LRU)

  flush list(根据oldest_lsn进行排序)

 

free list加上lru list基本上等于bp。flush list包括在lru list中。

free list很明显,放的是空闲的页。如果要把disk中的页加载到内存中,就会在free list中申请。

那么此时就会有一个问题,争用,或者说是并发控制。

这就是为什么要对bp加latch的原因。有并发,就要有控制。

好,这时候,free list中有一个页被使用了。

它被加载到lru list中了。然后我们对这个页进行了修改。这时候,这个页就叫做脏页。然后这个页的指针会被放入flush list。

也就是说flush list不直接存放页,而是存放逻辑页。

 

这时候你show engine innodb status\G

buffer pool and memory是整个buffer pool的信息。

找到individual buffer pool info 部分

就能看见每个buffer pool instance的信息。

free buffers就是free list的页数量。

database pages就是lru的页数量。

modified db pages就是脏页。

 

 

讲一下lru list相关的东西。

首先,MySQL的lru算法是有改动的。他并不会在你访问了某一页后,马上把此页放到列首。

而是,MySQL把lru列表分为了两端,一段是new,一段是old。new占整个lru列表的5/8,这个比例由innodb_old_block_pct这个参数控制。

在读到某一页时,MySQL会把此页放在new段和old段的交点。这个交点叫midpoint。

然后这个页什么时候会被放在new段哪?这里还有个参数innodb_old_block_time,默认是0,意思是这个页第二次被读到了,我就把它放入new段。如果把这个值设为1000,意思就是在1秒内,不管这个页被读到多少次,它都在midpoint这个位置,不会把他放入new段。

 

posted @ 2022-06-27 16:55  拿什么救赎  阅读(207)  评论(0)    收藏  举报