MySQL卡顿背后的是什么?⭐️

2026-05-21 23:423阅读0评论建站教程
  • 内容介绍
  • 文章标签
  • 相关推荐

MySQL卡顿背后的是什么?⭐️

2024新年新气象, 小菜同学又踏上了求职之路,但求职路艰辛,新年第一次面试又被面试官给问住了,这事儿我可太有发言权了。

面试官:你有没有遇到过主要原因是持久化,把线程的查询、修改请求卡住的情况,百感交集。?

MySQL持久化不为人知的一面⭐️卡顿现象的根源与对策

小菜:持久化时写redo log的, 利用写redo log的顺序性来提升性能, 栓Q! 避免随机IO,所以呢不会卡住其他线程的请求的

面试官:好,那我们今天的面试就到这里吧

经历本次面试,小菜同学又重新整理缓冲池、持久化相关的知识点终于搞懂卡顿的根源和对策,捡漏。

缓冲池

礼貌吗? 缓冲池是一块内存区域,用于将磁盘中的页加载到内存,加快访问速度

当访问数据页时需要先判断页是否在缓冲池中,如果不在则需要从磁盘加载到缓冲池中

实际是通过Key:表空间 + 页号 Value:页 的方式建立散列表,达到O的查找速度

那如何判断某个页是否存在于缓冲池中呢?难道去遍历吗,我心态崩了。?

数据页被加载到缓冲池后称为缓存页, 每个缓存页对应一个控制块,控制块上记录数据页的相关信息

注意:链表管理控制块相当于管理对应的缓存页,上手。

LRU算法优化

踩个点。 为了避免扩容时重新分配内存,还要将数据从旧的空间迁移到新的空间,使用chunk进行扩容

将LRU链表分为冷热数据区,从磁盘加载的页先放到冷数据区,经过一段时间多次读取后再放入热数据区头部,如果在短时间内多次访问一页则不会放入热数据区,如果页就在热数据区头部附近则不会移动到头部

算法类型 描述 优点 缺点
LRU 最近最少使用算法,将最近使用的页放在链表头部,最少使用的放在末尾,移除末尾的页 实现简单,能较好地处理顺序访问模式的数据。 对于随机访问或扫描操作,可能导致缓存污染,降低命中率。
优化后的LRU - 将LRU链表分为冷、 热数据区; - 新加载的页先放入冷数据区,经过一段时间多次读取后再放入热数据区; - 短时间内大量访问同一页不会直接进入热数据区,以防止全表扫描或范围扫描导致缓存命中率下降; - 如果某页已经在热数据区的“附近”,则不将其移动到头部。 - 减少了因全表扫描或范围扫描导致的缓存污染,提高了缓存命中率; - 更好地适应了不同类型的工作负载。 - 实现相对复杂,需要维护冷、热数据的界限及相关策略。

