数据库内存与Buffer Pool

news2025/2/2 12:53:13

数据库内存与Buffer Pool

文章目录

  • 数据库内存与Buffer Pool
    • 一:MySQL内存结构
      • 1:MySQL工作组件
      • 2:工作线程的本地内存
      • 3:共享内存区域
      • 4:存储引擎缓冲区
    • 二:InnoDB的核心:Buffer Pool
      • 1:数据页(Data Page)
      • 2:索引缓冲页(Index Page)
      • 3:锁空间(Lock Space)
      • 4:数据字典(Dict Info)
      • 5:日志缓冲区(log buffer)
      • 6:自适应哈希索引(Adaptivity Hash)
      • 7:写缓冲区(Change buffer)
    • 三:InnoDB缓冲池内存是如何管理的
      • 1:什么是缓冲页的控制块
      • 2:空闲页的管理
      • 3:标记页的管理
      • 4:淘汰机制(重点)
      • 5:末位淘汰的问题
        • 5.1:预读失效问题(young, old解决)
        • 5.2:缓冲池污染问题(young区晋升解决)

一:MySQL内存结构

在这里插入图片描述

JVM & MySQL

实际上MySQL内存模型和JVM类似,JVM内存主要会划分为线程共享区和线程私有区

上图中的MySQL内存区域,左边则是线程私有区域,每条工作线程中都会分配的区域,各线程之间互不影响,而右边的三大板块,则属于线程共享区域,即所有线程都可访问的内存。

然而,MySQL线程共享区这块也会细分,右边上面的两个板块,都属于MySQL-Server层使用的内存,也就意味着这两块内存是所有引擎都共享的区域

而最下面这个区域,每个存储引擎都不相同,也就是InnoDB会构建自己的Buffer缓冲区,MyISAM也会构建自己的缓冲区

1:MySQL工作组件

这个比较容易理解,也就是对应着MySQL架构图中的组件层
在这里插入图片描述
因为后续客户端连接时,都需要经过一系列的连接工作,处理SQL时也需要经过一系列的解析、验证、优化工作

所以MySQL会在启动时,会先将这些工作组件初始化到内存中,方便后续处理客户端的操作。

数据库的连接池中,存的到底是什么?

存的实际上就是数据库连接对象,MySQL内部的连接对象,其中包含了客户端连接信息,如客户端IP、登录的用户、所连接的DB…等这类信息

同时这些连接对象在内部会绑定一条工作线程,因此你也可以将它理解成是一个线程池!

MySQL复用连接的本质,实则是在复用线程,出现一个新的客户端连接时,首先会根据客户端信息为其创建连接对象,然后再复用连接池中的空闲线程。

2:工作线程的本地内存

工作线程的本地内存区域,也被称之为线程私有区,即MySQL在创建每条线程时,都会为其分配这些内存:
在这里插入图片描述
可以看到,在工作线程的本地内存中,除开最基本的线程堆栈外,MySQL还往内部“塞了”一堆东西

这些东西在不同的SQL运行时,都有各自的作用,但基本上是为了更好的保存临时数据而设计的。

为啥不是直接在共享内存中搞一块大的空间,然后提供给所有线程来操作呢?

因为这些数据本身就是一条线程在执行SQL时产生的临时数据,其他线程压根不会去用到另一条线程的临时数据,所以这些临时数据没有必要被共享。

除此之外,将这些缓冲区都放在线程本地内存中,还有一点最大的好处:能够提升多线程并发执行的性能!

不过也并非所有数据都适合放在线程的本地内存中,有一些多条线程之间都会访问的数据,如果再放到本地内存中,就会造成很大的冗余性,比如典型的索引根节点数据,每条线程都有可能会通过索引查询数据,因此每条线程都“缓存”一份放在自己的内存中,就会占用大量的内存空间,这样反而弊大于利。

3:共享内存区域

在这里插入图片描述

这里解释下文件描述符:File Descriptor

比如我现在想要操作users表的数据,那首先得找到这张表,但表的位置可能分布在磁盘的任何一处,总不能触发磁盘IO把整个磁盘检索一遍

所以内存中直接设计了一个缓存区,专门缓存这些表数据文件的磁盘位置

要对某张表进行操作时,直接去文件描述符缓存中找,然后根据其中记录的地址,去磁盘中固定的位置上操作表数据。

