超级苦工
阅读 28
LRU算法及其优化策略——Mysql篇

LRU算法及其优化策略——Mysql篇.jpg

上一篇文章中,介绍了LRU算法在Redis之中的应用,本篇继续给各位道友介绍在Mysql的InnobDB引擎中,是如何使用LRU算法的。

InnoDB缓冲池

缓存池简介及内存结构

首先来介绍下InnoDB的缓冲池,缓冲池简单来说就是一块内存区域,该区域内缓存着InnoDB访问存储在磁盘的数据和索引信息。缓冲池有两个作用,一是提高了大容量读取操作的效率,二是提高了缓存管理的效率。调配缓存池参数,使得经常访问的参数能够保留在缓存池中是一个很重要的Mysql优化手段。

一个InnoDB缓存池的内存结构图如下图所示:

缓冲池.png

图源自《Mysql技术内幕:InnoDB存储引擎》

缓存池状态

我们可以通过SHOW ENGINE INNODB STATUS命令来查看缓存池在InnoDB引擎中的表现:

----------------------
BUFFER POOL AND MEMORY
----------------------
Total memory allocated 6593445888;                       // 为缓冲池分配的总内存(字节)
Dictionary memory allocated 7687783                      // 为InnoDB数据字典分配的总内存(字节)
Buffer pool size   393208                                // 分配给缓冲池的页面总大小(页)
Free buffers       352642                                // 缓冲池空闲列表的页面总大小(页)
Database pages     40485                                 // 缓冲池LRU列表的页面总大小。(页)
Old database pages 14967                                 // 缓冲池旧LRU子列表的页面总大小(页)
Modified db pages  4                                     // 缓冲池中当前修改的页面数。
Pending reads 0                                          // 等待读入缓冲池的缓冲池页面数。
Pending writes: LRU 0, flush list 0, single page 0       // 从LRU列表的底部开始写入的缓冲池中的旧脏页数。                                                          // 检查点期间要刷新的缓冲池页面数。
                                                         // 缓冲池中暂挂的独立页面写入数。
Pages made young 5, not young 0                          // 缓冲池LRU列表中变年轻的页面总数
                                                         // 缓冲池LRU列表中未设置为年轻的页面总数
...
复制代码

完整的缓存池状态信息可以在这里找到:缓存池状态信息

缓存池的数量和大小

为了避免多个线程读写缓存池引起的并发冲突,InnoDB可以配置多个缓存池,由参数innodb_buffer_pool_instances指定,内部使用散列表进行分配和管理。

通常来说,当缓存池的大小越大,则Mysql表现的越像一个内存数据库。我们可以在启动时或者运行时通过innodb_buffer_pool_size参数动态地调整缓存池的大小,需要注意的innodb_buffer_pool_size的大小会自动的调整为InnoDB缓存池块innodb-buffer-pool-chunk-size(默认为128M)的整倍数。

为避免潜在的性能问题,缓存池大小/缓存池块大小(innodb_buffer_pool_size/ innodb_buffer_pool_chunk_size)的数量不应超过1000。

缓存池的刷新

说到缓存,必须有缓存刷新机制,即剔除缓存中的脏页(已经被修改,但是并未刷入磁盘中的数据页)。

在5.7以上的版本中,InnoDB会启动默认四个线程并发的来执行缓存池中脏页的清除。脏页的清除有两种模式:

  1. 普通模式,当缓存池中的脏页比例超过innodb_max_dirty_pages_pct_lwm(低水平线默认为25%)时,启动普通模式将脏页刷新到磁盘中。
  2. aggressively flushes(激进模式?),当缓存池中的脏页比例超过innodb_max_dirty_pages_pct(默认为75%)时,启动更快的刷新模式,尽快的将脏页刷新到磁盘当中。

缓存池的预读(Prefetching )

InnoDB的缓存池不仅是被动地缓存,而且会异步地预先从磁盘中读取数据页,有两种方式:

  • 线性:根据缓存池的访问数据的顺序来预读,当读取某一区(Extend)中的页(Page)的数据超过innodb_read_ahead_threshold时,则将该区中剩余的所有页都加载到缓存池中。

  • 随机:根据缓存池中的已有页面进行预读,而不管他们的顺序,当发现缓存池中某一区内页的数量超过了innodb_random_read_ahead,则将改区中剩余的所有页都加载到缓存池中。

缓存池LRU算法

在了解了InnoDB的缓存池概念后,我们来看看背后支持缓存池工作的算法。

当我们使用朴素的LRU算法时,会发现如果有批量的操作时,会打乱缓存数据,大大降低了缓存命中率。而在Mysql当中会有大量的预读及全表扫描的操作,为了使得真真的热数据留在内存中,InnoDB缓存池采用了一种变种的LRU算法,有些像我在这篇文章中写到的LRU-K算法。

新进入缓存池的页并不会直接进入LRU链表的头部,而是插入到距离链表尾3/8的位置(可以由innodb_old_blocks_pct参数进行配置),我们将距离链表尾3/8以上的位置称为新子列表,以下的位置称为旧子列表,数据在链表中自底而上称为变年轻,反之称为变老。下图是一个示意图:

innodb-buffer-pool-list.png

  • 变年轻

    变年轻分为两种情况,第一种是来源于用户的操作而需要读取页面,此时会直接使该页直接移至新子列表链表头部。第二种是来源于数据库内部的预读操作,则在距离插入innodb_old_blocks_time(默认为1000ms)的时间内,即使访问了该页,该页也不会别移到LRU链表的头部。

    也就是说,如果是来源于用户的操作,则最起码需要两次操作才能变年轻。而如果是预读操作,则需要加上一个等待期限。

  • 变老

    随着链表数据的替换和访问,整个列表中的数据会自然的变老。最终最老的页面会从尾部逐出。

