【数据库】MySQL InnoDB存储引擎详解 - 读书笔记

news2024/12/23 0:29:59

MySQL InnoDB存储引擎详解 - 读书笔记

  • InnoDB 存储引擎概述
  • InnoDB 存储引擎的版本
  • InnoDB 体系架构
    • 内存
      • 缓冲池
      • LRU List、Free List 和 Flush List
      • 重做日志缓冲(redo log buffer)
      • 额外的内存池
    • 存储结构
      • 表空间
        • 系统表空间
        • 独立表空间
        • 通用表空间
        • undo表空间
        • 临时表空间
    • 后台线程
      • Matser Thread
      • IO Thread
      • Purge Thread
      • Page Cleaner Thread
  • Checkpoint 技术
    • Sharp Checkpoint(尖峰检查点)
    • Fuzzy Checkpoint(模糊检查点)
  • InnoDB 关键特性
    • 插入缓冲(Insert Buffer)
    • 两次写(Double Write)
    • 自适应哈希索引(Adaptive Hash Index)
    • 异步IO(Async IO)
    • 刷新邻接页(Flush Neighbor Page)
  • 启动、关闭与恢复
    • innodb_fast_shutdown
    • innodb_force_recovery
    • 模拟故障

《MySQL技术内幕 InnoDB存储引擎 第2版》个人读书笔记
电子书本体:https://pan.baidu.com/s/1vlhnEE7OtYs1tK_sEatHuw?pwd=6jv4
提取码:6jv4

在这里插入图片描述
在这里插入图片描述
MySQL 官方文档:Chapter 17 The InnoDB Storage Engine

InnoDB 存储引擎概述

以下是 InnoDB 存储引擎的特性列表(mysql 8.3):

特性支持
B-tree 索引
备份/点恢复(在服务器中实现,而不是在存储引擎中)
集群数据库支持
聚集索引
压缩数据
数据缓存
加密数据是(在服务器中通过加密函数实现;在 MySQL 5.7 及更高版本中,支持数据静止加密。)
外键支持
全文搜索索引是(从 MySQL 5.6 开始支持全文索引。)
地理空间数据类型支持
地理空间索引支持是(从 MySQL 5.7 开始支持地理空间索引。)
哈希索引否(InnoDB 内部使用哈希索引来实现其自适应哈希索引功能。)
索引缓存
锁定粒度行级锁定
MVCC
复制支持(在服务器中实现,而不是在存储引擎中)
存储限制64TB
T 树索引
事务
更新数据字典统计信息

InnoDB 是 MySQL 数据库系统中最常用的存储引擎之一,尤其在事务处理、并发控制及数据可靠性方面表现突出。以下是对 InnoDB 存储引擎的主要特点和功能的概述:

  1. 事务支持:InnoDB 提供完全的 ACID(原子性、一致性、隔离性、持久性)事务支持,适用于需要高可靠性和数据一致性的应用场景,如银行交易、电子商务等。

  2. 行级锁定:InnoDB 实现了行级锁定机制,这意味着在执行事务时只锁定受影响的行,而非整个表,这大大提高了并发环境下多用户读写数据的性能。

  3. 多版本并发控制(MVCC):InnoDB 采用 MVCC 来实现高并发下的读写操作,允许在不阻塞读操作的情况下进行写操作,提升了并发性能,降低了死锁概率。

  4. 外键约束:支持外键约束,确保数据的一致性和参照完整性,防止非法的数据操作。

  5. 缓冲池:内存管理中,InnoDB 使用了一个称为缓冲池的内存区域,用于缓存表数据和索引,减少磁盘 I/O 操作,提高数据读写的效率。

  6. 崩溃恢复:提供完善的崩溃恢复机制,包括 redo 日志(重做日志)和 undo 日志(撤销日志),保证即使在系统崩溃后也能恢复到崩溃前的一致状态。

  7. 关键特性

    • 插入缓冲(Insert Buffer):优化对非唯一二级索引插入操作的性能。
    • 两次写(Double Write):用于保护数据页免受意外宕机造成的数据损坏。
    • 自适应哈希索引(Adaptive Hash Index):自动为经常使用的查询创建哈希索引,提高查询速度。
    • 异步IO(Async I/O):提高磁盘I/O性能,减少等待时间。
  8. 存储结构:InnoDB 存储引擎将数据按照页(Page)的方式组织在磁盘上,并且采用B+树作为索引结构,使得数据检索效率相对较高。

  9. 内存结构:InnoDB 内部有多个内存区域,如redo log buffer、undo logs、buffer pool manager等,用于管理事务、缓存数据以及辅助事务处理过程。

总之,InnoDB 存储引擎因其强大的事务处理能力和高度并发控制,被广泛应用于企业级应用中,尤其适合那些需要复杂事务处理和高度数据一致性的业务场景。随着MySQL的发展,InnoDB 不断改进和优化,已经成为现代MySQL数据库的默认存储引擎。

InnoDB 存储引擎的版本

  1. 早期版本:最初的InnoDB是由Innobase Oy公司开发的,并在MySQL数据库中作为一个可选的存储引擎引入。

  2. 集成到MySQL内核:到MySQL 5.1版本时,MySQL数据库开始允许动态加载存储引擎,意味着InnoDB引擎可以独立于MySQL核心版本进行更新升级。

  3. 默认存储引擎:从MySQL 5.5版本开始,InnoDB成为默认的表存储引擎,取代了以前的MyISAM存储引擎,强调了其在事务处理和并发控制方面的优势。

  4. 新特性与优化:后续的MySQL 5.6和5.7版本中,InnoDB引擎获得了更多的改进和优化,例如更好的性能、更高效的内存管理、增强的并发控制、在线DDL操作(如在线添加索引)等。

  5. MySQL 8.0:在MySQL 8.0及更高版本中,InnoDB继续得到加强,包括但不限于:

    • 更快的整数主键查找速度。
    • 默认启用持久化统计信息(persistent statistics)。
    • 改进的临时表处理。
    • 更强的只读事务性能。
    • 对窗口函数的支持更好。

随着时间推移,InnoDB存储引擎不断增强了对更大规模数据处理、更高的并发性能以及更多企业级特性的支持。具体的版本细节和新增功能,需查阅对应MySQL版本的官方文档或发布说明。

InnoDB 体系架构

在这里插入图片描述
InnoDB 的体系架构设计兼顾了事务处理、并发控制、数据安全和性能优化。以下是 InnoDB 体系架构的主要组成部分:

  1. 内存结构

    • Buffer Pool (缓冲池):它是 InnoDB 的核心组件之一,用于缓存表的数据页和索引页,减少对磁盘 I/O 的访问次数。缓冲池中的数据页在事务提交时会根据需要刷新到磁盘。
    • Change Buffer(变更缓冲区):对于非唯一二级索引的插入操作,如果所在的数据页尚未加载到缓冲池中,InnoDB 会将这部分修改暂存在变更缓冲区中,待后续数据页加载到缓冲池时再合并写入。
    • Adaptive Hash Index(自适应哈希索引):InnoDB 会根据查询模式自动生成哈希索引,加快某些查询的性能。
    • Log Buffer(日志缓冲区):存储待写入重做日志文件(redo log)的事务日志记录,以确保事务的持久性和崩溃恢复能力。
  2. 磁盘存储

    • 表空间(Tablespaces):InnoDB 使用表空间作为逻辑上的数据容器,每个表空间可以包含多个数据文件。系统表空间存放全局数据,用户表空间存放用户数据和索引。
    • 重做日志文件(Redo Log):记录了对数据的更改操作,以支持事务的持久性和崩溃恢复。
    • 回滚段(Undo Logs):用于实现事务的原子性和一致性,存储了事务回滚所需的历史版本数据。
  3. 线程与并发控制

    • 后台线程:包括 master thread、IO thread(用于处理redo log刷盘)、purge thread(用于清理不再需要的undo日志)等,共同维护数据库的正常运行和数据一致性。
    • 行级锁定(Row-level Locking):InnoDB 支持行级锁定,有效减少并发事务之间的冲突,提高并发性能。
    • 多版本并发控制(MVCC):InnoDB 通过 MVCC 实现了非锁定读(Read Committed 或 Repeatable Read 隔离级别下),减少了事务之间的阻塞。
  4. 数据组织结构

    • 页(Page):InnoDB 使用页作为基本的存储单位,每个页通常为16KB,存储表数据和索引。
    • B+树索引:InnoDB 表的数据和二级索引都采用 B+树结构组织,保证高效的数据检索和范围查询。
  5. 外部接口

    • InnoDB 通过与 MySQL Server 的交互接口,响应来自客户端的 SQL 请求,执行事务处理、查询优化和数据检索等功能。