表结构的文件描述符缓存,作用也是相同的,直接根据内存中的文件描述符,去操作磁盘中对应位置的表结构文件。
在这里插入图片描述

MySQL8.x为什么移除了Query Cache

Query Cache的核心工作原理是对于一些频繁执行的查询SQL,直接将结果缓存在内存中,之后再次来查询相同数据时,就无需走磁盘,而是直接从查询缓存中获取数据并返回。看似使用,其实很鸡肋,原因如下:

  1. 缓存命中率低:几乎大部分SQL都无法从查询缓存中获得数据。
  2. 占用内存高:将大量查询结果放入到内存中,会占用至少几百MB的内存。
  3. 增加查询步骤:查询表之前会先查一次缓存,查询后会将结果放入缓存,额外多几步开销。
  4. 缓存维护成本不小,需要LRU算法淘汰缓存,同时每次更新、插入、删除数据时,都要清空缓存中对应的数据。
  5. InnoDB引擎构建出的缓冲区中,也会类似的功能,因为与查询缓存也存在冲突。
  6. 项目中一般都会使用Redis先做业务缓存,因此能来到MySQL的查询语句,几乎都是要从表中读数据的,所以查询缓存的地位就显得更加突兀

4:存储引擎缓冲区

几乎任何存储引擎都会在启动时,向操作系统申请一块内存,用来作为缓冲区

每个引擎的缓冲区也并不相同,但有一点是共通的:即所有引擎的缓冲区,对于MySQL的工作线程而言,都是一块共享的内存区域

为何各大存储引擎都会设计一个缓冲池?

虽然MySQL是基于磁盘存储数据的,但总不能每次读写操作都走磁盘

这样绝对会导致资源开销极大,同时性能也极低,因此各引擎都在内存中设计了一个缓冲池,用来提升数据库整体的读写性能。

🎉 而InnoDB引擎,是尤为特殊的存在,几乎将所有的操作都放在了内存中完成,这也是InnoDB能取代MyISAM作为默认引擎的原因之一

二:InnoDB的核心:Buffer Pool

MySQL众多存储引擎中,应用最为广泛的是InnoDB,Buffer-Pool中主要有下面的内容:
在这里插入图片描述

Buffer Pool到底会占用多大内存呢?

show global variables like "%innodb_buffer_pool_size%";

在这里插入图片描述

MySQL5.6以后的版本中,默认大小为128MB,这块内存是MySQL启动时向OS申请的一块连续空间。

当然,也可以手动调整innodb_buffer_pool_size参数来控制,一般建议设置为机器内存的60~80%。

1:数据页(Data Page)

主要用来缓冲磁盘的表数据,将写操作转移到内存进行。

InnoDB引擎为了方便读取,会将磁盘中的数据划分为一个个的「页」,每个页的默认大小为16KB,以页作为内存和磁盘交互的基本单位

而InnoDB的缓冲池也会以页作为单位,也就意味着:当InnoDB拿到申请的连续内存后,会按照16KB的尺寸将整块空间,划分成一个个的缓冲页。

在MySQL运行之初,这些划分出的缓冲页,都属于空闲页,也就是未使用的内存

随着运行时长的慢慢增长,会将磁盘中的数据页,一点点的载入内存当中

因为磁盘中的表数据是以16KB作为单位划分的,而内存中的缓冲页也是这个大小,因此发生一次磁盘IO读到的数据(读一页磁盘数据),会放入到一个缓冲页中存储,而这些承载磁盘数据的缓冲页,就被称之为数据页

在这里插入图片描述
当磁盘中的数据被载入到内存之后,带来的优势会极为明显:

  • 读数据时:如果在数据页中有,则直接会从内存中读取数据并返回,没有再去磁盘检索数据。
  • 写数据时:会先修改数据页的数据,修改后会标记相应的数据页,然后直接返回,再由后台线程去完成数据的落盘工作。

MySQL会将哪些表数据放到缓冲池中呢?

其实刚启动时里面并不会有数据,而是随着业务SQL的执行,一点点将磁盘中的数据加载进内存的

比如执行一条查询语句,因为最初内存中并没有加载数据页,因此会走磁盘检索数据,检索数据的过程中,不管此次IO读到的数据是不是目标数据,都会将它们放在内存中,而不是直接回收。这样做的好处是方便后续其他SQL要操作对应数据时,可以直接在内存中读到数据。

