自学内容网 自学内容网

Mysql 架构

目录

1.1 Mysql 逻辑架构图

1.2 SQL 的执行流程

1.3 SQL 书写顺序和执行顺序

 1.4 Mysql 日志文件

1.4.1. 二进制日志(Binary Log)

1.4.2. 错误日志(Error Log)

1.4.3. 慢查询日志(Slow Query Log)

1.4.4. 查询日志(General Log)

1.4.5. 重做日志(Redo Log)

1.4.6. 回滚日志(Undo Log)

1.4.7. 中继日志(Relay Log)

2.1 InnoDB架构图

2.2 InnoDB SQL 执行流程

2.3 InnoDB 内存结构 之 Buffer Pool 缓冲池

2.4 InnoDB 内存结构 之Change Buffer 缓冲池

2.5 InnoDB 内存结构 之 Adaptive Hash Index 自适应索引

 2.6 InnoDB 内存结构 之 Log Buffer 日志缓冲

2.7  磁盘文件之表空间

2.7.1 系统表空间

2.7.2 独立表空间

2.7.3  通用表空间

2.7.4  Undo 表空间

2.7.5  临时表空间

2.8 磁盘文件之存储结构

2.8.1 段【Segment】

2.8. 2 区【Extent】

2.8. 3 页【Page】

2.8. 4 行【Row】

2.9 内存数据落盘

2.9.1 整体思路分析

2.9.2 脏页落盘

2.9.3 Redo日志落盘

2.9.4  CheckPoint检查点机制

2.9.5 Double Write双写


1.1 Mysql 逻辑架构图

  • Connectors连接器:负责跟客户端建立连接
  • Management Serveices & Utilities系统管理和控制工具
  • Connection Pool连接池:管理用户连接,监听并接收连接的请求,转发所有连接的请求到线程管理模块
  • SQL Interface SQL接口:接受用户的SQL命令,并且返回SQL执行结果
  • Parser解析器:SQL传递到解析器的时候会被解析器验证和解析
  • Optimizer查询优化器:SQL语句在查询之前会使用查询优化器对查询进行优化,explain语句查看SQL语句执行计划,就是由此优化器生成
  • CacheBuffer查询缓存:MySQL5.7中包含缓存组件。在MySQL8中移除了
  • Pluggable Storage Engines存储引擎:存储引擎就是存取数据、建立与更新索引、查询数据等技术的实现方法

MySQL的三层结构包括:

连接层:负责与MySQL客户端之间的通信,提供如连接处理,身份验证等功能。
服务层:在MySQL数据库系统处理底层数据之前的所有工作,都是在服务层完成的。包括权限判断,SQL 解析,行计划优化,query cache的处理以及所有内置的函数(如日期,时间,数学运算,加密)等等。
引擎层:负责存储和获取所有存储在MySQL中的数据。

1.2 SQL 的执行流程

1.3 SQL 书写顺序和执行顺序

 

 1.4 Mysql 日志文件

MySQL中的日志文件是MySQL数据库系统的重要组成部分,它们记录了数据库的运行情况、用户操作、错误信息等,对于数据库的维护、优化、故障排查和恢复都具有重要意义。以下是MySQL中几种主要日志文件的详解:

1.4.1. 二进制日志(Binary Log)

功能:记录所有对数据库所做的更改操作,包括插入、更新和删除记录等。二进制日志的主要用途包括数据恢复、数据备份和复制。
重要性:通过二进制日志,可以将以前提交的更改应用到数据库以恢复数据,或者将数据从一个MySQL服务器复制到另一个MySQL服务器,实现数据分布和备份。
配置:默认情况下,二进制日志是启用的。可以通过修改MySQL配置文件(如my.cnf或my.ini)来设置更多参数,如日志文件的位置、大小、保留天数等。

 默认关闭,需要通过以下配置进行开启。binlog记录了数据库所有的ddl语句和dml语句,但不包括 select语句内容,语句以事件的形式保存,描述了数据的变更顺序,binlog还包括了每个更新语句的执行时间信息。如果是DDL语句,则直接记录到binlog日志,而DML语句,必须通过事务提交才能记录到binlog日志中。 binlog主要用于实现mysql主从复制、数据备份、数据恢复。

