Mysql缓存-缓存与读写、数据Flush相关

在日常Mysql查询过程中,可能会出现“抖”一下?
那么“抖”一下这个词后面可能会包含几种阻塞场景?
我总结了下3种情况(有漏的、错的欢迎补充哈!):
(1)IO问题
(2)锁
(3)数据flush
那么,今天主要分析下数据库读写、内存与日志应用、以及数据Flush过程。

了解下这篇文章涉及到的一些概念:

1.当内存数据页跟磁盘数据页内容不一致的时候,我们称这个内存页为“脏页”。
2.内存数据写入到磁盘后,内存和磁盘上的数据页的内容就一致了,称为“干净页”。
3.清除或者重新加载内部缓存,称为“flush”。
4.数据库缓冲池称为"buffer pool"。
5.存放二级索引的没有在bufferr pool的变更页的缓存区,变更的buffer是由insert,update,delete等操作导致的。等页被加载进buffer pool中后会将change buffer中的页合并,这块缓存区称为“change buffer”。

InnoDB数据读写

InnoDB的数据是按数据页为单位来读写的。当需要读一条记录的时候,并不是将这个记录本身从磁盘读出来,而是以页为单位,将其整体读入内存。在InnoDB中,每个数据页的大小默认是16KB。

1.更新一个数据页

数据页在内存中就直接更新,而如果这个数据页还没有在内存中的话,在不影响数据一致性的前提下,InooDB会将这些更新操作缓存在change buffer中,这样就不需要从磁盘中读入这个数据页了。在下次查询需要访问这个数据页的时候,将数据页读入内存,然后执行change buffer中与这个页有关的操作。
实际上它是可以持久化的数据。change buffer在内存中有拷贝,也会被写入到磁盘上。

2.插入一个新记录

<1>这个记录要更新的目标页在内存中。
对于唯一索引来说,找到3和5之间的位置,判断到没有冲突,插入这个值,语句执行结束;
对于普通索引来说,找到3和5之间的位置,插入这个值,语句执行结束。
<2>这个记录要更新的目标页不在内存中。
对于唯一索引来说,需要将数据页读入内存,判断到没有冲突,插入这个值,语句执行结束;
对于普通索引来说,则是将更新记录在change buffer,语句执行就结束了。

将数据从磁盘读入内存涉及随机IO的访问,是数据库里面成本最高的操作之一。change buffer因为减少了随机磁盘访问,所以对更新性能的提升是会很明显的。

3.merge操作

将change buffer中的操作应用到原数据页,得到最新结果的过程称为merge。除了访问这个数据页会触发merge外,系统有后台线程会定期merge。在数据库正常关闭(shutdown)的过程中,也会执行merge操作。
change buffer用的是buffer pool里的内存,因此不能无限增大。change buffer的大小,可以通过参数innodb_change_buffer_max_size来动态设置。这个参数设置为50的时候,表示change buffer的大小最多只能占用buffer pool的50%。

4.change buffer使用场景

1.对于写多读少的业务来说,页面在写完以后马上被访问到的概率比较小,此时change buffer的使用效果最好。这种业务模型常见的就是账单类、日志类的系统。
反过来,假设一个业务的更新模式是写入之后马上会做查询,那么即使满足了条件,将更新先记录在change buffer,但之后由于马上要访问这个数据页,会立即触发merge过程。这样随机访问IO的次数不会减少,反而增加了change buffer的维护代价。所以,对于这种业务模式来说,change buffer反而起到了副作用。
2.普通索引和唯一索引的索引的选择(从内存的影响来看,业务优先)。

change buffer 和 redo log

case1: insert into t(id,k) values(id1,k1),(id2,k2);

我们假设当前k索引树的状态,查找到位置后,k1所在的数据页在内存(InnoDB buffer pool)中,k2所在的数据页不在内存中。如图所示是带change buffer的更新状态图。
在这里插入图片描述
分析这条更新语句,它涉及了四个部分:内存、redo log(ib_log_fileX)、 数据表空间(t.ibd)、系统表空间(ibdata1)。

这条更新语句做了如下的操作(按照图中的数字顺序):
1.Page 1在内存中,直接更新内存;
2.Page 2没有在内存中,在内存的change buffer区域,记录“我要往Page 2插入一行”这个信息
3.将上述两个动作记入redo log中(图中3和4)。
事务完成,写了两处内存,然后写了一处磁盘(两次操作合在一起写了一次磁盘),而且还是顺序写的。同时,图中的两个虚线箭头,是后台操作,不影响更新的响应时间。

case2: select * from t where k in (k1, k2)。
1.读Page 1的时候,直接从内存返回。
2.要读Page 2的时候,需要把Page 2从磁盘读入内存中,然后应用change buffer里面的操作日志,生成一个正确的版本并返回结果。
直到需要读Page 2的时候,这个数据页才会被读入内存。
redo log 主要节省的是随机写磁盘的IO消耗(转成顺序写),而change buffer主要节省的则是随机读磁盘的IO消耗。

什么情况会引发数据库的flush过程呢?

场景1:
InnoDB的redo log写满了。这时候系统会停止所有更新操作,把checkpoint往前推进,redo log留出空间可以继续写。checkpoint可不是随便往前修改一下位置就可以的。把checkpoint位置从CP推进到CP’,就需要将两个点之间的日志(浅绿色部分),对应的所有脏页都flush到磁盘上。之后,图中从write pos到CP’之间就是可以再写入的redo log的区域

性能影响:出现这种情况的时候,整个系统就不能再接受更新了,所有的更新都必须堵住。

场景2:
系统内存不足。需要新的内存页,淘汰掉旧的,如果淘汰是脏页,需要flush脏页到磁盘。

那么为什么不直接淘汰脏页,下次直接读磁盘呢?
为了数据库性能。

如果刷脏页一定会写盘,就保证了每个数据页有两种状态:
<1>内存里存在,内存里就肯定是正确的结果,直接返回;
<2>内存里没有数据,就可以肯定数据文件上是正确的结果,读入内存后返回。

缓冲池中的内存页有三种状态:
<1>还没有使用的;---->申请数据页
<2>使用了并且是干净页;---->直接释放复用
<3>使用了并且是脏页。----->脏页先刷到磁盘,变成干净页后才能复用。

刷脏页虽然是常态,但是出现以下这两种情况,都是会明显影响性能的:
(1)一个查询要淘汰的脏页个数太多,会导致查询的响应时间明显变长;
(2)日志写满,更新全部堵住,写性能跌为0

场景3:
Mysql后台线程自己刷新脏页。性能:无影响

场景4:
MySQL正常关闭的情况。MySQL会把内存的脏页都flush到磁盘上,这样下次MySQL启动的时候,就可以直接从磁盘上读数据,启动速度会很快。性能:无影响

刷页策略

MySQL刷页机制:刷新脏页时,边上也是脏页,也连带刷新掉。
MySQL 8.0 innodb_flush_neighbors参数的默认值是0,0:只刷自己 1:连带刷新

如果某次写入使用change buffer机制,之后主机异常重启,是否会丢失change buffer和数据?

虽然是只更新内存,但是在事务提交的时候,把change buffer的操作也记录到redo log里了,所以崩溃恢复的时候,change buffer也能找回来。

[资料来源]
1.Mysql实战45讲-丁奇

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值