在条件允许,即内存充足的情况下,InnoDB会试图将磁盘中的所有表数据全部载入内存。

不过一般的机器,磁盘空间都会比内存要大出很多倍,所以当表数据较大时,也不可能无限制的载入

因而InnoDB会有一套完善的内存管理与淘汰机制,以此防止内存溢出风险

2:索引缓冲页(Index Page)

上面提到InnoDB会将部分乃至所有表数据载入内存,以此达到提升性能的目的,但不可能无限制载入

比如现在机器的内存为16GB,但磁盘中有30GB表数据,这显然无法放入进内存,所以无可避免的一点:在运行过程中,MySQL会走磁盘读数据。

比如一条查询语句要读的数据,在内存中没有相关的缓冲数据页,因此需要触发磁盘IO检索数据

但此时这条SQL可以命中索引,那会通过索引去查找数据,但问题是索引的根节点可能位于磁盘的任意位置

所以InnoDB也会有对应的优化机制,即内存中也会缓冲索引页。

在MySQL启动时,就会将当前库中所有已存在的索引,其根节点放入到内存缓冲区中,因为索引的根节点只有16KB,因此就算目前库中就算创建了1000个索引,所有索引的根节点加起来占用的内存空间,也不过才15MB左右。

将索引的根节点载入内存后,对于需要走索引查询的SQL,就会直接以相应的索引根节点为起始,然后去走索引查找数据

这样就避免了全盘查找索引根节点的这步操作。

Buffer Pool中有一块专门的区域:Index Page,专门用来存放载入的索引数据,存储这些数据的缓冲页,则被称之为索引页。

随着运行时间的增长,也会将一些非根节点的索引页载入内存中,这是一种对于访问频率较高的索引页,专门推出的优化机制。

3:锁空间(Lock Space)

每个事务会生成自己的锁结构,而这些锁结构也同样需要空间来存储,而锁空间就是专门用来存储锁结构的一块内存区域。

但锁空间也不仅仅只会存储锁结构,还会存储一些并发事务的链表,例如死锁检测时需要的「事务等待链表、锁的信息链表」等。

锁空间一般都是有大小限制的,当锁空间内存不足时,就会导致行锁粗化成表锁,以此来减少锁结构的数量,释放一定程度上的内存,但此时并发冲突就会变高!

4:数据字典(Dict Info)

为啥我们可以通过SQL语句查询到库中的表信息、查询一张表的索引、约束等信息呢?

-- 查询当前库中的所有表
show tables;

-- 查询一张表的全部索引
show index from tableName;

这些语句执行后都能查询出对应的信息,但这些信息咋来的呢?

这首先跟MySQL的系统表有关,在InnoDB引擎中主要存在SYS_TABLES、SYS_COLUMNS、SYS_INDEXES、SYS_FIELDS这四张系统表

主要是用来维护用户定义的所有表的各种信息,如下:

  • SYS_TABLES:这张表中会存储所有引擎为InnoDB的表信息。
    • ID:一张表的ID号。
    • NAME:一张表的名称。
    • N_COLS:一张表的字段数量。
    • TYPE:一张表所使用的存储引擎、编码格式、压缩算法、排序规则等。
    • SPACE:一张表所位于的表空间。
  • SYS_COLUMNS:这张表用来存储所有用户定义的表字段信息。
    • TABLE_ID:表示一个字段属于那张表。
    • POS:一个字段在一张表中属于第几列。
    • NAME:一个字段的名称。
    • MTYPE:一个字段的数据类型。
    • PRTYPE:一个字段的精度值。
    • LEN:一个字段的存储长度限制。
  • SYS_INDEXES:这张表用来存储所有InnoDB引擎表的索引信息。
    • TABLE_ID:表示这个索引属于哪张表。
    • ID:一个索引的ID号。
    • NAME:一个索引的名称。
    • N_FIELDS:一个索引由几个字段组成。
    • TYPE:一个索引的类型,如唯一、联合、全文、主键索引等。
    • SPACE:一个索引的数据所位于的表空间位置。
    • PAGE_NO:这个索引对应的B+Tree根节点位置。
  • SYS_FIELDS:这张表用来存储所有索引的定义信息。
    • INDEX_ID:当前这个索引字段属于哪个索引。
    • POS:当前这个索引字段,位于索引的第几列。
    • COL_NAME:当前索引字段的名称。