内存

在这里插入图片描述
内存结构中有包括缓冲池(innodb_buffer_pool)、重做日志缓冲(redo log_buffer)、额外内存池(innodb_additional_mem_pool_size)

缓冲池

mysql> show variables like '%innodb_buffer_pool%';
+-------------------------------------+----------------+
| Variable_name                       | Value          |
+-------------------------------------+----------------+
| innodb_buffer_pool_chunk_size       | 134217728      |
| innodb_buffer_pool_dump_at_shutdown | ON             |
| innodb_buffer_pool_dump_now         | OFF            |
| innodb_buffer_pool_dump_pct         | 25             |
| innodb_buffer_pool_filename         | ib_buffer_pool |
| innodb_buffer_pool_in_core_file     | ON             |
| innodb_buffer_pool_instances        | 1              |
| innodb_buffer_pool_load_abort       | OFF            |
| innodb_buffer_pool_load_at_startup  | ON             |
| innodb_buffer_pool_load_now         | OFF            |
| innodb_buffer_pool_size             | 134217728      |
+-------------------------------------+----------------+
11 rows in set (0.00 sec)

InnoDB内存数据对象:
在这里插入图片描述
innodb_buffer_pool
在数据库系统中,CPU与磁盘速度之间存在极大速度差异,基于磁盘的数据库系统通常使用缓冲池技术来提高数据库整体性能。缓冲池是一块内存区域,通过内存速度弥补磁盘速度较慢带来的影响。

数据库读取页操作,首先将从磁盘读到的页存放到缓冲池中,称为将页fix到缓冲池中。下次读取相同页时,首先判断缓冲池中是否有该夜,如果有,则命中,直接读取该页,如果没有,在进行fix的过程。

数据库中页的修改操作,先修改缓冲池中的页,然后再以一定的频率刷到磁盘上。从缓冲池刷回磁盘操作并不是在每次页发生更改后进行,而是通过一种checkPoint机制刷回磁盘。

缓冲池的大小直接影响着数据库的整体性能。InnoDB存储引擎而言,缓冲池的配置通过参数innodb_buffer_pool_size 来设置。

缓冲池中的数据类型有:索引页(index page),数据页(data page),插入缓冲(change buffer),自适应哈希索引(Adaptive hash index),锁信息(lock info),数据字典信息(dictionary cache)

执行 show engine innodb status 显示的不是当前的状态,而是过去某个时间范围内InnoDB存储引擎的状态。如下:Per second averages calculated from the last 33 seconds 代表的信息为过去33秒内的数据库状态。

mysql> show engine innodb status \G;
...
=====================================
2024-03-13 09:17:27 140257528968960 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 33 seconds
...
----------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 0
Dictionary memory allocated 489041
Buffer pool size   8192
Free buffers       7033
Database pages     1155
Old database pages 446
Modified db pages  0
Pending reads      0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 1009, created 146, written 202
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
No buffer pool page gets since the last printout
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 1155, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
...

// 观察缓冲池使用状态
mysql> select POOL_ID,POOL_SIZE,FREE_BUFFERS,DATABASE_PAGES from information_schema.INNODB_BUFFER_POOL_STATS \G;
*************************** 1. row ***************************
       POOL_ID: 0
     POOL_SIZE: 8192
  FREE_BUFFERS: 7033
DATABASE_PAGES: 1155
1 row in set (0.00 sec)

LRU List、Free List 和 Flush List

InnoDB存储引擎是怎么对这么大的内存区域进行管理的呢?

LRU(Latest Rcent Used,最近最少使用)算法 ,LRU List 是 InnoDB 缓冲池的核心组件,它是一个双向链表,用于缓存从磁盘读取的页。即最频繁适用的页再LRU列表的前端,而最少适用的页在LRU列表的尾端。当缓冲池不能存放新读取到的页时,将首先释放LRU列表中尾端的页。InnoDB 对传统的LRU算法做了一些优化。

mysql> show variables like 'innodb_old_blocks%';
+------------------------+-------+
| Variable_name          | Value |
+------------------------+-------+
| innodb_old_blocks_pct  | 37    |
| innodb_old_blocks_time | 1000  |
+------------------------+-------+

innodb_old_blocks_pct

  • 默认值为 37,取值范围是 5 到 95 之间的整数。
  • 此参数决定新读取的数据页在首次进入缓冲池时放置的位置。当数据页首次加载到缓冲池时,会被放在 LRU 链表的中间位置(大致为整个链表长度的 3/8 处,即 37% 处)而不是尾部。这样做的目的是为了让经常访问的新读取页有机会在短时间内再次被访问时仍然保留在缓冲池中,而不是很快被淘汰出去。

innodb_old_blocks_time

  • 默认值为 1000,单位是毫秒。
  • 当新读取的数据页被插入到 LRU 中间位置(midpoint)后,此参数控制该数据页在被移动到 LRU 尾部(即将面临淘汰的风险)之前需要等待的最短时间。
  • 如果在这段时间内数据页又被访问到,那么它就不会移动到 LRU 尾部,继续保留下来,这样有助于缓存热数据并减少磁盘I/O。

这两个参数共同作用,旨在优化 InnoDB 缓冲池的缓存命中率,特别是对于周期性或临时热点数据,能够有效地保持在缓冲池中,提高数据库的整体性能。可以通过调整这两个参数,根据实际工作负载的特性来微调缓存管理策略。

mysql> SET GLOBAL innodb_old_blocks_time=0;
mysql> SET GLOBAL innodb_old_blocks_pct=20;

Free List
LRU 列表用来管理已经读取的页,但当数据库刚启动时,LRU列表是空的,即没有任何的页。这时页都存放在Free列表中。Free List 是缓冲池中未分配给任何数据页的空闲块列表。当缓冲池中的页不再需要时(比如已被删除或更新),这些页会被移到 Free List 上,等待重新分配给新的数据页使用。

mysql> show engine innodb status \G;
*************************** 1. row ***************************
  Type: InnoDB
  Name: 
Status: 
=====================================
2024-03-13 11:37:39 140257528968960 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 7 seconds
...
----------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 0
Dictionary memory allocated 489041
Buffer pool size   8192
Free buffers       7033
Database pages     1155
Old database pages 446
Modified db pages  0
Pending reads      0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 1009, created 146, written 202
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
No buffer pool page gets since the last printout
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 1155, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
...
----------------------------
END OF INNODB MONITOR OUTPUT
============================

1 row in set (0.00 sec)

通过命令 show engine innodb status 可以看到:当前 Buffer pool size 共有 8192 个页,即 8192*16K,总共 128MB 的缓冲池。Free buffers 表示当前 Free 列表中页的数量, Database pages 表示 LRU 列表中页的数量。
Free buffers + Database pages 不等于 Buffer pool size,是因为缓冲池的页可能还会分配给其他不需要 LRU 算法维护的部分(自适应哈希索引、Lock信息、Insert Buffer 等页),因此不存在于 LRU列表中。(如图 <2-2:InnoDB内存数据对象> 所示)

Flush List
Flush List 主要用于跟踪脏页(即已修改但尚未写回磁盘的数据页)。InnoDB 会定期检查并执行后台刷脏任务,将 Flush List 中的脏页刷新到磁盘,以保持数据的一致性和持久性。当缓冲池空间不足时,InnoDB 也会优先考虑将 Flush List 中的脏页写出并释放空间。

重做日志缓冲(redo log buffer)

mysql> SHOW variables like 'innodb_log_buffer_size';
+------------------------+----------+
| Variable_name          | Value    |
+------------------------+----------+
| innodb_log_buffer_size | 16777216 |
+------------------------+----------+
1 row in set (0.00 sec)