配置中 mysql-bin binlog 日志文件的 basename, binlog 日志文件的完整名称: mysql-bin.000001
开启方式:
#服务ID
server_id=42
#binlog 配置 只要配置了log_bin地址 就会开启
log-bin=mysql-bin
1.4.2. 错误日志(Error Log)

功能:记录MySQL服务器在启动过程和运行过程中的错误信息,如启动失败、连接失败等。它是排查问题和调试的重要工具。
重要性:错误日志包含了重要的系统信息和警告,如MySQL服务器启动和关闭的详细信息、访问限制、网络连接中的错误等,这些信息对于理解所面临的问题并修复问题至关重要。
位置:默认情况下,错误日志被记录在MySQL安装目录下的data文件夹下的hostname.err文件中(其中hostname是主机名)。

默认开启,错误日志记录了运行过程中 遇到的所有严重的错误信息 ,以及 MySQL 每次启动和关闭的详细 信息 。错误日志所记录的信息是可以通过 log_error log_warnings 配置 来定义的。从 5.5.7 以后不能关闭错误日志。
  • log_error:指定错误日志存储位置
  • log-warnings:是否将警告信息输出到错误日志中。
    • log_warnings 0, 表示不记录告警信息。
    • log_warnings 1, 表示告警信息写入错误日志。
    • log_warnings 大于1, 表示各类告警信息,例如:有关网络故障的信息和重新连接信息写入 错误日志。
1.4.3. 慢查询日志(Slow Query Log)

功能:记录所有执行时间超过指定时间阈值的查询语句。这个阈值通常由管理员设置。
重要性:慢查询日志主要用于查找慢查询,以便更好地优化查询。通过分析慢查询日志,可以生成SQL语句报告,找出需要进行优化操作的查询语句,从而实现MySQL服务器的最佳性能。
配置:默认情况下,慢查询日志是禁用的。可以通过修改MySQL配置文件来启用慢查询日志,并设置阈值和日志文件的位置。

默认关闭, 通过以下设置开启。记录执行时间超过 long_query_time 秒的所有查询,便于收集查询时间 比较长的 SQL 语句。
配置慢查询开启
# 开启慢查询日志
slow_query_log=ON
# 慢查询的阈值,单位秒
long_query_time=10
# 日志记录文件
# 如果没有给出file_name值, 默认为主机名,后缀为-slow.log。
# 如果给出了文件名,但不是绝对路径名,文件则写入数据目录。
slow_query_log_file=slow_query_log.log
1.4.4. 查询日志(General Log)

功能:记录MySQL服务器上的所有查询操作,包括数据库连接、查询和断开连接操作等。
重要性:查询日志通常用于诊断数据库性能问题。它记录了所有查询的详细信息,包括MySQL服务器接收到的所有数据库客户端请求。
配置:默认情况下,查询日志是禁用的。可以通过修改MySQL配置文件来启用查询日志,并设置日志文件的位置和记录的详细程度。

默认关闭, 由于通用查询日志会记录用户的所有操作,其中还包含增删查改等信息,在并发操作大的环境下会产生大量的信息从而导致不必要的磁盘 IO ,会影响 MySQL 的性能的。 如果不是为了调试数据库,不建议开启查询日志。
开启方式:
#启动开关
general_log={ON|OFF}
#日志文件变量,而general_log_file如果没有指定,默认名是host_name.log
general_log_file=/var/lib/mysql/host_name.log
1.4.5. 重做日志(Redo Log)

功能:重做日志是保证事务持久性的重要机制。它记录了对数据库所做的物理修改,以确保在发生故障时能够恢复数据。
特点:重做日志是物理日志,记录的是“在某个数据页上做了什么修改”。它是循环写入固定的文件,且是顺序写入磁盘的。

innodb_log_buffer_size:设置redo log buffer大小参数,默认16M ,最大值是4096M,最小值为1M。

innodb_log_group_home_dir:设置redo log文件存储位置参数,默认值为"./",即innodb数据文件存储位置,其中的 ib_logfile0 和 ib_logfile1 即为redo log文件.