这四张表也被称为InnoDB的内部表,这四张表在载入内存前,位于.ibdata文件中

在MySQL启动时会开始加载,载入内存后就会放入到Dict Info这块区域,当利用show语句查询表的结构信息时,就会在字典信息中检索数据。

5:日志缓冲区(log buffer)

InnoDB的缓冲池中,主要存在两个日志缓冲区,即undo_log_buffer、redo_log_buffer,分别对应着撤销日志和重做日志

它俩的作用主要是用来提升日志记录的写入速度,因为日志文件在磁盘中,执行SQL时直接往磁盘写日志,其效率太低了

因此会先写缓冲区,再由后台线程去刷写日志。

6:自适应哈希索引(Adaptivity Hash)

这种技术可以算的上是一种AI技术

哈希算法查找数据的效率非常高,在没有哈希冲突的情况下复杂度为O(1),而B+Tree检索数据的效率,取决于树的高度。

建立索引时,只能选用一种数据结构来作为索引的底层结构:

  • 如果选择哈希结构,虽然效率高,但数据是无序的,因此不方便做排序查询。
  • 如果选择B+Tree结构,虽然有序,但查询的效率会受到树高的影响。

两种结构各有优劣,但一般为了满足业务按序查询的需求,所以会折中选择B+Tree结构,虽然没有哈希索引那么快,但速度也还可以。

而正是由于此原因,InnoDB创始人在研发时,就实现了一种名为自适应哈希索引的技术

在MySQL运行过程中,InnoDB引擎会对表上的索引做监控,如果某些数据经常走索引查询,那InnoDB就会为其建立一个哈希索引,以此来提升数据检索的效率,并且减少走B+Tree带来的开销

由于这种哈希索引是运行过程中,InnoDB根据B+Tree的索引查询次数来建立的,因此被称之为自适应哈希索引。

自适应哈希索引和普通哈希索引的区别在哪儿呢?

普通哈希索引是在创建索引时将结构声明为Hash结构,这种索引会以索引字段的整表数据建立哈希,而自适应哈希索引是根据缓冲池的B+树构造而来,只会基于热点数据构建,因此建立的速度会非常快,毕竟无需对整表都建立哈希索引。

自适应哈希索引在InnoDB中是默认开启的,可以通过手动调整innodb_adaptive_hash_index参数来控制关闭,但一般尽量不要去关闭它,因为该技术能让MySQL的整体性能翻倍。

在MySQL8.0以下的版本中,如果同时删除一张大表的很多数据,有可能会因为自适应哈希索引的原因,造成线上MySQL出现抖动,不过该问题在MySQL8.x版本中已经被修复,但如若你的MySQL版本在此之下,那尽量不要在业务高峰期删除大量数据。

对于自适应哈希索引的使用情况,可以通过show engine innodb status \G;命令查看

但哈希索引由于自身特性的原因,因此也仅只能用于等值查询的场景,无法支持排序、范围查询。

在这里插入图片描述

7:写缓冲区(Change buffer)

如果要变更的数据页在缓冲区中存在,则会直接修改缓冲区中的数据页,然后标记一下变更过的数据页

但如果要操作的数据页并未被加载到缓冲区,那依旧会走磁盘去操作数据

走磁盘显然会影响性能,因此InnoDB就创造了一个「写入缓冲」

以insert语句为例,不管在MySQL的任何版本中,执行一条插入语句之前,因为这条数据在磁盘中都不存在,因此缓冲区中自然也不可能会有对应的数据页

而「写入缓冲」出现的原因,就是为了解决此问题,当一条写入语句执行时,流程如下:

  1. 判断要变更的数据页是否被载入到内存。
  2. 如果内存中有对应的数据页,则直接变更缓冲区中的数据页,完成标记后则直接返回。
  3. 如果内存中没有对应的数据页,则将要变更的数据放入到「写入缓冲」中,然后返回。

此时会发现,不管内存中是否存在相应的数据页,InnoDB都不会走磁盘写数据,而是直接在内存中完成所有操作

⚠️ 并不是所有的写入动作,都可以在内存中完成,「写入缓冲」是有限制的:插入的数据字段不能具备唯一约束或唯一索引

因为如果存在唯一字段的表,在插入数据前必须要先判断表中是否存在相同值