通常情况下,16M 的重做日志缓冲池足以满足绝大部分的应用,因为重做日志在下列三种情况下会将重做日志缓冲中的内容刷新到外部磁盘的重做日志文件中。

  • Master Thread 每一秒将重做日志缓冲刷新到重做日志文件;
  • 每个事务提交时会将重做日志缓冲刷新到重做日志文件;
  • 当重做日志缓冲池剩余空间小于 1/2 时,重做日志缓冲刷新到重做日志文件。

额外的内存池

额外的内存池通常被DBA忽略,他们认为该值并不十分重要,事实恰恰相反,该值同样十分重要。在InnoDB存储引擎中,对内存的管理是通过一种称为内存堆(heap)的方式进行的。在对一些数据结构本身的内存进行分配时,需要从额外的内存池中进行申请,当该区域的内存不够时,会从缓冲池中进行申请。例如,分配了缓冲池(innodbbuffer_pool),但是每个缓冲池中的帧缓冲(frame buffer)还有对应的缓冲控制对象(buffer controlblock),这些对象记录了一些诸如LRU、锁、等待等信息,而这个对象的内存需要从额外内存池中申请。因此,在申请了很大的InnoDB缓冲池时,也应考虑相应地增加这个值。

存储结构

InnoDB 逻辑存储单元主要分为:表空间、段、区、页。层级关系如下图:
在这里插入图片描述

表空间

MySQL5.7以后,表空间分为:系统表空间、独立表空间、通用表空间、undo表空间、临时表空间。

系统表空间

系统表空间以 ibdata1 命名,新建一个数据库时,InnoDB 存储引擎会初始化一个名为 ibdata1 的表空间文件,默认情况下,这个文件存储所有表的数据,比如看不到的系统表,此外,还会存储数据字典、双写缓冲区、更新缓冲区和undo log。MySQL 5.6 以后,已经可以通过参数来设置 undo log 的存储位置了,可以独立于 ibdata1 文件进行存储。

如果系统表空间大小不够,可以配置为自动扩展,使用以下参数指定系统表空间的路径、初始化大小、自动扩展策略。数据库默认的自动扩展大小是 64MB,当然还可以通过添加另一个数据文件来增加系统表空间的大小。

mysql> show variables like '%innodb_data_file_path%';
+-----------------------+------------------------+
| Variable_name         | Value                  |
+-----------------------+------------------------+
| innodb_data_file_path | ibdata1:12M:autoextend |
+-----------------------+------------------------+
1 row in set (0.02 sec)

mysql> show variables like '%auto%';
+----------------------------------------------+-------+
| Variable_name                                | Value |
+----------------------------------------------+-------+
| auto_generate_certs                          | ON    |
| auto_increment_increment                     | 1     |
| auto_increment_offset                        | 1     |
| autocommit                                   | ON    |
| automatic_sp_privileges                      | ON    |
| binlog_expire_logs_auto_purge                | ON    |
| caching_sha2_password_auto_generate_rsa_keys | ON    |
| innodb_autoextend_increment                  | 64    |
| innodb_autoinc_lock_mode                     | 2     |
| innodb_stats_auto_recalc                     | ON    |
| sha256_password_auto_generate_rsa_keys       | ON    |
| sql_auto_is_null                             | OFF   |
+----------------------------------------------+-------+
12 rows in set (0.00 sec)

MySQL 默认 ibdata1 大小是 12MB(Mysql 8),一般不建议使用默认大小,在遇到高并发事务时,会受到很大影响,建议把 ibdata1 的初始数值大小调整为 1GB

独立表空间

除了系统表空间还有独立表空间(File-per-table),每个表的表空间包含单个 InnoDB 表的数据和索引,并存储在文件系统上的单个数据文件中。

MySQL 每创建一个表,就会生成一个独立表空间,是以 table_name.db 命名的。

系统表空间和独立表空间有什么区别?

  • 截断或删除独立表空间中创建的表后,磁盘空间将被释放,而截断或删除系统表空间中的表会在系统表空间数据文件中创建空闲空间,此空间只能用于存储 InnoDB 数据,也就是系统表空间在表截断或删除后占用系统空间不会缩小。
  • 可以从另一个 MySQL 实例导入独立表空间中的表,而系统表空间不行
  • 更多请查看官网…
通用表空间

与系统表空间类似,通用表空间是能够为多个表存储数据的共享表空间。可以根据活跃度来划分表,存放在不同的磁盘上,可以减少 metadata 的存储开销。

undo表空间

undo表空间包含undo log,这是包含有关如何撤消事务对聚集索引记录的最新更改的信息的记录集合。

undo日志默认存储在系统表空间中,但也可以存储在一个或多个undo表空间中。使用undo表空间可以减少任何一个表空间中undo log所需的空间量。undo log 的 I/O 模式也使 undo 空间成为SSD存储的理想选择 。

临时表空间

把临时表的数据从系统表空间中抽离出来,形成独立的表空间参数 innodb_temp_data_file_path ,独立表空间文件名为 ibtmp1,默认大小 12MB

表空间是由段组成的,也可以把一个表理解为一个段,通常由数据段、回滚段、索引段等,每个段由 N 个区和 32 个零散的页组成,段空间扩展是以区为单位进行扩展的,通常情况下,创建一个索引的同时会创建两个段,分别为非叶子节点和叶子节点段。

区是由连续的页组成的,是物理上连续分配的一段空间,每个区的大小固定是 1MB

InnoDB 的最小物理存储单位是页,有数据页回滚页等。一般情况下,一个区由 64 个连续的页组成,页默认大小是 16KB,可以自行调整页的大小。区也就是 64*16KB = 1MB

页里又记录着行记录的信息,InnoDB存储引擎是面向列的,也就是数据是按行存储的。行记录格式可以分为四种:Compact,dynamic,redundant,compressed,MySQL5.7 默认使用 dynamic 行记录格式。Compact 是目前使用最多的一种,但默认是 dynamic ,二者有什么不同?

行溢出简单来讲就是需要存储的数据在当前存储页面外,拆分到多个页进行存储。针对大数据类型 text 或者 blob 存储在其字段中的数据,dynamic 实际采用的数据都存放在溢出的页中,而数据页只存前20个字节的指针。在 compact 行格式下,溢出的列只存放 768 个前缀字节,dynamic 这种行格式模式,针对溢出列所在的新页利用率会更高,所以目前生产环境中建议使用 dynamic 行格式存储。

redundant 是最早的行记录格式,相比 compact 消耗更多存储空间,不建议使用。

compressed 是压缩行格式,对数据和索引页进行压缩。但只是针对物理层面的压缩,在内存中是不压缩的,当数据调用到内存中就涉及转换,会有很多无用的CPU消耗,而且效率也低。压缩比例也不高,大概1/2 的比例,压缩会使数据库的TPS下降,很影响线上业务,不建议使用。

后台线程

MySQL中InnoDB存储引擎使用了多个IO线程来优化和管理与磁盘的交互操作,确保数据的高效读写。

  1. Master Thread:主线程是InnoDB存储引擎的核心调度者,负责执行多种后台任务,其中包括清理缓冲池中的脏页(通过checkpoint机制)、合并插入缓存(Insert Buffer)、刷新doublewrite buffer、以及定期检查并关闭空闲事务等。尽管主线程不完全等同于传统的IO线程,但它在处理与磁盘相关的I/O活动时起到了关键作用。

  2. IO Thread (Read/Write):InnoDB中有多个读写IO线程,它们专门负责从磁盘读取数据页到内存缓冲池或把缓冲池中的脏页刷新回磁盘。这些线程独立运行,并发地处理I/O请求以提高性能。例如,当查询需要访问尚未加载至缓冲池的数据页时,读取IO线程会异步地将所需页面从磁盘读入内存;同样,在需要将脏页刷回到磁盘时,写入IO线程会将脏页从缓冲池写入到磁盘上的数据文件。

  3. Doublewrite Buffer IO Thread:在实现doublewrite功能时,有一个特定的IO线程用于执行doublewrite buffer的操作,即在脏页被写入最终位置之前,先将其复制到一个连续的物理区域,以防止部分写入导致的数据损坏问题。这个过程涉及到两次I/O操作,分别由对应的IO线程进行处理。

  4. Log Write Thread:除了上述数据页的读写操作外,还有日志写入线程负责将重做日志缓冲区的内容持久化到磁盘上的重做日志文件中。这是一个至关重要的I/O操作,因为它保障了事务的持久性,即使发生系统崩溃也能恢复未完成的事务。