MySQL持久化机制与卡顿原因分析⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️ 前文说过 当数据页遇到写操作变成脏页时需要写入磁盘进行持久化 如果对每一条记录都这么做,遇到一个写操作就写入磁盘,而且写回磁盘时由于页的无序此时会是随机IO,开销非常大 如果想要存一段时间,等该页的脏记录多了再一边刷盘性价比会高一些,但是如果该期间宕机了那岂不是会发生数据丢失? 为了防止数据丢失, 在宕机时能够进行数据恢复,使用redo log记录页中修改的数据并以顺序写入的方式进行IO 所以呢我们应该减低这种场景的发生,可以通过调整参数或升级磁盘等多方面实现 当发生这种场景时会暂停用户线程去进行刷盘操作从而造成阻塞 除了这种场景外还会有线程定时刷新、关闭前把脏页刷入磁盘等 redo log 记录数据页修改的数据,用于实现物理上的数据恢复,由于redo log对应的页刷盘后该redo log相当于无效,所以呢被设计成环形文件 在生成redo log时会将redo log写在redo log buffer缓冲池,由于每个事务可能对应多条redo log,redo log在缓冲池中是被交替写入的 innodb_log_file_size :规定每个redo log的大小; innodb_log_files_in_group :设置Redo Log文件的数量,多个文件串联形成环形文件。 通过这两个参数可以设置redo log文件的大小 lsn:标识写redo log娱乐位置 flushed_to_disk_lsn:标识redo log刷入磁盘娱乐位置 checkpoint_lsn:标识checkpoint推动到娱乐的位置 lsn与flushed_to_disk_lsn 之间的redo log是没有刷入磁盘的 flushed_to_disk_lsn与checkpoint_lsn之间的redo log是刷入磁盘的 将redo log刷入磁盘后等待后续线程将对应的脏页刷入磁盘后该redo log就可以被覆盖了 后台线程会定期checkpoint推动可覆盖redo log的标记,每次进行checkpoint更新checkpoint_lsn的位置 写操作太多,很多页没有刷盘,导致redo log占满,此时触发checkpoint将脏页刷入磁盘,空出可覆盖的redo log 崩溃回复时的判断: innodb_flush_log_at_trx_commit=0 , 提交事务时不强制刷新Redo Log到磁盘, 而是由后台线程以固定的频率进行刷新.性能最好, 但最不平安. 在系统宕机时会丢失部分事务. **`innodb_flush_log_at_trx_commit=1`**, 提交事务时马上刷新Redo Log到磁盘, 这是最平安的做法, 但也是性能最差的做法. 主要原因是涉及到一次fsync操作. **`innodb_flush_log_at_trx_commit=2`**, 提交事务时将Redo Log写入操作系统的Page Cache. 相比于第一种做法, 这种做法在发生MySQL宕机但操作系统未宕机的情况下不会丢失事务. 使用XA事务的两阶段提交: binlog 是MySQL逻辑上的数据恢复日志,在redo log进行刷盘时为了保证数据一致性,binlog 与 redo Log 基于XA协议使用两阶段提交. 如果先写完binlog 宕机没写 redo Log,那么主机就无法通过 redo Log恢复数据,从而导致 数据不一致. 如果先写完 redo Log宕机没写 binlog ,那么主机会通过 redo Log恢复 数据,而从 机需要 通过 binlog恢复 数据,此时 binlog不存在就会导致 数据不一致. 可以使用 `sync_binlog` 控制binlog刷盘时机,类似 redo Log 的 `innodb_flush_log_at_trx_commit` 本篇文章从MySQL 的缓冲池开始聊起聊聊Innodb 中是如何一步步实现持久化的~ 有什么问题可以在评论区交流,如果觉得菜菜写的不错,可以点赞、关注、收藏支持一下~ 关注菜菜,分享更多干货,公众号:菜菜的后端私房菜

MySQL卡顿背后的是什么?⭐️

2024新年新气象, 小菜同学又踏上了求职之路,但求职路艰辛,新年第一次面试又被面试官给问住了,这事儿我可太有发言权了。

面试官:你有没有遇到过主要原因是持久化,把线程的查询、修改请求卡住的情况,百感交集。?

MySQL持久化不为人知的一面⭐️卡顿现象的根源与对策

小菜:持久化时写redo log的, 利用写redo log的顺序性来提升性能, 栓Q! 避免随机IO,所以呢不会卡住其他线程的请求的

面试官:好,那我们今天的面试就到这里吧

经历本次面试,小菜同学又重新整理缓冲池、持久化相关的知识点终于搞懂卡顿的根源和对策,捡漏。

缓冲池

礼貌吗? 缓冲池是一块内存区域,用于将磁盘中的页加载到内存,加快访问速度

当访问数据页时需要先判断页是否在缓冲池中,如果不在则需要从磁盘加载到缓冲池中

实际是通过Key:表空间 + 页号 Value:页 的方式建立散列表,达到O的查找速度

那如何判断某个页是否存在于缓冲池中呢?难道去遍历吗,我心态崩了。?

数据页被加载到缓冲池后称为缓存页, 每个缓存页对应一个控制块,控制块上记录数据页的相关信息

注意:链表管理控制块相当于管理对应的缓存页,上手。

LRU算法优化

踩个点。 为了避免扩容时重新分配内存,还要将数据从旧的空间迁移到新的空间,使用chunk进行扩容

将LRU链表分为冷热数据区,从磁盘加载的页先放到冷数据区,经过一段时间多次读取后再放入热数据区头部,如果在短时间内多次访问一页则不会放入热数据区,如果页就在热数据区头部附近则不会移动到头部