一张表的数据不可能全部都载入数据,所以这个判断重复值的工作必须依赖磁盘中的表数据来完成,所以插入具备唯一性的数据时,就必须要走磁盘。

那如果我的表上有一个主键id岂不是一定要走磁盘了?

其实并不一定,如果表的主键声明了是一个自增ID,那这个自增序列会由MySQL-Server自己来维护,因此ID会由MySQL来生成,是绝对不会出现重复值的。

因此对于这种情况,会将要插入的数据放到「写入缓冲区」中。

如果表中存在唯一索引、或者表的主键未声明是自增ID,难道插入数据时就不会用到这个「写入缓冲区」吗?

其实也会用,前面说过,一条插入语句的执行过程如下:

  1. 先向聚簇索引中,插入一条相应的行记录(数据)。
  2. 对于非聚簇索引,都插入一个新的索引键,并将值指向聚簇索引中插入的主键值。

所以插入数据时还需额外维护表中的次级索引,会为插入的新数据构建次级索引的索引键,并且将索引键插入到次级索引树当中

这个过程就会用到「写入缓冲区」。

因为首先需要走一次磁盘,先插入行记录,插入完成后,假设表中存在三个非聚簇索引(次级索引),都会将要插入的索引键放在「写入缓冲区」。

「写入缓冲区」中的数据究竟啥时候会真正写入到磁盘呢?

  • 当一条SQL需要用到对应的索引键查询数据时,会触发后台线程执行刷盘工作。
  • 当「写入缓冲区」内存空间不足时,会触发后台线程执行刷盘工作。
  • 当距离上一次刷盘的时间,间隔达到一定程度(默认10s),会触发后台线程执行刷盘工作。
  • MySQL-Server正在关闭时,也会触发后台线程执行刷盘工作。

上述这四种情况,都会导致后台线程执行刷盘工作,从而将数据真正的落入磁盘中存储。

三:InnoDB缓冲池内存是如何管理的

InnoDB虽然在启动时,会将连续的内存划分为一块块的缓冲页,但这仅是逻辑上的划分,本质上所有的缓冲页之间,也是连续的内存。

但随着MySQL在线上运行的时间越来越长,自然会导致这片连续的缓冲页变得七零八落

在这里插入图片描述

当从磁盘加载一个数据页时,总不能将所有的缓冲页全部遍历一次,所以为了更好的管理缓冲池,InnoDB会为每个缓冲页创建一个控制块

1:什么是缓冲页的控制块

控制块是专门用于管理缓冲页而设计的一种结构,其中会包含:数据页所属的表空间、页号、缓冲页地址、链表节点指针等信息

所有的控制块都会放在缓冲池最前面

在这里插入图片描述

控制块也会占用缓冲池的内存空间,InnoDB会为每一个缓冲页都分配一个对应的控制块,后续InnoDB可以基于控制块去管理每一块缓冲页

2:空闲页的管理

为了能够更快的找到缓冲池中的空闲页,InnoDB会以控制块作为节点,将所有空闲的缓冲页组成一个空闲链表,即Free链表

在这里插入图片描述

因为控制块对应着一个个的缓冲页,以控制块作为链表节点,也就等价于是由缓冲页组成的链表,在链表中会存在一个头结点,内部主要有三个值:

  • head:这是一根指针,指向空闲链表的第一个控制块。
  • tail:同样是一根指针,指向空闲链表的最后一个控制块。
  • count:这是一个数字,用来记录空闲链表的节点数量。

有了空闲链表后,会有什么好处呢?

当需要一块新的缓冲页存储磁盘数据时,直接找到空闲链表,根据空闲链表的指针,从中拿一块空闲缓冲页使用即可。

3:标记页的管理

当线程变更了内存中的数据页之后,会先对这个数据页做个标记,然后直接给客户端返回「执行成功」的响应。

在这个过程中,被线程变更并标记过的数据页,则被称之为标记页,有些地方也被称之为“脏页”。

对于内存中变更过的数据页,最终绝对是需要刷写到磁盘中的,这个工作会由MySQL的后台线程完成

当后台线程要刷盘时,它咋知道哪些数据页是变更过的呢?

为了后台线程刷盘时效率更高,InnoDB同样又创造了一个Flush链表,它的结构和Free链表一模一样,两者的不同点在于:

  • Free链表:记录空闲缓冲页,为了使用时能更快的找到空闲缓冲页。
  • Flush链表:记录标记过的缓冲页,为了刷盘时能够更快的找到变更数据页。