Matser Thread

在这里插入图片描述
MySQL InnoDB 存储引擎中的 Master Thread 是一个非常关键的核心后台线程,它负责协调和调度多种重要任务,确保数据库引擎能够有效地执行事务处理、日志管理、内存管理、I/O操作等核心功能。以下是 Master Thread 的主要工作原理和功能的详细解释:

工作原理概述:

  • 主循环 (Loop): Master Thread 执行一系列循环,其中最为核心的是主循环。在主循环中,InnoDB 定期执行一系列定时任务,分为每秒操作和每10秒操作两部分。这些操作可能包括检查并刷新脏页(即将缓冲池中的修改同步到磁盘数据文件)、合并插入缓冲、清理无用的 undo 页等。

  • 后台循环 (Background Loop): 当数据库处于空闲状态(没有用户链接)或收到停止命令时,Master Thread 可能会进入后台循环,执行一些不需要实时响应的任务,比如后台 IO 操作、清理无效的数据块等。

  • 刷新循环 (Flush Loop): 当需要集中处理脏页刷新到磁盘时,Master Thread 会进入刷新循环,确保重做日志不会因满载而导致事务无法继续进行。此时,Master Thread 将尽可能快地将缓冲池中的脏页写回磁盘,以释放重做日志空间。

  • 暂停循环 (Suspend Loop): 若数据库长时间保持空闲,且后台及刷新循环中的任务都已完成,Master Thread 将进入暂停循环,挂起自己,直到有新的事件唤醒它。这样设计有助于节省系统资源。

Master Thread 的具体工作内容:

  • 重做日志管理: Master Thread 负责定期将重做日志缓冲区的内容刷新到磁盘上的重做日志文件,以确保事务的持久性。

  • 脏页刷新: 定时或按需将缓冲池中的脏页写回磁盘,实现数据的持久化,并释放脏页以供新数据使用。

  • 合并插入缓冲: 对于非聚集索引的插入和更新操作,若目标索引页不在缓冲池中,InnoDB 会先将改动暂存于插入缓冲,然后 Master Thread 会在适当时候将多个插入操作合并并批量写入磁盘,从而优化随机写操作。

  • 系统监控和清理: 监控和调整系统资源使用,如内存碎片整理、死锁检测和处理、undo页的回收等。

  • 其它后台任务: 执行一些计划性的后台维护任务,如统计信息收集、表空间检查、redo log文件切换等。

相关参数:

mysql> show variables like '%innodb_io%';
+------------------------+-------+
| Variable_name          | Value |
+------------------------+-------+
| innodb_io_capacity     | 200   |
| innodb_io_capacity_max | 2000  |
+------------------------+-------+
2 rows in set (0.01 sec)

这两个参数共同决定了InnoDB在正常情况和突发状况下对磁盘I/O资源的使用策略,对于确保数据库性能稳定性和最大化硬件利用率至关重要。在进行调优时,应根据实际环境中的存储设备类型(HDD、SSD或其他高速存储)、硬件IOPS能力、业务负载特点等因素综合考虑和调整这两个参数。

innodb_io_capacity:

  • 这个参数指示了InnoDB认为其所在服务器的磁盘I/O子系统的最大吞吐能力,即每秒能处理多少个页的读写操作。默认值通常是较低的(如200或100),可以根据实际硬件性能进行调整。
  • 设置该参数会影响到诸如脏页刷新频率、双写缓冲区(doublewrite buffer)的刷新策略等,从而间接影响到事务提交的速度和数据的持久性保证。
  • 如果设置得过低,可能导致系统无法充分利用存储设备的实际I/O能力;过高则可能造成过于激进的脏页刷新,使得IO资源消耗尖峰,反而影响整体性能。

innodb_io_capacity_max:

  • 这个参数定义了InnoDB在极端情况下可以临时提升IO操作上限到的最大值。
  • 它通常设置为 innodb_io_capacity 参数值的倍数,例如可能是它的两倍或者更高。
  • 当系统处于高并发写入压力下,并且存储系统有足够的剩余IO能力时,InnoDB可能会临时提高IO操作量以更快地清理脏页,但不会长期维持在这个峰值水平,以免对其他服务造成影响。
mysql> show variables like '%innodb_max_dirty%';
+--------------------------------+-----------+
| Variable_name                  | Value     |
+--------------------------------+-----------+
| innodb_max_dirty_pages_pct     | 90.000000 |
| innodb_max_dirty_pages_pct_lwm | 10.000000 |
+--------------------------------+-----------+
2 rows in set (0.02 sec)

innodb_max_dirty_pages_pct 控制脏页总量的绝对上限,而 innodb_max_dirty_pages_pct_lwm 则是一个更加灵活的指标,用于调节脏页刷新的“宽松度”,使系统能够在较低负载时减少不必要的刷脏动作,在较高负载时又能及时响应,防止脏页累积过多。这两者结合使用,旨在实现对数据库性能和数据安全性的平衡管理。

innodb_max_dirty_pages_pct:

  • 这个参数设定了InnoDB缓冲池中脏页(尚未写回磁盘的数据页)所占百分比的上限。一旦缓冲池中的脏页数量达到了这个百分比,InnoDB就开始主动发起刷脏操作,将内存中的数据写回到磁盘上的表空间中。
  • 默认情况下,这个百分比设定为75%,也就是说,当缓冲池中有75%的页变为脏页时,InnoDB将开始尝试减少脏页的数量以保持在这个阈值以下。
  • 该参数的合理设置有助于平衡内存中数据的持久性和写操作的效率,防止在某些情况下由于大量脏页需要同时刷新导致I/O阻塞,进而影响整个数据库的性能。

innodb_max_dirty_pages_pct_lwm:

  • 这是“low water mark”的缩写,意为脏页百分比的低水位标记。
  • 当缓冲池中的脏页比例低于这个参数设定的值时,InnoDB会相对减少刷脏页的力度,允许脏页比例上升至较高的程度再进行更积极的刷新操作。
  • 设置这个参数可以提供一个更为精细化的控制机制,让InnoDB能够更加智能地调整脏页刷新的策略,在满足一定持久性要求的同时尽可能地减少不必要的I/O活动,从而提升系统性能。
mysql> show variables like '%innodb_adaptive%';
+----------------------------------+--------+
| Variable_name                    | Value  |
+----------------------------------+--------+
| innodb_adaptive_flushing         | ON     |
| innodb_adaptive_flushing_lwm     | 10     |
| innodb_adaptive_hash_index       | ON     |
| innodb_adaptive_hash_index_parts | 8      |
| innodb_adaptive_max_sleep_delay  | 150000 |
+----------------------------------+--------+
5 rows in set (0.00 sec)

innodb_adaptive_flushing: 这个参数控制是否启用InnoDB的自适应刷脏(adaptive flushing)特性。当设置为ON时,InnoDB能够动态调整脏页刷新的策略,根据系统当前的工作负载和IO能力来决定何时将内存中的脏页写回磁盘,以优化系统性能和数据安全性。

innodb_adaptive_flushing_lwm: 这个参数是在启用innodb_adaptive_flushing后使用的,表示脏页百分比的低水位线(lower watermark)。当缓冲池中脏页的比例降低到这个值以下时,InnoDB会相应减慢脏页刷新的速率,而在超过这个比例时增加刷新速度。这有助于在不同的工作负载条件下更平滑地管理IO。

innodb_adaptive_hash_index: 此参数控制是否启用InnoDB的自适应哈希索引功能。当启用时,InnoDB会动态地为经常访问的B-tree索引创建哈希索引,以加快点查询的执行速度。不过,如果表结构频繁变化或者哈希冲突率较高,自适应哈希索引可能反而带来额外的开销。