总结

本文介绍了Mysql的InnoDB引擎的缓存池的概念,及其对于LRU算法的改造。介绍了另一种解决LRU列表被污染的解决方案。

关注下面的标签,发现更多相似文章
评论
相关推荐
Android 使用 HTTPS

来源: 简书 原文: Android 使用 HTTPS 如果你的项目的网络框架是okhttp,那么使用https还是挺简单的,因为okhttp默认支持HTTPS。传送门 Android 使用 HTTP...

Android 适配一篇就够 - 编译版本?support?API 兼容?图片适配?

来源: 简书 原文: Android 适配一篇就够 - 编译版本?support?API 兼容?图片适配? 本文介绍 Android 不同系统及图片资源的常见适配问题。 compileSdkVersi...

[Kotlin Tutorials 11] Kotlin和Java的双向互操作

来源: 简书 原文: [Kotlin Tutorials 11] Kotlin和Java的双向互操作 Kotlin和Java的双向互操作 Kotlin和Java是有互操作性的(Interoperabi...

Android跳转到获取应用通知权限

来源: 简书 原文: Android跳转到获取应用通知权限 1.判断是否有通知权限 官方只最低支持到API 19(4.4),低于19的只会返回true,目前暂时没有办法获取19以下的系统是否开启了某个...

Android--PathMeasure基本用法

来源: 简书 原文: Android--PathMeasure基本用法 PathMeasure是一个用来测量Path的类 构造方法 //创建一个空的PathMeasure public PathMea...

Android屏幕适配的总结

来源: 简书 原文: Android屏幕适配的总结 屏幕适配的核心:其一,就是适配的效率,即把设计图转化为App界面的过程是否高效,其二如何保证实现UI界面在不同尺寸和分辨率的手机中UI的一致性。 背...

Android热修复之-Frameworks层修复原理分析

来源: 简书 原文: Android热修复之-Frameworks层修复原理分析 说到热修复主要有两种修复方案一种是通过dex替换的方式来达到修复效果、一种是基本native层的修复。dex替换的方式...

[译文]MongoDB WiredTiger引擎调优技巧

来源: 简书 原文: [译文]MongoDB WiredTiger引擎调优技巧 MongoDB从3.0开始引入可插拔存储引擎的概念。当前,有不少存储引擎可供选择:MMAPV1、WiredTiger、M...

知道了这些 MongoDB设计技巧,提升效率50%

来源: 掘金 原文: 知道了这些 MongoDB设计技巧,提升效率50% 范式化设计还是反范式 考虑下这样的场景,我们的订单数据是这样的 商品: { "_id": productI...

Mongo实时聚合千万文档数据

来源: 掘金 原文: Mongo实时聚合千万文档数据 1.前言 大数据的聚合分析在企业中非常有用,有过大数据开发经验的人都知道ES、Mongo都提供了专门的聚合方案来解决这个问题。但是大量数据的实时聚...

历时七天,史上最强MySQL优化总结,从此优化So Easy!

来源: 掘金 原文: 历时七天,史上最强MySQL优化总结,从此优化So Easy! 一、概述 1. 为什么要优化 一个应用吞吐量瓶颈往往出现在数据库的处理速度上 随着应用程序的使用,数据库数据逐渐增...

Mysql 百问系列:B+Tree 到底是什么

来源: 掘金 原文: Mysql 百问系列:B+Tree 到底是什么 前言: 以前看过许多关于B+ Tree的文章,当时看了总觉得明白了,可是没过多久就又要忘了。直到我看了掘金小册:Mysql是怎么运...

LRU算法及其优化策略——Mysql篇

来源: 掘金 原文: LRU算法及其优化策略——Mysql篇 在上一篇文章中,介绍了LRU算法在Redis之中的应用,本篇继续给各位道友介绍在Mysql的InnobDB引擎中,是如何使用LRU算法的。...

mysql order by 优化

来源: 掘金 原文: mysql order by 优化 version : 5.7, from 8.2.1.14 ORDER BY Optimization 本节描述MySQL何时可以使用索引来满足...

MYSQL-多表查询

来源: 掘金 原文: MYSQL-多表查询 首先创建数据表tb_departments create table tb_departments (dept_id INT PRIMARY KEY, de...

MySQL索引和SQL调优

来源: 掘金 原文: MySQL索引和SQL调优 [TOC] MySQL索引和SQL调优 本文有参考网上其他相关文章,本文最后有附参考的链接 MySQL索引 MySQL支持诸多存储引擎,而各种存储引擎...

iOS 底层拾遗:AutoreleasePool

来源: 掘金 原文: iOS 底层拾遗:AutoreleasePool 前言 在阳神的 黑幕背后的Autorelease 文章中已经把 AutoreleasePool 核心逻辑讲明白了,不过多是结论性...

iOS app秒开H5优化探索

来源: 掘金 原文: iOS app秒开H5优化探索 背景 为了快递迭代、更新,公司app有一大模块功能使用H5实现,但是体验比原生差,这就衍生了如何提高H5加载速度,优化体验的问题。此文,记录一下自...

iOS常用宏 定义

来源: 简书 原文: iOS常用宏 定义 iOS开发过程中,使用的一些常用宏定义 字符串是否为空 #define kStringIsEmpty(str) ([str isKindOfClass:[NS...

比较一下iOS中的三种定时器

来源: 掘金 原文: 比较一下iOS中的三种定时器 NSTimer NSTimer是iOS开发中的最常见的定时器。 Timers work in conjunction with run loops....