因此,为了提升读写性能,Innodb 引擎引入了一个中间层,即缓冲池(buffer pool)。
缓冲池是内存中一块连续的空间,主要用于缓存数据页。每个数据页的大小为 16KB。
页是 Innodb 进行数据存储的基本单元,无论是在磁盘还是在缓冲池中,数据的读取都是以页为单位进行的,这也体现了一种“预读”的思想。
图片
有了缓冲池之后,当我们进行数据查询时,InnoDB 会首先检查缓冲池中是否存在该数据。如果存在,数据就可以直接从内存中获取,避免了频繁的磁盘读取,从而提高查询性能。如果不存在,则会去磁盘中读取数据,并将找到的数据页复制到缓冲池中,再返回给客户端。这样,后续的查询可以直接从缓冲池中就近读取数据。
图片
当需要进行数据修改时,操作也会先在缓冲池中进行,然后再将修改后的数据写入磁盘。
然而,由于缓冲池是基于内存的,其空间不可能无限大,默认大小为 128M。当然,这个大小并不是固定的,我们可以通过修改 MySQL 配置文件中的 innodb_buffer_pool_size 参数来调整缓冲池的大小。
# 查看buffer pool
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
# 修改buffer pool
SET GLOBAL innodb_buffer_pool_size = 512M;
扩展知识
InnoDB 的数据页
上面提到了 InnoDB 的数据页,它和 B+树的关系是怎样的呢?
InnoDB 的数据页是其存储引擎中用于存储数据的基本单位。数据页在磁盘上是一个连续的区域,通常大小为 16KB,当然也可以通过配置进行调整。16KB 意味着 InnoDB 的每次读写操作都是以 16KB 为单位的,即一次从磁盘读取到内存的最小单位是 16KB,从内存写入到磁盘的最小单位也是 16KB。
在 B+树结构中,每个节点都对应着一个数据页,包括根节点、非叶子节点和叶子节点。B+树通过节点之间的指针连接不同层级的数据页,从而构建出一个有序的索引结构。
图片
通过 B+树的搜索过程,可以从根节点开始逐层遍历,最终到达叶子节点,找到所需的数据行。
因此,数据页是存储数据行的实际物理空间,以页为单位进行磁盘读写操作。B+树通过节点和指针的组织,构建了一个层次结构的索引,用于快速定位和访问数据行。
B+树的非叶子节点对应着数据页,其中存储着主键及指向子节点(即其他数据页)的指针。B+树的叶子节点包含实际的数据行,每个数据行存储在一个数据页中。
通过这种方式,InnoDB 利用 B+树和数据页的组合,实现了高效的数据存储和检索。B+树提供了快速的索引查找能力,而数据页提供了实际存储和管理数据行的机制。它们相互配合,使得 InnoDB 能够处理大规模数据的高效访问。
数据页的构成
一个数据页包含七个部分,分别是文件头、页头、最小和最大记录、用户记录、空闲空间、页目录以及文件尾。
图片
buffer pool 和 query cache 的区别
在 InnoDB 中,除了缓冲池(Buffer Pool),还有另一个缓存层用于数据缓存,提升查询效率。很多人容易混淆它与缓冲池的区别。
首先,它们的目的和作用不同。缓冲池用于缓存表和索引的数据页,从而加速读取操作;而查询缓存(Query Cache)用于缓存查询结果,减少重复查询的执行时间。
缓冲池主要与存储引擎 InnoDB 相关,而查询缓存也支持其他引擎,如 MyISAM 等。因此,查询缓存位于服务器层的优化技术,而缓冲池位于引擎层的优化技术。
需要注意的是,在 MySQL 5.7 版本中,查询缓存已经被标记为废弃,并在 MySQL 8.0 版本中彻底被移除。
buffer pool 的读写过程是怎么样的?
MySQL 的缓冲池(Buffer Pool)是一个内存区域,用于缓存数据页,从而提高查询性能。读写过程涉及将数据从磁盘读取到内存、在内存中进行修改,并最终写回磁盘。
读过程
当我们在 MySQL 执行一个查询请求时,其过程如下:
- MySQL 首先检查缓冲池(Buffer Pool)中是否存在本次查询的数据。如果数据在缓冲池中,就直接返回结果。
- 如果数据不在缓冲池中,MySQL 会从磁盘读取数据。
- 读取的数据页被放入缓冲池,同时 MySQL 会将请求的数据返回给应用程序。
读取过程相对简单,而缓冲池的写入过程则稍显复杂。
写过程
当执行一次更新语句(如 INSERT、UPDATE 或 DELETE)时,MySQL 的过程如下:
- 应用程序执行写操作时,MySQL 首先将要修改的数据页加载到缓冲池(Buffer Pool)中。
- 在缓冲池中,对数据页进行修改,以满足写请求。这些修改只在内存中进行,不会立即写回磁盘。
- 如果缓冲池中的数据页被修改过,MySQL 会将这个页标记为“脏页”(Dirty Page)。
- 脏页会被后台线程写回磁盘,这个过程称为脏页刷盘。写入操作完成后,数据得以持久化。
需要注意的是,脏页的写回磁盘是由后台线程进行的。在 MySQL 服务器空闲或负载较低时,InnoDB 会执行脏页刷盘操作,以减少对用户线程的影响,从而降低性能的影响。
参考文档:https://dev.mysql.com/doc/refman/8.0/en/innodb-buffer-pool-flushing.html
图片
当脏页的百分比达到innodb_max_dirty_pages_pct_lwm变量定义的低水位标记时,将启动缓冲池的刷新。缓冲池页的默认低水位标记为 10%。将innodb_max_dirty_pages_pct_lwm值设为 0 会禁用这种提前刷新行为。
InnoDB 还采用了一种适应性刷新算法,根据 redo log 的生成速度和当前的刷新率动态调整刷新速度。其目的是通过确保刷新活动与当前工作负载保持同步,来平滑整体性能。
当然,我们也可以通过执行SET GLOBAL innodb_buffer_pool_dump_now=ON来手动触发脏页刷新到磁盘。
此外,在 MySQL 服务器正常关闭或重启时,所有的脏页都会被刷新到磁盘,以确保数据持久化。