innodb_log_files_in_group:设置redo log文件的个数,命名方式如: ib_logfile0, iblogfile1... iblogfileN。默认2个,最大100个.

innodb_log_file_size:设置单个redo log文件大小,默认值为48M。最大值为512G,注意最大值指的是整个 redo log系列文件之和,即(innodb_log_files_in_group * innodb_log_file_size)不能大于最大值512G

innodb_flush_log_at_trx_commit 配置详解:
  • 0:事务提交时,不会立即把 log buffer里的数据写入到redo log日志文件的。而是等待主线程 每秒写入一次。
    • 如果MySQL崩溃或者服务器宕机,此时内存里的数据会全部丢失,最多会丢失1秒的事务。
    • 写入效率最高,但是数据安全最低;
  • 1:每次事务提交时,会将数据将从log buffer写入redo日志文件与文件系统缓存,并同时 fsync刷新到磁盘中。
    • 系统默认配置为1MySQL崩溃已经提交的事务不会丢失,要完全符合ACID必须使用默认设1
    • 写入效率最低,但是数据安全最高;
  • 2:事务提交时,也会将数据写入redo日志文件与文件系统缓存,但是不会调用fsync,而是让操作系统自己去判断何时将缓存写入磁盘。
    • 事务提交都会将数据刷新到操作系统缓冲区,可以认为是已经持久化到磁盘,但没有真正意义上持久化到磁盘。
    • 如果MySQL崩溃已经提交的事务不会丢失。但是如果服务器宕机或者意外断电,操作系统缓 存内的数据会丢失,所以最多丢失1秒的事务。
设置为 1 时,安全性最好但写入效率比较低。如果没有设置为 1 ,是无法满足 ACID D 持久性由于 MySQL执行刷新操作 fsync() 是阻塞的,所以磁盘刷新速度比较慢,如果开启了 1MySQL 性能会下降。 0 是性能 最好的模式,但是会存在丢失数据的风险。 2 是介于 1 0 之间的一个选择,数据的安全性会依赖于操作 系统是否稳定。在配置这一项的时候需要慎重考虑。
1.4.6. 回滚日志(Undo Log)

功能:回滚日志用于撤销事务或数据库崩溃时未提交的事务对数据库产生的影响。它保存了事务发生之前的数据版本,以支持事务的回滚和多版本并发控制(MVCC)。
特点:回滚日志是逻辑格式的日志,执行undo操作时,仅仅是将数据从逻辑上恢复至事务之前的状态,而不是从物理页面上操作实现的。
 

1.4.7. 中继日志(Relay Log)

功能:中继日志在MySQL复制架构中使用,从服务器用于从主服务器中接收二进制日志的事件,并将其记录在中继日志中,以便稍后重播这些事件来更新从服务器的数据。

MySQL的存储引擎之InnoDB

2.1 InnoDB架构图