innodb_adaptive_hash_index_parts: 自适应哈希索引的一个扩展选项,指定将自适应哈希索引划分为多少个部分,以支持多核并行处理,提高并发性能。更高的值意味着更多的并行性和潜在的更好的性能,但也可能消耗更多的内存。

innodb_adaptive_max_sleep_delay: 这个参数决定了InnoDB在检查点和其他后台操作之间等待的最长时间(以微秒为单位)。如果系统没有太多活动,InnoDB会休眠一段时间以减少空闲CPU周期的消耗。更大的数值意味着在低负载时InnoDB会等待更长的时间再进行下一个后台操作,这样可以在不影响性能的前提下节省资源。

IO Thread

innodb_read_io_threadsinnodb_write_io_threads 均默认为4

mysql> show variables like 'innodb_version' \G;
*************************** 1. row ***************************
Variable_name: innodb_version
        Value: 8.3.0
1 row in set (0.01 sec)

mysql> show variables like '%innodb%thread%' ;
+------------------------------+-------+
| Variable_name                | Value |
+------------------------------+-------+
| innodb_ddl_threads           | 4     |
| innodb_log_writer_threads    | ON    |
| innodb_parallel_read_threads | 4     |
| innodb_purge_threads         | 4     |
| innodb_read_io_threads       | 4     |
| innodb_thread_concurrency    | 0     |
| innodb_thread_sleep_delay    | 10000 |
| innodb_write_io_threads      | 4     |
+------------------------------+-------+
8 rows in set (0.00 sec)

SHOW ENGINE INNODB STATUS/G;

Purge Thread

回收undolog
innodb_purge_threads 默认为4

Page Cleaner Thread

脏页的刷新操作。

Checkpoint 技术

MySQL InnoDB 存储引擎采用了Checkpoint(检查点)技术来实现持久化和数据恢复,它主要负责将缓冲池(Buffer Pool)中的脏页(dirty pages)定期或在特定条件下刷回到磁盘,确保数据库系统的数据一致性以及在崩溃恢复时能尽快重启服务。
Checkpoint(检查点)技术的目的时解决一下几个问题:

  • 缩短数据库的恢复时间;
  • 缓冲池不够用时,将脏页刷新到磁盘;
  • 重做日志不可用时,刷新脏页。

对于 InnoDB存储引擎而言,其是通过 LSN(Log Sequence Number,日志序列号 )来标记版本的。而LSN是8字节的数字,其单位是字节。每个页有LSN,重做日志中也有LSN,Checkpoint 也有 LSN,可以通过命令 SHOW ENGINE INNODB STATUS 来观察;

mysql> SHOW ENGINE INNODB STATUS \G;
...
---
LOG
---
Log sequence number          32749227
Log buffer assigned up to    32749227
Log buffer completed up to   32749227
Log written up to            32749227
Log flushed up to            32749227
Added dirty pages up to      32749227
Pages flushed up to          32749227
Last checkpoint at           32749227
Log minimum file id is       9
Log maximum file id is       9
41 log i/o's done, 0.00 log i/o's/second
...

在 InnoDB 存储引擎中,Checkpoint 发生的时间、条件及脏页的选择等都非常复杂。而Checkpoint 所做的事情无外乎是将缓存池中的脏页刷回磁盘。不同之处在于每次刷新多少页到磁盘,每次从哪里取脏页,以及什么时间触发Checkpoint。在 InnoDB 存储引擎内部 Checkpoint 主要有两种类型:

Sharp Checkpoint(尖峰检查点)

  • Sharp Checkpoint 发生在数据库关闭时将所有的脏页都刷新回磁盘,这时默认的工作方式,即参数 innodb_fast_shutdown = 1
  • 但是若数据库在运行时也使用 Sharp Checkpoint,那么数据库的可用性就会受到很大的影响。故在InnoDB存储引擎内部使用Fuzzy Checkpoint进行页的刷新,即只刷新一部分脏页,而不是刷新所有的脏页回磁盘。

Fuzzy Checkpoint(模糊检查点)

Fuzzy Checkpoint 是一种在线检查点,它在数据库运行期间周期性发生,不强制刷新所有脏页,而是采用一种更细粒度的方式来平衡磁盘I/O与系统性能。这种检查点可以根据系统负载和预设的策略自动决定何时刷新部分脏页到磁盘,如基于重做日志文件切换、LRU算法淘汰出的脏页、或者每秒刷脏页的数量等条件触发。
在 InnoDB 存储引擎中可能发生如下几种情况的 Fuzzy Checkpoint:

Master Thread Checkpoint

  • 描述: Master Thread是InnoDB存储引擎中的一个重要后台线程,它负责定期执行多种后台任务,其中包括Checkpoint。在Master Thread的主循环中,会根据设定的策略周期性地发起Fuzzy Checkpoint。在该Checkpoint过程中,Master Thread会选择性地将一部分缓冲池中的脏页写入磁盘,而不是强制刷新所有脏页。选择的标准可以是基于时间间隔、脏页数量、重做日志文件切换等因素。
  • 目的: 通过这种方式,Master Thread可以持续地将脏页的负担分散到整个系统运行期间,减轻在特定时刻的I/O压力,并确保重做日志不至于过快填满,导致事务无法继续。

FLUSH_LRU_LIST Checkpoint

  • 描述: 当InnoDB的缓冲池使用LRU(Least Recently Used)算法进行管理时,最久未使用的页可能会被从缓冲池中淘汰出去,以保证有差不多100个空闲页可供使用。如果被淘汰的页是脏页(即已被修改但尚未写回磁盘),则在页被驱逐出缓冲池之前,会触发一个FLUSH_LRU_LIST Checkpoint,这时会把这部分脏页强制刷回磁盘。而从MySQL 5.6 版本开始,这个检查被放在了一个单独的 Page Cleaner 线程中进行,并且用户可以通过参数 innodb_lru_scan_depth 控制 LRU 列表中可用页的数量。
  • 目的: 保证即使在LRU替换过程中,也不会丢失任何已经提交事务的修改,并且维护了数据的一致性和完整性。
mysql> show variables like 'innodb_lru_scan_depth';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| innodb_lru_scan_depth | 1024  |
+-----------------------+-------+
1 row in set (0.00 sec)

Async/Sync Flush Checkpoint

  • 描述: Async/Sync Flush Checkpoint 指的是重做日志文件不可用的情况,这时需要强制将一些脏页刷新回磁盘,而此时脏页是从脏页列表中选取的。若将已经写入到重做日志的 LSN 记为 redo_lsn,将已经刷新回磁盘最新页的 LSN 记为 checkpoint_lsn,则可定义:
    checkpoint_age = redo_lsn - checkpoint_lsn
    再定义以下的变量:
    async_water_mark = 75% * total_redo_log_file_size
    sync_water_mark = 90% * total_redo_log_file_size
    若每个重做日志文件的大小为 1GB,并且定义了两个重做日志文件,则重做日志文件的总大小为 2GB。那么 async_water_mark=1.5GB sync_water_mark=1.8GB。则:
    • 当 checkpoint_age<async_water_mark 时,不需要刷新任何脏页到磁盘;
    • 当 async_water_mark<checkpoint_age<sync_water_mark 时触发 Async Flush,从 Flush 列表中刷新足够的脏页回磁盘,使得刷新后满足 checkpoint_age<async_water_mark;
    • checkpoint_age>sync_water_mark 这种情况一般很少发生,除非设置的重做日志文件太小,并且在进行类似 LOAD DATA 的 BULK INSERT 操作。此时触发 Sync Flush 操作,从 Flush 列表中刷新足够的脏页回磁盘;使得刷新后满足 checkpoint_age<async_water_mark。
  • 目的: 可见,Async/Sync Flush Checkpoint 是为了保证重做日志的循环使用的可用性。在 InnoDB 1.2.x 版本之前,Async Flush Checkpoint 会阻塞发现问题的用户查询线程,而 Sync Flush Checkpoint 会阻塞所有的用户查询线程,并且等待脏页刷新完成。从 InnoDB 1.2.x 版本开始——也就是 MySQL 5.6 版本,这部分的刷新操作同样放到了单独的 Page Cleaner Thread 中,故不会阻塞用户查询线程。