在这里插入图片描述

当后台线程开始刷盘工作时,会直接找到Flush链表,然后直接将该链表中对应的缓冲页,其变更过的数据刷写到磁盘。

4:淘汰机制(重点)

Buffer Pool的内存空间是有限的,因此无法支撑所有数据源源不断的载入内存,所以InnoDB内部有一套自己的淘汰机制

首先明确下淘汰的含义:淘汰是指将一个已使用的缓冲页,其中的所有数据清空,使其变为一个空闲页。

淘汰愿景:那些频繁被访问的数据页可以长期驻留在内存中,一些很少被访问的数据页能够淘汰掉。

与Free空闲链表、Flush刷写链表相同,对于要可淘汰的数据页,也会被组合成一个LRU淘汰链表

在这里插入图片描述

对于空闲页和标记页是不会纳入淘汰范围的

  • 空闲页:本身这些缓冲页都没有被使用,内存都是空白的,淘汰空闲页没有任何意义。
  • 标记页:被标记过的缓冲页中,由于存在数据还未落盘,所以淘汰掉之后代表数据会丢失。

因此LRU链表是由已使用、但未曾变更过的缓冲页组成的,不过要注意:有些数据页会在Flush、LRU两个链表之间“跳动”:

  • 当LRU链表中的一个数据页发生变更后,会从LRU链表转到Flush链表。
  • 当标记页中的变更数据落盘后,此时标记页又会从Flush链表回到LRU链表。

末位淘汰机制 - 残酷的森林法则

  • 当一条线程来读写数据时,命中了缓冲区中的某个数据页,那就直接将该页挪到LRU链表最前面。
  • 当未命中缓冲数据页时,需要走磁盘载入数据页,此时内存不够的情况下,会淘汰链表末尾的数据页。

假设此时LRU链表由8个缓冲页组成,并且此时缓冲区空间已满

在这里插入图片描述

此时假设一条查询语句,命中了其中的第6个数据页,此时这个数据页会被挪到最前面:

在这里插入图片描述

此时又来了一条SQL,要操作的数据在缓冲区不存在,因此会从磁盘读取数据并载入内存,但因为目前缓冲区已经满了,所以需要淘汰一个缓冲页,用来存放载入的新数据,此时就会将末尾的数据页淘汰

在这里插入图片描述

5:末位淘汰的问题

5.1:预读失效问题(young, old解决)

因为局部性原理,MySQL在读取数据时,也就是读取一个数据时,默认会将其附近的16KB数据一次性全部载入内存

当数据载入内存后会分配一个缓冲页来存放,并且会将相应的数据页放在LRU链表的最前面,而这个数据页一共是有16KB数据的,也就意味着里面会有多行表数据,假设此时程序只读取了这页数据中的一行记录,对于其他数据并不需要读取,这也就是所谓的预读失效问题

在这里插入图片描述

同时,MySQL读取数据时,除开会利用局部性原理将一整页数据载入内存外,InnoDB也有自己的预读机制

InnoDB并未采用最基本的末尾淘汰算法,而是对其做了些许优化,会将整个LRU算法划分为old、young两个区域组成。

在这里插入图片描述

young、old两个区域在LRU链表中的占比,默认为63:37,你也可以通过innodb_old_blocks_pc这个参数,来手动调整old区在整个LRU链表中的占比。

默认不改的情况下,假设LRU链表中由100缓冲页构成,那么前63个属于young区,后37个属于old

LRU链表被划分为两个区域后,从磁盘中预读的数据页,就只需要加入到old区域的头部,当这个数据页被真正访问时,才会将其插入young区的头部。

如果预读的这页在后续一直没有被访问,就会从old区域移除,从而不会影响young区域中的热点数据。

也就是说,在划分为两个区域后,young区域是用来存储真正的热点数据页,而old区则是用来存放有可能成为热点数据页的“候选人”

当需要淘汰缓冲页时,会优先淘汰old区中的数据页,毕竟young区中留下的都是久经考验的精英!

5.2:缓冲池污染问题(young区晋升解决)

什么是缓冲池污染问题

假设一条线程在执行SQL语句,目前是需要查询一张百万级别的所有表数据,由于Buffer Pool空间有限,所以如果按照原本的淘汰规则来清理内存,这次查询过程可能会导致Buffer Pool里面的所有热点数据全部被换出。