InnoDB 内存结构主要分为如下四个区域:
1. Buffer Pool 缓冲池
2. Change Buffer 修改缓冲
3. Adaptive Hash Index 自适应索引
4. Log Buffer 日志缓冲
InnoDB 磁盘结构主要分为如下区域:
1.系统表空间(System Tablespace
2.独立表空间(File-per-table Tablespace
3.通用表空间(General Tablespace
4.回滚表空间(Undo Tablespace
5.临时表空间(The Temporary Tablespace
6.重做日志 (Redo Log)

2.2 InnoDB SQL 执行流程

2.3 InnoDB 内存结构 之 Buffer Pool 缓冲池

       缓冲池 Buffer Pool 用于加速数据的访问和修改,通过将热点数据缓存在内存的方法,最大限度地减少磁 IO,加速热点数据读写。

  • 默认大小为128MBuffer Pool 中数据以页为存储单位,其实现的数据结构是以页为单位的单链
  • 由于内存的空间限制,Buffer Pool 仅能容纳最热点的数据。
  • Buffer Pool 使用LRU算法(Least Recently Used 最近最少使用)淘汰非热点数据页。
    • LRU:根据页数据的历史访问来淘汰数据,如果数据最近被访问过,那么将来被访问的几率也更高,优先淘汰最近没有被访问到的数据。
  • 对于 Buffer Pool 中数据的查询,InnoDB 直接读取返回。对于 Buffer Pool 中数据的修改InnoDB 直接在 Buffer Pool 中修改,并将修改写入redo log
# 查看innodb存储引擎状态,包含缓冲池、修改缓冲、自适应哈希状态信息、日志缓冲等信息...
mysql> show engine innodb status;
# 查看InnoDB的Buffer Pool大小
mysql> show variables like 'innodb_buffer_pool_size';

2.4 InnoDB 内存结构 之Change Buffer 缓冲池

        change buffer 是一个用来存储不在 buffer pool 中的二级索引页的特殊数据结构。通常 DML 操作(增,删,改等操作)发生时会将修改过的索引数据页存放到 change buffer 中,而后这些索引页需要被 buffer pool 读取时,将合并加载到 buffer pool 中。Change Buffer 用于加速非热点数据中二级索引的写入操作.因为二级索引数据的不连续性,导致 修改二级索引时需要进行频繁的磁盘 IO 消耗大量性能,Change Buffer 缓冲对二级索引的修改操作,同时将写操作录入 redo log 中,在缓冲到一定量或系统较空闲时进行 merge 操作将修改写入磁盘中。

        在MySQL5.5之前,只针对insert做了优化,叫插入缓冲(insert buffer),后面进行了优化,对delete 和 update 也有效,叫做写缓冲(change buffer).

        Change Buffer 大小默认占 Buffer Pool 25%,最大50%,在引擎启动时便初始化完成。其物理结构为一棵名为 ibuf B Tree

       change buffer 是 buffer pool 中的一部分,虽然 Change Buffer 的名字叫做 buffer。但是他是可以进行持久化的,在右边的磁盘中的 System Tablespace (系统表空间 -- idbata1)可以看到持久化 Change Buffer 的空间以及把这个 Change Buffer 页 的改动记录在 redo log 里。

触发 Change Buffer 持久化操作有以下几种情况:

  • 数据库空闲时,后台有线程定时持久化
  • 数据库缓冲池不够用时。
  • 数据正常关闭时
  • redo log 写满时

从图上可以看到 Change Buffer 的功能,Change Buffer 中的数据最终还是会刷回到数据原始数据页中。把 Change Buffer 应用到原始的数据页,得到新的数据页的过程称之为merge。merge 过程中 只会将 Change Buffer 中与原始数据页有关的数据应用到原始数据页上。 

触发 merge 操作有以下几种情况:

  • 原始数据页加载到buffer pool时(访问数据页会触发merge)
  • 后台线程定时merge操作
  • MySQL 数据库正常关闭时

2.5 InnoDB 内存结构 之 Adaptive Hash Index 自适应索引

        自适应哈希索引 Adaptive Hash Index AHI 用于实现对于热数据页的一次查询。 是建立在索引之上的索引!使用聚簇索引进行 数据页 定位的时候需要根据索引树的高度从根节点走到叶子节点, 通常需要 3 4 次查询才能定位到数据 InnoDB 根据对索引使用情况的分析和索引字段的分析,通过自调优 Self-tuning 的方式为索引页建立或者删除哈希索引。
        AHI 的大小为 Buffer Pool 1/64 ,在 MySQL 5.7 之后支持分区,以减少对于全局 AHI 锁的竞争,默认 分区数为 8
        AHI 所作用的目标是 频繁查询的数据页和索引页 ,而由于数据页是聚簇索引的一部分,因此 AHI 是建立 在索引之上的索引, 对于二级索引,若命中 AHI ,则将直接从 AHI 获取二级索引页的记录指针,再根据 主键沿着聚簇索引查找数据;若聚簇索引查询同样命中 AHI ,则直接返回目标数据页的记录指针,此时 就可以根据记录指针直接定位数据页
# 查看innodb存储引擎状态,包含自适应哈希状态信息
mysql> show engine innodb status;
# 查看是否开启自适应哈希配置,默认是开启的
mysql> show variables like 'innodb_adaptive_hash_index';

 2.6 InnoDB 内存结构 之 Log Buffer 日志缓冲

       InnoDB 使用 Log Buffer 来缓冲日志文件的写入操作。内存写入加上日志文件顺序写的特点,使得InnoDB 日志写入性能极高。

        对于任何修改操作,都将录入诸如 redo log undo log 这样的日志文件中,因此日志文件的写入操作 非常频繁,却又十分零散。这些文件都存储在磁盘中,因此日志记录将引发大量的磁盘 IO Log Buffer 将分散的写入操作放在内存中,通过定期批量写入磁盘的方式提高日志写入效率和减少磁盘 IO

2.7  磁盘文件之表空间

2.7.1 系统表空间

        系统表空间是 InnoDB 数据字典、双写缓冲、修改缓冲和回滚日志的存储位置,如果关闭独立表空间, 它将存储所有表数据和索引。

        它默认下是一个初始大小 12MB、名为 ibdata1 的文件,系统表空间所对应的文件由 innodb_data_file_path 定义。         

        指定系统表空间文件自动增长后,其增长大小由 innodb_autoextend_increment 设置(默认为 64MB)且不可缩减,即使删除系统表空间中存储的表和索引,此过程释放的空间仅仅是在表空间文件 中标记为已释放而已,并不会缩减其在磁盘中的大小。

数据字典(Data Dictionary): 数据字典是由各种表对象的元数据信息(表结构,索引,列信息 等)组成的内部表

双写缓冲(Doublewrite Buffer):双写缓冲用于保证写入磁盘时页数据的完整性,防止发生部 分写失效问题。

修改缓冲(Change Buffer): 内存中 Change Buffer 对应的持久化区域

回滚日志(Undo Log):实现事务进行 回滚 操作时对数据的恢复。是实现多版本并发控制 (MVCC)重要组成。

2.7.2 独立表空间

        独立表空间用于存放每个表的数据和索引。其他类型的信息,如:回滚日志、双写缓冲区、系统事务信 息、修改缓冲等仍存放于系统表空间内。因此即使用了独立表空间,系统表空间也会不断增长。在5.7版 本中默认开启

        开启独立表空间(File-per-table TableSpace)( innodb_file_per_table=ON )之后,InnoDB 会为 每个数据库单独创建子文件夹,数据库文件夹内为每个数据表单独建立一个表空间文件 table.ibd 。 同时创建一个 table.frm 文件用于保存表结构信息。

        每个独立表空间的初始大小是 96KB。

2.7.3  通用表空间

        通用表空间(General Tablespace)是一个由 CREATE TABLESPACE 命令创建的共享表空间,创建时必 须指定该表空间名称和 ibd 文件位置,ibd 文件可以放置于任何 MySQL 有权限的地方。该表空间内可以 容纳多张数据表,同时在创建时可以指定该表空间所使用的默认引擎。

        通用表空间存在的目的是为了在系统表空间与独立表空间之间作出平衡。系统表空间与独立表空间中的 表可以向通用表空间移动,反之亦可,但系统表空间中的表无法直接与独立表空间中的表相互转化。

2.7.4  Undo 表空间

Undo TableSpace 用于存放一个或多个 undo log 文件。默认 undo log 存储在系统表空间中,MySql 5.7中支持自定义 Undo log 表空间并存储所有 undo log。一旦用户定义了 Undo Tablespace,则系统 表空间中的 Undo log 区域将失效。对于 Undo Tablespace 的启用必须在 MySQL 初始化前设置, Undo Tablespace 默认大小为 10MB。Undo Tablespace 中的 Undo log 表可以进行 truncate 操作。

2.7.5  临时表空间

        MySQL 5.7 之前临时表存储在系统表空间中,这样会导致 ibdata 在使用临时表的场景下疯狂增长。5.7 版本之后 InnoDB 引擎从系统表空间中抽离出临时表空间(Temporary Tablespace),用于独立保存 临时表数据及其回滚信息。该表空间文件路径由 innodb_temp_data_file_path 指定,但必须继承 innodb_data_home_dir 。

2.8 磁盘文件之存储结构

2.8.1 段【Segment】

        表空间由各个段(Segment)组成,创建的段类型分为数据段、索引段、回滚段等。由于 InnoDB 采用 聚簇索引与 B+ 树的结构存储数据,所以事实上数据页和二级索引页仅仅只是 B+ 树的叶子节点,因此数 据段称为 Leaf node segment,索引段其实指的是 B+ 树的非叶子节点,称为 Non-Leaf node segment。一个段会包含多个区,至少会有一个区,段扩展的最小单位是区。

  • 数据段称为 Leaf node segment
  • 索引段称为 Non-Leaf node segment
2.8. 2 区【Extent】

区(Extend)是由连续的页组成的空间,大小固定为 1MB,由于默认页大小为 16K,因此一个区默认 存储 64 个连续的页。如果页大小调整为 4K,则 256 个连续页组成一个区。为了保证页的连续性, InnoDB 存储引擎会一次从磁盘申请 4 ~ 5 个区。

2.8. 3 页【Page】

页(Page)是 InnoDB 的基本存储单位,每个页大小默认为 16K,从 InnoDB1.2.x 版本开始,可通过 设置 innodb_page_size 修改为 4K、8K、16K。InnoDB 首次加载后便无法更改。

# 查看MySQL页大小
show variables like 'innodb_page_size';

MySQL规定一个页上最少存储2个数据项。如果向一个页插入数据时,这个页已经满了,就会从区中分 配一个新页。如果向索引树叶子节点中间的一个页中插入数据,如果这个页是满的,就会发生页分裂。

操作系统读写磁盘最小单位也是页,当然此页非毕页!Linux的页一般是4K,通过命令查看:

# 默认 4096 4K
getconf PAGE_SIZE

由此可知,InnoDB从磁盘中读取一个数据页,操作系统会分4次从磁盘文件中读取数据到内存。写入也 是一样的,需要分4次从内存写入到磁盘中。

2.8. 4 行【Row】

InnoDB的数据是以行为单位存储的,1个页中包含多个行。在MySQL5.7中,InnoDB提供了4种行格 式:Compact、Redundant、Dynamic和Compressed行格式,Dynamic为MySQL5.7默认的行格式。

创建表时可以指定行格式:

# 查看MySQL页大小

CREATE TABLE t1 (c1 INT) ROW_FORMAT=DYNAMIC;

#修改表的行格式

ALTER TABLE tablename ROW_FORMAT=行格式名称;

#修改默认行格式

SET GLOBAL innodb_default_row_format=DYNAMIC;

#查看表行格式

SHOW TABLE STATUS LIKE 't1'; 

InnoDB行格式官网:https://dev.mysql.com/doc/refman/5.7/en/innodb-row-format.html

2.9 内存数据落盘

2.9.1 整体思路分析

在数据库中进行读取操作,将从磁盘中读到的页放在缓冲区中,下次再读相同的页时,首先判断该页是 否在缓冲区中。若在缓冲区中,称该页在缓冲区中被命中,直接读取该页。否则读取磁盘上的页。

对于数据库中页的修改操作,则首先修改在缓冲区中的页,然后再以一定的频率刷新到磁盘上。页从缓 冲区刷新回磁盘的操作并不是在每次页发生更新时都触发,而是通过一种称为CheckPoint的机制刷新 回磁盘。

数据库灵魂三问:

① 写入性能如何保证?

分散写入操作放在内存中,通过定期批量写入磁盘的方式提高写入效率减少磁盘 IO。

② 如何持久化?

也就是修改后的数据如何到磁盘中去。内存里缓冲池中的数据页要完成持久化通过两个 流程来完成

  • 通过CheckPoint机制进行脏页落盘
  • 日志先行,所有操作前先写Redo日志

③ 数据安全性怎么保证?

  • 记录操作日志:Force Log at Commit机制与Write Ahead Log(WAL)策略
  • CheckPoint机制
  • Double Write机制
2.9.2 脏页落盘

什么是脏页?

        对于数据库中页的修改操作,则首先修改在缓冲区中的页,缓冲区中的页与磁盘中的页数据不一致,所 以称缓冲区中的页为脏页。然后再以一定的频率将脏页刷新到磁盘上。页从缓冲区刷新回磁盘的操作并 不是在每次页发生更新时触发,而是通过一种称为CheckPoint的机制刷新回磁盘。

为什么不是每次更新直接写入磁盘呢?

  • 如果每次一个页发生变化就进行落盘,每次落盘一个页,必然伴随着4次IO操作,那么性能开销会 非常大。而且这个开销是随着写入操作的增加指数级增长的!
  • 如果数据长期在内存中保存,那么数据就存在安全性风险!
  • InnoDB采用了Write Ahead Log(WAL)策略和Force Log at Commit机制实现事务级别下数据的 持久性。
    • Force Log at Commit机制:当事务提交时,所有事务产生的日志都必须刷到磁盘。如果日 志刷新成功后,缓冲池中的数据刷新到磁盘前数据库发生了宕机,那么重启时,数据库可以从 日志中恢复数据。这样可以保证数据的安全性
    • Write Ahead Log(WAL)策略:要求数据的变更写入到磁盘前,首先必须将内存中的日志 写入到磁盘;InnoDB 的 WAL(Write Ahead Log)技术的产物就是 redo log,对于写操 作,永远都是日志先行,先写入 redo log 确保一致性之后,再对修改数据进行落盘。
    • 说白了保证数据的持久性与安全性我们采用记录日志的方式,那么也就是说,日志安全了,数 据就安全了!

怎么确保日志就能安全的写入系统呢?

为了确保每次日志都写入到redo日志文件,在每次将redo日志缓冲写入redo日志后,调用一次 fsync操作,将缓冲文件从文件系统缓存中真正写入磁盘。

这样做不就等同于数据直接写入磁盘吗?

  • redo日志不会记录完整的一页数据,因为这样日志太大,它只会记录那次(sequence)如何操作 了(update,insert)哪页(page)的哪行(row)
  • 日志是顺序写入,而数据是随机写入。顺序写入效率更高
  • 日志也不是改一条写一条,而是采用redo 日志落盘策略来兼顾安全性与性能!
  • 可以通过 innodb_flush_log_at_trx_commit 来控制redo日志刷新到磁盘的策略。 
2.9.3 Redo日志落盘

Log Buffer写入磁盘的时机由参数 innodb_flush_log_at_trx_commit 控制,此属性控制每次事务提交时 InnoDB的行为。是InnoDB性能调优的一个基础参数,涉及InnoDB的写入性能和数据安全性。默认1, 表示事务提交后立即落盘。

# 查看写入时机参数配置
show VARIABLES like 'innodb_flush_log_at_trx_commit';
2.9.4  CheckPoint检查点机制

1. 什么是CheckPoint?

Checkpoint要做的事情是将缓冲池中的脏页数据刷到磁盘上。CheckPoint决定了脏页落盘的时机、条件 及脏页的选择,不同的CheckPoint做法并不相同。

2. CheckPoint要解决什么问题?

1). 脏页落盘:避免数据更改直接操作磁盘

2). 缩短数据库的恢复时间:当数据库发生宕机时,数据库不需要重做所有的日志,因为Checkpoint之 前的页都已经刷新回磁盘。数据库只需对Checkpoint后的redo日志进行恢复,这样就大大缩短了 恢复的时间。

3. 缓冲池不够用时,将脏页刷新到磁盘:当缓冲池不够用时,根据LRU算法会溢出最近最少使用的 页,若此页为脏页,那么需要强制执行Checkpoint,将脏页也就是页的新版本刷回磁盘。

4. redo日志不可用时,刷新脏页:当redo日志出现不可用时,Checkpoint将缓冲池中的页至少刷新 到当前redo日志的位置

        1)). 数据库系统对redo日志的设计都是循环使用的,并不是让其无限增大的。

        2)). 如果当数据库宕机恢复操作时,不需要redo日志中的部分redo日志,这部分就可以被覆盖重 用。

        3)). InnoDB通过LSN(Log Sequence Number)来标记日志刷新的版本。LSN是8字节的数字, 每个页有LSN,redo日志中也有LSN,Checkpoint也有LSN。