Dirty Page too much Checkpoint

  • 描述:
    如果缓冲池中的脏页数量过多(超过了InnoDB引擎预设的阈值),即使在Async Flush机制下,也可能触发一次Fuzzy Checkpoint,目的是尽快减少缓冲池中积压的脏页数量,以保持重做日志的滚动和数据库的持久性。

  • 目的:
    避免脏页累积过多导致重做日志不可用,进而引发事务无法提交或数据库不能正常运作的风险。通过适时的Fuzzy Checkpoint,可以确保即使在极端的写密集型负载下,系统也能保持良好的运行状态和数据安全性。

需要注意的是,随着时间推移和技术发展,MySQL/InnoDB的具体实现细节和配置选项可能会有所改变,但上述原则仍然适用于理解Fuzzy Checkpoint的基本原理和应用场景。

  • 脏页刷新:Checkpoint 过程中,InnoDB 需要将那些在缓冲池中修改过的页面(即脏页)的数据写回到磁盘上的数据文件,使得数据文件和缓冲池的内容一致。

  • 重做日志循环利用:为了防止重做日志无限增长,当重做日志文件达到一定大小或达到预先设定的阈值时,InnoDB 必须通过 Checkpoint 清理旧的事务记录,以便复用日志空间。

  • 恢复过程:在数据库启动时,InnoDB 通过最后一个 Checkpoint 记录的信息,找出需要应用哪些 REDO 日志记录来恢复到一致的状态。只需重放 Checkpoint 后的 REDO 日志,即可将数据库带至崩溃前的最新状态。

通过有效的 Checkpoint 策略,InnoDB 能够在保证数据完整性的同时,尽量减少对磁盘 I/O 的压力,提高数据库的整体性能和可靠性。此外,对于大容量的数据库系统来说,合理的 Checkpoint 设置还能显著缩短数据库在异常情况下的恢复时间。

InnoDB 关键特性

InnoDB提供了事务处理、崩溃修复能力和多版本并发控制的事务安全。以下是InnoDB存储引擎的关键特性:

插入缓冲(Insert Buffer)

插入缓冲是一种优化策略,用于提高非唯一索引的插入性能。当向辅助索引插入数据时,如果主键索引页在内存中不可用,InnoDB会将这些插入操作缓存起来,稍后在后台进行批量处理。这可以减少磁盘I/O操作,提高性能。

mysql> show variables like '%innodb_change%';
+-------------------------------+-------+
| Variable_name                 | Value |
+-------------------------------+-------+
| innodb_change_buffer_max_size | 25    |
| innodb_change_buffering       | all   |
+-------------------------------+-------+

innodb_change_buffer_max_size :这个参数表示InnoDB更改缓冲的最大使用比例。其值是一个百分比,默认为25%,范围在0到100之间。更改缓冲主要应用于非唯一二级索引的插入、删除和更新操作,当数据页不在缓冲池中时,这些操作会被缓存起来,而非立即写入磁盘上的对应索引页面。设置一个较高的百分比可以更有效地利用更改缓冲来减少随机I/O,提高插入性能,但同时会占用更多的内存资源。

innodb_change_buffering :此参数定义了InnoDB更改缓冲的行为模式。默认值“all”意味着对所有类型的更改操作(inserts、deletes、purges)都会启用更改缓冲。其他可能的取值包括“none”(完全禁用更改缓冲)、“inserts”(仅针对插入操作启用更改缓冲)。根据不同的数据库工作负载和需求,可以选择适合的更改缓冲策略以达到最佳性能平衡。

两次写(Double Write)

两次写是一种崩溃恢复机制,用于防止数据页损坏。当InnoDB将一个数据页从缓冲池刷新到磁盘时,首先会将整个数据页复制到共享表空间的内存区域,然后再执行实际的磁盘写操作。如果在写入过程中发生崩溃,InnoDB可以在重启时从共享表空间的内存区域重新加载数据页,从而避免数据丢失或损坏。

mysql> show variables like '%double%';
+-------------------------------+-------+
| Variable_name                 | Value |
+-------------------------------+-------+
| innodb_doublewrite            | ON    |
| innodb_doublewrite_batch_size | 0     |
| innodb_doublewrite_dir        |       |
| innodb_doublewrite_files      | 2     |
| innodb_doublewrite_pages      | 4     |
+-------------------------------+-------+

innodb_doublewrite :这是一个布尔型变量,表示是否启用InnoDB的双写缓冲(Double Write Buffer)功能。当设置为ON时,意味着InnoDB会开启一个特殊的区域用来在将数据页写入表空间前先进行预写操作,防止因电源故障或系统崩溃导致的数据页损坏问题。启用该功能能增加数据的一致性和完整性,但也可能略微降低写性能。

innodb_doublewrite_batch_size :这个参数决定了每次提交时批量写入到双写缓冲区的数据页数量。如果值为0,则表示默认行为,即没有特别指定批量大小,每一页都会单独进行预写。非零值可以尝试减少磁盘I/O次数,但实际应用中此参数的调整需谨慎,因为它可能会影响数据一致性及恢复速度。

innodb_doublewrite_dir :这是用于指定双写缓冲区文件存放路径的目录名。如果为空,那么双写缓冲区将会被存储在InnoDB系统的共享表空间中。如果提供了特定目录,则双写缓冲区会被创建在这个目录下,但通常情况下不建议用户直接修改此参数。

innodb_doublewrite_files :这个参数定义了双写缓冲区使用的临时文件数量。在预写过程中,InnoDB会循环使用这些文件来保存待写入主表空间的数据页副本。默认值2意味着有两组文件用于轮换,这有助于在并发写入较多的情况下提供足够的空间。

innodb_doublewrite_pages :该参数标识每个双写缓冲文件中包含的连续未分配页面数。这些页面用于存储即将写入磁盘的数据页副本。当达到这个阈值后,InnoDB将触发一次同步写入操作,将缓冲中的数据刷入磁盘。默认值4表明每个双写缓冲文件中有4个连续页面用于预写。

自适应哈希索引(Adaptive Hash Index)

自适应哈希索引是一种动态创建和销毁的索引,用于提高查询性能。当InnoDB检测到某些页上的哈希查找频繁发生时,会自动为这些页创建哈希索引。这可以将B+树索引转换为更高效的哈希索引,从而提高查询速度。

mysql> show variables like '%innodb_adaptive_hash%';
+----------------------------------+-------+
| Variable_name                    | Value |
+----------------------------------+-------+
| innodb_adaptive_hash_index       | ON    |
| innodb_adaptive_hash_index_parts | 8     |
+----------------------------------+-------+

innodb_adaptive_hash_index :这个参数控制InnoDB是否启用自适应哈希索引功能。当设置为ON时,InnoDB会根据查询的访问模式动态地为经常被访问的索引创建哈希索引结构,以提高点查询性能。通过在内存中建立一个哈希表,可以实现对索引记录的快速查找,减少磁盘I/O操作。如果禁用此功能(OFF),则不会自动创建哈希索引。

innodb_adaptive_hash_index_parts :该参数表示自适应哈希索引内部划分的分区数。自适应哈希索引在内部是可分割的,分成多个部分有助于在多核处理器环境下更好地进行并发处理,并减少锁竞争。将此值设置得较高可以提高在高并发场景下的哈希索引性能,但也会占用更多的内存资源。默认情况下,MySQL通常将其设置为一个合理的数值,以平衡性能和内存使用之间的关系。

异步IO(Async IO)

异步IO是一种I/O模型,允许InnoDB在执行磁盘I/O操作时继续处理其他任务。这意味着InnoDB可以同时处理多个I/O请求,从而提高并发性能和系统吞吐量。

mysql> show variables like '%innodb_%aio%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| innodb_use_native_aio | ON    |
+-----------------------+-------+

启用原生异步I/O可以减少数据库在等待I/O操作完成时产生的阻塞,特别是在高I/O负载或者大量数据需要存取的情况下,能够显著提升系统的响应速度和吞吐量。然而,并非所有操作系统都支持原生异步I/O,如果系统不支持(苹果的macOS),即使将此参数设置为ON,InnoDB也会自动回退到模拟异步I/O的方式。