算法类型 描述 优点 缺点
LRU 最近最少使用算法,将最近使用的页放在链表头部,最少使用的放在末尾,移除末尾的页 实现简单,能较好地处理顺序访问模式的数据。 对于随机访问或扫描操作,可能导致缓存污染,降低命中率。
优化后的LRU - 将LRU链表分为冷、 热数据区; - 新加载的页先放入冷数据区,经过一段时间多次读取后再放入热数据区; - 短时间内大量访问同一页不会直接进入热数据区,以防止全表扫描或范围扫描导致缓存命中率下降; - 如果某页已经在热数据区的“附近”,则不将其移动到头部。 - 减少了因全表扫描或范围扫描导致的缓存污染,提高了缓存命中率; - 更好地适应了不同类型的工作负载。 - 实现相对复杂,需要维护冷、热数据的界限及相关策略。

MySQL持久化机制与卡顿原因分析⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️ 前文说过 当数据页遇到写操作变成脏页时需要写入磁盘进行持久化 如果对每一条记录都这么做,遇到一个写操作就写入磁盘,而且写回磁盘时由于页的无序此时会是随机IO,开销非常大 如果想要存一段时间,等该页的脏记录多了再一边刷盘性价比会高一些,但是如果该期间宕机了那岂不是会发生数据丢失? 为了防止数据丢失, 在宕机时能够进行数据恢复,使用redo log记录页中修改的数据并以顺序写入的方式进行IO 所以呢我们应该减低这种场景的发生,可以通过调整参数或升级磁盘等多方面实现 当发生这种场景时会暂停用户线程去进行刷盘操作从而造成阻塞 除了这种场景外还会有线程定时刷新、关闭前把脏页刷入磁盘等 redo log 记录数据页修改的数据,用于实现物理上的数据恢复,由于redo log对应的页刷盘后该redo log相当于无效,所以呢被设计成环形文件 在生成redo log时会将redo log写在redo log buffer缓冲池,由于每个事务可能对应多条redo log,redo log在缓冲池中是被交替写入的 innodb_log_file_size :规定每个redo log的大小; innodb_log_files_in_group :设置Redo Log文件的数量,多个文件串联形成环形文件。 通过这两个参数可以设置redo log文件的大小 lsn:标识写redo log娱乐位置 flushed_to_disk_lsn:标识redo log刷入磁盘娱乐位置 checkpoint_lsn:标识checkpoint推动到娱乐的位置 lsn与flushed_to_disk_lsn 之间的redo log是没有刷入磁盘的 flushed_to_disk_lsn与checkpoint_lsn之间的redo log是刷入磁盘的 将redo log刷入磁盘后等待后续线程将对应的脏页刷入磁盘后该redo log就可以被覆盖了 后台线程会定期checkpoint推动可覆盖redo log的标记,每次进行checkpoint更新checkpoint_lsn的位置 写操作太多,很多页没有刷盘,导致redo log占满,此时触发checkpoint将脏页刷入磁盘,空出可覆盖的redo log 崩溃回复时的判断: innodb_flush_log_at_trx_commit=0 , 提交事务时不强制刷新Redo Log到磁盘, 而是由后台线程以固定的频率进行刷新.性能最好, 但最不平安. 在系统宕机时会丢失部分事务. **`innodb_flush_log_at_trx_commit=1`**, 提交事务时马上刷新Redo Log到磁盘, 这是最平安的做法, 但也是性能最差的做法. 主要原因是涉及到一次fsync操作. **`innodb_flush_log_at_trx_commit=2`**, 提交事务时将Redo Log写入操作系统的Page Cache. 相比于第一种做法, 这种做法在发生MySQL宕机但操作系统未宕机的情况下不会丢失事务. 使用XA事务的两阶段提交: binlog 是MySQL逻辑上的数据恢复日志,在redo log进行刷盘时为了保证数据一致性,binlog 与 redo Log 基于XA协议使用两阶段提交. 如果先写完binlog 宕机没写 redo Log,那么主机就无法通过 redo Log恢复数据,从而导致 数据不一致. 如果先写完 redo Log宕机没写 binlog ,那么主机会通过 redo Log恢复 数据,而从 机需要 通过 binlog恢复 数据,此时 binlog不存在就会导致 数据不一致. 可以使用 `sync_binlog` 控制binlog刷盘时机,类似 redo Log 的 `innodb_flush_log_at_trx_commit` 本篇文章从MySQL 的缓冲池开始聊起聊聊Innodb 中是如何一步步实现持久化的~ 有什么问题可以在评论区交流,如果觉得菜菜写的不错,可以点赞、关注、收藏支持一下~ 关注菜菜,分享更多干货,公众号:菜菜的后端私房菜