可以通过命令 SHOW ENGINE INNODB STATUS 来观察:

 mysql> show engine innodb status;

3. Checkpoint分类

在InnoDB存储引擎内部,有两种Checkpoint,分别为:Sharp Checkpoint、Fuzzy Checkpoint

  • sharp checkpoint:在关闭数据库的时候,将buffer pool中的脏页全部刷新到磁盘中。
  • fuzzy checkpoint:数据库正常运行时,在不同的时机,将部分脏页写入磁盘。仅刷新部分脏页到 磁盘,也是为了避免一次刷新全部的脏页造成的性能问题。

Fuzzy Checkpoint:默认方式,只刷新一部分脏页,不是刷新所有脏页;

主要有以下几种情况:

  • Master Thread Checkpoint :在Master Thread中,会以每秒或者每10秒一次的频率,将部分 脏页从内存中刷新到磁盘,这个过程是异步的。正常的用户线程对数据的操作不会被阻塞。
  • FLUSH_LRU_LIST Checkpoint:缓冲池不够用时,根据LRU算法会淘汰掉最近最少使用的页,如 果该页是脏页的话,会强制执行CheckPoint,将该脏页刷回磁盘(由Page Cleaner Thread完 成);
  • Async/Sync Flush Checkpoint:重做日志不可用的情况,需要强制从脏页列表中选取一些脏页 刷盘(由Page Cleaner Thread完成)。由于磁盘是一种相对较慢的存储设备,内存与磁盘的交互 是一个相对较慢的过程。innodb_log_file_size定义的是一个相对较大的值,正常情况下,由前面两 种checkpoint刷新脏页到磁盘,在前面两种checkpoint刷新脏页到磁盘之后,脏页对应的redo log 空间随即释放,一般不会发生Async/Sync Flush checkpoint。 1 mysql> show engine innodb status;
  • Dirty Page too much:即脏页数量太多,导致强制进行Checkpoint。由参数 innodb_max_dirty_pages_pt 来控制,默认75(即75%)。当脏页数量占据75%缓冲池时,刷新 一部分脏页到磁盘。(由Page Cleaner Thread完成)