刷新邻接页(Flush Neighbor Page)

刷新邻接页是一种优化策略,用于减少不必要的磁盘I/O操作。当InnoDB需要刷新一个数据页到磁盘时,会检查相邻的数据页是否也需要刷新。如果是,则会将这些相邻的数据页一起刷新,以减少磁盘I/O操作的数量。

mysql> show variables like '%innodb%neighbors%';
+------------------------+-------+
| Variable_name          | Value |
+------------------------+-------+
| innodb_flush_neighbors | 0     |
+------------------------+-------+

innodb_flush_neighbors 参数决定了当一个脏页被刷新到磁盘时,是否同时将邻近的脏页也一并写入。这个参数值可以是0或1。
- 0 表示关闭邻接页刷新功能,仅针对当前需要刷新的单个脏页进行I/O操作,这样可以减少不必要的I/O开销和延迟,提高数据库在高并发事务下的性能。
- 1 表示开启邻接页刷新功能,当刷新一个脏页时,会检查其周围的邻接页是否也是脏页,并且一并写入磁盘。这种做法理论上可以优化磁盘顺序读写的效率,降低磁头移动带来的寻道时间,但在某些场景下可能会增加I/O操作量,影响性能。

默认情况下,MySQL通常设置为0,以适应现代硬件的特性,特别是在固态硬盘(SSD)等存储设备上,由于没有机械寻道时间,所以通常不需要考虑邻接页刷新来优化顺序写入。

启动、关闭与恢复

innodb_fast_shutdown

该参数是MySQL InnoDB存储引擎中的一个配置项,它控制了在数据库关闭时InnoDB表空间的清理方式和速度。

mysql> show variables like '%innodb%fast%';
+----------------------+-------+
| Variable_name        | Value |
+----------------------+-------+
| innodb_fast_shutdown | 1     |
+----------------------+-------+

innodb_fast_shutdown 参数有三个可能的取值(0、1、2),默认为1:

  • 0 表示进行完全的慢速关闭。在这种模式下,InnoDB会在关闭前执行完整的清理和检查操作,包括刷新所有脏页到磁盘,以及对事务日志做完整校验等。这种方式最为安全,但关闭数据库所需的时间较长。如果在进行InnoDB 升级时,必须将这个参数调为0,然后再关闭数据库。
  • 1 表示进行快速关闭。在这个模式下,InnoDB会尽可能地将脏页写入磁盘,并跳过一些耗时的完整性检查步骤,但仍保证数据的一致性。这样可以在牺牲部分恢复时间的前提下,提高数据库关闭的速度。
  • 2 表示非常快的关闭。这种模式下,InnoDB不会确保所有的脏页都被刷到磁盘上,而是依赖于下次启动时的恢复机制来达到一致性状态。这通常会导致数据库重启时需要花费更多的时间来恢复未完成的事务和脏页。

因此,在实际应用中,根据业务需求权衡数据库关闭时间和安全性,可以选择合适的 innodb_fast_shutdown 值。如果更关心数据安全性和一致性,建议使用较低的数值;如果希望快速关闭数据库以减少维护窗口的时间,则可选择较高的数值,但需要注意在下次启动时可能会有额外的恢复开销。

innodb_force_recovery

该参数是MySQL InnoDB存储引擎中的一个关键变量,用于在数据库无法正常启动时强制InnoDB进入不同的恢复模式。

mysql> show variables like '%innodb%recovery%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| innodb_force_recovery | 0     |
+-----------------------+-------+

该参数的值通常介于0到6之间,每增加一级表示对数据库进行更严格、更保守的恢复处理。

  • innodb_force_recovery 设置为 0 时,这是默认值,意味着InnoDB将按照正常流程启动并尝试进行所有可能的数据恢复操作,包括回滚未完成事务、应用重做日志等。

  • 当设置 innodb_force_recovery 的值大于0时,InnoDB会采取一系列逐渐加强的措施来确保数据库能够启动,即使这意味着牺牲数据的一致性或完整性。随着该参数值的增加,InnoDB将禁用某些可能导致循环或错误的操作,如:

    • 1(SRV_FORCE_IGNORE_CORRUPT):忽略检查到的 corrupt 页。
    • 2(SRV_FORCE_NO_BACKGROUND):阻止 Master Thread 线程的运行,如 Master Thread 线程需要仅需 full purge 操作,而这会导致 crash。
    • 3(SRV_FORCE_NO_TRX_UNDO):不进行事务的回滚操作。
    • 4(SRV_FORCE_NO_IBUF_MERGE):不进行插入缓冲的合并操作。
    • 5(SRV_FORCE_NO_UNDO_LOG_SCAN):不查看撤销日志(Undo Log),InnoDB 存储引擎会将未提交的事务视为已提交。
    • 6(SRV_FORCE_NO_LOG_REDO):不进行前滚的操作。

需要注意的是,在设置了 innodb_force_recovery 大于0后,用户可以对表进行 select、create 和 drop 操作,但 insert、update 和 delete 这类 DML 操作时不允许的。

使用非零的 innodb_force_recovery 值是一种非常手段,主要用于紧急情况下的数据拯救,例如当数据库因各种原因(如硬件故障、数据损坏、不完整的事务等)无法正常启动时。启用这个参数可以帮助用户在不触发进一步问题的情况下启动数据库,并尽快导出重要数据以防止潜在数据丢失。 一旦数据库通过这种方式启动成功,应尽快备份重要数据,并在问题修复后立即将 innodb_force_recovery 参数还原为0,然后重启数据库恢复正常服务。

模拟故障

我们来做个实验,模拟故障的发生。在第一会话中,对一张接近1 000W行的InnoDB存储引擎表执行更新操作,但是完成后不要马上提交:

start transaction;
update Profile set password='';

–start transaction语句开启了事务,同时防止了自动提交的发生,update操作则会产生大量的回滚日志。这时,我们人为地kill掉MySQL数据库服务器。

ps-ef|grep mysqld
kill-9 pid

通过kill命令,我们人为地模拟了一次数据库宕机故障,当MySQL数据库下次启动时会对update的这个事务执行回滚操作,而这些信息都会记录在错误日志文件中,默认后缀名为err。如果查看错误日志文件,可得到如下结果:

090922 13:40:20 InnoDB:Started;log sequence number 6 2530474615
InnoDB:Starting in background the rollback of uncommitted transactions
090922 13:40:20 InnoDB:Rolling back trx with id 0 5281035,8867280 rows to undo
InnoDB:Progress in percents:1090922 13:40:20
090922 13:40:20[Note]/usr/local/mysql/bin/mysqld:ready for connections.
Version:'5.0.45-log'socket:'/tmp/mysql.sock'port:3306 MySQL Community
Server(GPL)
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
InnoDB:Rolling back of trx id 0 5281035 completed
090922 13:49:21 InnoDB:Rollback of non-prepared transactions completed

可以看到,如果采用默认的策略,即把innodb_force_recovery设为0,InnoDB会在每次启动后对发生问题的表执行恢复操作,通过错误日志文件,可知这次回滚操作需要回滚8 867 280行记录,总共耗时约9分多钟。

我们做再做一次同样的测试,只不过在启动MySQL数据库前将参数innodb_force_recovery设为3,然后观察InnoDB存储引擎是否还会执行回滚操作,查看错误日志文件,可看到:

090922 14:26:23 InnoDB:Started;log sequence number 7 2253251193
InnoDB:!innodb_force_recovery is set to 3!
090922 14:26:23[Note]/usr/local/mysql/bin/mysqld:ready for connections.
Version:'5.0.45-log'socket:'/tmp/mysql.sock'port:3306 MySQL Community
Server(GPL)

这里出现了“!”,InnoDB警告你已经将innodb_force_recovery设置为3,不会进行undo的回滚操作了。因此数据库很快启动完成,但是你应该很小心当前数据库的状态,并仔细确认是否不需要回滚操作。