等这次查询结束后,内存中只剩下了这次查询载入的数据页,当有线程访问原本哪些热点数据时,由于缓冲区中的数据页被换出了,因此就会产生大量的磁盘IO

上述这个过程,则被称之为Buffer Pool污染问题。

⚠️ 并不是需要查询大量结果才会导致这个问题出现,而是当扫描的数据过多时,都会引发此问题,比如典型的对大表执行了全表扫描

young区晋升限制

JVM中,为了防止新生代过早晋升年老代,从而频繁触发FullGC的问题,在设计时也有晋升条件限制

默认情况下,一个对象只有达到了15岁之后,才能从新生代晋升年老代,毕竟能够熬过16轮新生代GC的对象,也绝对不会无缘无故突然挂掉。

InnoDB中的young区晋升限制,同样是这个原理

加入了young区的晋升限制后,就能有效避免这种访问一次的数据页过早进入young

InnoDB加了一个停留时间的限制,如果一个数据页想从old区晋升到young区,必须要在old区中存活一定时间,这个时间默认为1s/1000ms

也可以通过参数innodb_old_blocks_time调整。

由于存在这个时间限制,所以old区的数据页,想要进入young区,就必须达成两个条件:

  • old区中停留的时间超过了1000ms
  • old区中,一秒后有线程再次访问了这个数据页。

通过这种晋升限制的方式,就能完美的解决全表扫描引起的缓冲池污染问题,这也是InnoDB最终的淘汰机制

当一个缓冲页的数据被淘汰后,也就是一个缓冲页的数据被清空后,会将其再次加入到Free空闲链表中等待分配。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2289782.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

程序员学英文之At the Airport Customs

Dialogue-1 Making Airline Reservation预定机票 My cousin works for Xiamen Airlines. 我表哥在厦航上班。I’d like to book an air ticket. 我想预定一张机票。Don’t judge a book by its cover. 不要以貌取人。I’d like to book / re-serve a table for 10. 我想预定一…

Redis代金卷(优惠卷)秒杀案例-单应用版