show variables like 'innodb_max_dirty_pages_pct';
2.9.5 Double Write双写

1. 脏页落盘出现的问题:写失效

我们知道脏页会在某些场景下进行刷盘,将缓冲池内的脏页数据落地到磁盘。因为存储引擎缓冲池内的 数据页大小默认为16KB,而文件系统一页大小为4KB,所以在进行刷盘操作时,就有可能发生如下场 景:

如图所示,数据库准备刷新脏页时,将16KB的刷入磁盘,但当写入了8KB时,就宕机了这种情况被称为 写失效(partial page write)。

2. 怎么解决? 

Doublewrite其实就是写两次,解决写失效问题,需要用到Doublewrite机制,简单来说就是在redo日 志前,对需要写入的页的做个副本,当写失效发生时,通过页的副本来还原该页再重做,这就是所谓的 double write。写失效后redo日志也是无法进行恢复的,因为redo日志记录的是对页的物理修改。

Double Write分两个部分:

  • 内存中的Doublewrite buffer,大小为2MB
  • 磁盘上的Doublewrite buffer,大小为2MB,连续的128个页,相当于两个extent

Double write脏页刷新流程:

        1. 首先复制:脏页刷新时不直接写磁盘,而是先将脏页复制到内存的Doublewrite buffer

        2. 再顺序写:内存的Doublewrite buffer分两次,每次1MB顺序地写入共享表空间的物理磁盘上,会 立即调用fsync函数同步OS缓存到磁盘中,顺序写性能好

        3. 最后离散写:内存的Doublewrite buffer最后将页写入各自表空间文件中,离散写较顺序写入差一 些 

3. Double write崩溃恢复

如果脏页数据未来得及落盘,系统就奔溃了,直接应用redo日志重新执行脏页落盘。 如果操作系统在将页写入磁盘的过程中发生了崩溃,其恢复过程如下:

        1. 首先InnoDB存储引擎从系统表空间中的Double write中找到该页的一个副本

        2. 然后将其复制到独立表空间

        3. 最后清楚redo日志,完成数据恢复 


原文地址:https://blog.csdn.net/weixin_46203834/article/details/142377401

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!