参考博客:
https://blog.csdn.net/weixin_42653522/article/details/118807279
https://blog.csdn.net/weixin_45437022/article/details/121525939
https://blog.csdn.net/zhwxl_zyx/article/details/115717167

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

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

相关文章

力扣---反转链表 II ***

给你单链表的头指针 head 和两个整数 left 和 right &#xff0c;其中 left < right 。请你反转从位置 left 到位置 right 的链表节点&#xff0c;返回 反转后的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], left 2, right 4 输出&#xff1a;[1,4,3,…

【QT入门】 Qt代码创建布局综合运用:仿写腾讯会议登陆界面

往期回顾&#xff1a; 【QT入门】 Qt代码创建布局之水平布局、竖直布局详解-CSDN博客 【QT入门】 Qt代码创建布局之栅格布局详解-CSDN博客 【QT入门】 Qt代码创建布局之分裂器布局详解-CSDN博客 【QT入门】 Qt代码创建布局综合运用&#xff1a;仿写腾讯会议登陆界面 一、界面分…

rust 面向对象编程特性、模式与模式匹配、高级特征

面向对象编程OOP 学习了结构体、枚举&#xff0c;它们可以包含自定义数据字段&#xff0c;也可以定义内部方法&#xff0c;它们提供了与对象相同的功能。 面向对象的四大特征&#xff1a;封装、继承、多态 通过pub标记为公有的结构体&#xff0c;在其他模块中可以访问使用这…

redis主从复制与哨兵模式

redis主从复制 Redis主从复制&#xff08;Redis replication&#xff09;是Redis提供的一种数据备份和故障转移机制。通过主从复制&#xff0c;可以将一个Redis服务器&#xff08;主节点&#xff09;的数据复制到一个或多个Redis服务器&#xff08;从节点&#xff09;。这样做…

算法设计与分析实验报告java实现(排序算法、三壶谜题、交替放置的碟子、带锁的门)

一、 实验目的 1&#xff0e;加深学生对算法设计方法的基本思想、基本步骤、基本方法的理解与掌握&#xff1b; 2&#xff0e;提高学生利用课堂所学知识解决实际问题的能力&#xff1b; 3&#xff0e;提高学生综合应用所学知识解决实际问题的能力。 二、实验任务 1、排序算法…

ctf_show笔记篇(web入门---jwt)

目录 jwt简介 web345&#xff1a; web346&#xff1a; web347&#xff1a; web348: web349&#xff1a; web350&#xff1a; jwt简介 JSON Web Token&#xff08;JWT&#xff09;通常由三部分组成 Header&#xff08;头部&#xff09;&#xff1a;包含了两部分信息&…

使用 Clickhouse 集成的表引擎同步数据方式详解

Clickhouse作为一个列式存储分析型数据库&#xff0c;提供了很多集成其他组件的表引擎数据同步方案。 官网介绍 一 Kafka 表引擎 使用Clickhouse集成的Kafka表引擎消费Kafka写入Clickhouse表中。 1.1 流程图 1.2 建表 根据上面的流程图需要建立三张表&#xff0c;分别Click…

Jenkins 安装部署

1、安装下载 官网地址&#xff1a;Jenkins 下载 war 包 1、前置环境 JDK 环境&#xff08;根据 Jenkins 版本不同&#xff0c;需要的 JDK 版本不同&#xff0c;目前需要 JDK11 的版本来支持&#xff09;Maven maven 官网下载压缩包 &#xff0c;并将其传输到服务器&#xf…

一、OpenCV(C#版本)环境搭建

一、Visual Studio 创建新项目 二、选择Windows窗体应用&#xff08;.NET Framework&#xff09; 直接搜索模板&#xff1a;Windows窗体应用(.NET Framework) 记得是C#哈&#xff0c;别整成VB(Visual Basic)了 PS&#xff1a;若搜索搜不到&#xff0c;直接点击安装多个工具和…

【Linux】线程概念及线程互斥

目录 线程概念 线程优点 线程缺点 线程异常 线程系统编程接口 线程创建及终止 线程等待 使用线程系统接口封装一个小型的C线程库并实现一个抢票逻辑 线程互斥 互斥量的接口 线程互斥实现原理 使用系统加锁接口封装LockGuard 实现自动化加锁 线程安全和可重入函数 …

Win10文件夹共享(有密码的安全共享)(SMB协议共享)

前言 局域网内&#xff08;无安全问题&#xff0c;比如自己家里wifi&#xff09;无密码访问&#xff0c;参考之前的操作视频 【电脑文件全平台共享、播放器推荐】手机、电视、平板播放硬盘中的音、视频资源 下面讲解公共网络如办公室网络、咖啡厅网络等等环境下带密码的安全…

探寻马来西亚服务器托管的优势与魅力

随着全球跨境业务的不断增加&#xff0c;境外服务器成为越来越受欢迎的选择。在这其中&#xff0c;马来西亚服务器备受关注&#xff0c;其机房通常位于马来西亚首都吉隆坡。对于客户群体主要分布在东南亚、澳大利亚和新西兰等地区的用户来说&#xff0c;马来西亚服务器是一个理…

【opencv】教程代码 —ml (主成分分析、支持向量机、非线性支持向量机)

1. introduction_to_pca.cpp 主成分分析 /*** file introduction_to_pca.cpp* brief 这个程序演示了如何使用OpenCV PCA 提取物体的方向* author OpenCV团队*/// 包含OpenCV函数库所需要的头文件 #include "opencv2/core.hpp" #include "opencv2/imgproc.hpp&q…

JVM专题——内存结构

本文部分内容节选自Java Guide和《深入理解Java虚拟机》, Java Guide地址: https://javaguide.cn/java/jvm/memory-area.html &#x1f680; 基础&#xff08;上&#xff09; → &#x1f680; 基础&#xff08;中&#xff09; → &#x1f680;基础&#xff08;下&#xff09;…

Neo4j数据库(一)

目录 新建节点 Neo4j CQL创建具有属性的节点 多个标签到节点 单个标签到关系 MATCH命令 RETURN命令&#xff1a; Neo4j CQL - MATCH & RETURN匹配和返回 总结&#xff1a;本文介绍了Neo4j的CREATE&#xff0c;MATCH&#xff0c;RETURN的基本操作 新建节点 Neo4j创建一…

Coursera上托福专项课程02:TOEFL Speaking and Writing Sections Skills Mastery 学习笔记

TOEFL Speaking and Writing Sections Skills Mastery Course Certificate 本文是学习 https://www.coursera.org/learn/toefl-speaking-writing-sections-skills-mastery 这门课的学习笔记&#xff0c;如有侵权&#xff0c;请联系删除。 文章目录 TOEFL Speaking and Writing…

Unity 代码控制播放序列帧动画的实现

在Unity中有些应用场景的动画是通过序列帧实现的。 如下图即为一个英雄攻击的一个动画&#xff1a; 那么如何实现该序列帧动画的播放呢&#xff0c;我们可以通过代码来控制播放。 1、把以上序列帧导入编辑器中&#xff0c;并修改图片属性&#xff0c;如下图所示&#xff0c;其…

三菱上升沿和下降沿

1&#xff0c;上升沿 含义 上升沿是在接通的第一个周期执行。 2&#xff0c;下将沿 断开的第一个周期执行 M0 按下后Y0 亮 M1 松开后Y0灭

Docker 安装 Linux 系统可视化监控 Netdata

docker 安装 netdata 前提准备Docker 两种方式部署 Netdata1、使用 docker run 命令运行 netdata 服务2、使用 docker compose 运行 netdata 服务 Netdata 服务可视化界面Netdata 汉化处理 前提准备 说明&#xff1a;此处使用 windows11 安装的 docker desktop & wsl2/apli…

2. 如何让mybatis-plus的逻辑删除注解@TableLogic临时失效

文章目录 如何让mybatis-plus的逻辑删除注解TableLogic临时失效1. 场景复现1.1 controller代码1.2 service层代码1.3 entity代码 2. 问题分析3. 解决方案3.1 说明3.2 核心代码3.3 service方法对应修改为3.4 运行结果 如何让mybatis-plus的逻辑删除注解TableLogic临时失效 1. 场…