优惠卷表:优惠卷基本信息,优惠金额,使用规则 包含普通优惠卷和特价优惠卷(秒杀卷) 优惠卷的库存表:优惠卷的库存,开始抢购时间,结束抢购时间.只有特价优惠卷(秒杀卷)才需要填写这些信息 优惠卷订单表 卷的表里已经有一条普通优惠卷记录 下面首先新增一条秒杀优惠卷记录 { &quo…

51单片机 01 LED

一、点亮一个LED 在STC-ISP中单片机型号选择 STC89C52RC/LE52RC;如果没有找到hex文件(在objects文件夹下),在keil中options for target-output- 勾选 create hex file。 如果要修改编程 :重新编译-下载/编程-单片机重…

MusicFree-开源的第三方音乐在线播放和下载工具, 支持歌单导入[对标落雪音乐]

MusicFree 链接:https://pan.xunlei.com/s/VOI0RrVLTTWE9kkpt0U7ofGBA1?pwd4ei6#

消息队列篇--原理篇--常见消息队列总结(RabbitMQ,Kafka,ActiveMQ,RocketMQ,Pulsar)

1、RabbitMQ 特点: AMQP协议:RabbitMQ是基于AMQP(高级消息队列协议)构建的,支持多种消息传递模式,如发布/订阅、路由、RPC等。多语言支持:支持多种编程语言的客户端库,包括Java、P…

nacos 配置管理、 配置热更新、 动态路由

文章目录 配置管理引入jar包添加 bootstrap.yaml 文件配置在application.yaml 中添加自定义信息nacos 配置信息 配置热更新采用第一种配置根据服务名确定配置文件根据后缀确定配置文件 动态路由DynamicRouteLoaderNacosConfigManagerRouteDefinitionWriter 路由配置 配置管理 …

(笔记+作业)书生大模型实战营春节卷王班---L0G2000 Python 基础知识

学员闯关手册:https://aicarrier.feishu.cn/wiki/QtJnweAW1iFl8LkoMKGcsUS9nld 课程视频:https://www.bilibili.com/video/BV13U1VYmEUr/ 课程文档:https://github.com/InternLM/Tutorial/tree/camp4/docs/L0/Python 关卡作业:htt…

SpringBoot中Excel表的导入、导出功能的实现

文章目录 一、easyExcel简介二、Excel表的导出2.1 添加 Maven 依赖2.2 创建导出数据的实体类4. 编写导出接口5. 前端代码6. 实现效果 三、excel表的导出1. Excel表导入的整体流程1.1 配置文件存储路径 2. 前端实现2.1 文件上传组件 2.2 文件上传逻辑3. 后端实现3.1 文件上传接口…

动态规划DP 背包问题 完全背包问题(题目分析+C++完整代码)

概览检索 动态规划DP 概览(点击链接跳转) 动态规划DP 背包问题 概览(点击链接跳转) 完全背包问题 原题链接 AcWiing 3. 完全背包问题 题目描述 有 N种物品和一个容量是 V的背包,每种物品都有无限件可用。 第 i种物…

【cocos creator】【模拟经营】餐厅经营demo

下载:【cocos creator】模拟经营餐厅经营

【深度学习】softmax回归的从零开始实现

softmax回归的从零开始实现 (就像我们从零开始实现线性回归一样,)我们认为softmax回归也是重要的基础,因此(应该知道实现softmax回归的细节)。 本节我们将使用Fashion-MNIST数据集,并设置数据迭代器的批量大小为256。 import torch from IP…

【Redis】set 和 zset 类型的介绍和常用命令

1. set 1.1 介绍 set 类型和 list 不同的是,存储的元素是无序的,并且元素不允许重复,Redis 除了支持集合内的增删查改操作,还支持多个集合取交集,并集,差集 1.2 常用命令 命令 介绍 时间复杂度 sadd …

程序诗篇里的灵动笔触:指针绘就数据的梦幻蓝图<3>

大家好啊,我是小象٩(๑ω๑)۶ 我的博客:Xiao Xiangζั͡ޓއއ 很高兴见到大家,希望能够和大家一起交流学习,共同进步。 今天我们来对上一节做一些小补充,了解学习一下assert断言,指针的使用和传址调用…

神经网络的数据流动过程(张量的转换和输出)

文章目录 1、文本从输入到输出,经历了什么?2、数据流动过程是张量,如何知道张量表达的文本内容?3、词转为张量、张量转为词是唯一的吗?为什么?4、如何保证词张量的质量和合理性5、总结 🍃作者介…

爬取鲜花网站数据

待爬取网页: 代码: import requestsfrom lxml import etree import pandas as pdfrom lxml import html import xlwturl "https://www.haohua.com/xianhua/"header {"accept":"image/avif,image/webp,image/apng,image/sv…

vue框架技术相关概述以及前端框架整合

vue框架技术概述及前端框架整合 1 node.js 介绍:什么是node.js Node.js就是运行在服务端的JavaScript。 Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎。 作用 1 运行java需要安装JDK,而Node.js是JavaScript的运行环…

数据结构 树2

文章目录 前言 一,二叉搜索树的高度 二,广度优先VS深度优先 三,广度优先的代码实现 四,深度优先代码实现 五,判断是否为二叉搜索树 六,删除一个节点 七,二叉收索树的中序后续节点 总结 …

NeetCode刷题第19天(2025.1.31)

文章目录 099 Maximum Product Subarray 最大乘积子数组100 Word Break 断字101 Longest Increasing Subsequence 最长递增的子序列102 Maximum Product Subarray 最大乘积子数组103 Partition Equal Subset Sum 分区等于子集和104 Unique Paths 唯一路径105 Longest Common Su…

Google Chrome-便携增强版[解压即用]

Google Chrome-便携增强版 链接:https://pan.xunlei.com/s/VOI0OyrhUx3biEbFgJyLl-Z8A1?pwdf5qa# a 特点描述 √ 无升级、便携式、绿色免安装,即可以覆盖更新又能解压使用! √ 此增强版,支持右键解压使用 √ 加入Chrome增强…

[EAI-027] RDT-1B,目前最大的用于机器人双臂操作的机器人基础模型

Paper Card 论文标题:RDT-1B: a Diffusion Foundation Model for Bimanual Manipulation 论文作者:Songming Liu, Lingxuan Wu, Bangguo Li, Hengkai Tan, Huayu Chen, Zhengyi Wang, Ke Xu, Hang Su, Jun Zhu 论文链接:https://arxiv.org/ab…