科普文:软件架构数据库系列之【MySQL存储引擎InnoDB】

388 篇文章 1 订阅
124 篇文章 2 订阅

概叙

科普文:软件架构数据库系列之【MySQL前世今生及其体系结构概叙】-CSDN博客

前面我们梳理了MySQL体系结构,涉及到innodb的并未进行详细说明,故在此文中进行补充说明。

为了更好的理解Innodb,这里还是和前面MySQL体系结构一文中一样,保持一样的结构。重复的章节就不再描述,重点描述Inodb引擎。

MySQL体系架构

 MySQL是由SQL接口,解析器,优化器,缓存,存储引擎组成的(SQL Interface、 Parser、 Optimizer、Caches&Buffers、Pluggable Storage Engines)

  1. 客户端:Connectors指的是不同语言中与SQL的交互,含jdbc驱动、mysql客户端、以及一些mysql链接工具(sqlYong、Navcat等等)。
  2. 服务层(MySQL Server)

    1. Management Serveices & Utilities: 系统管理和控制工具,例如备份恢复、Mysql复制、集群等
    2. Connection Pool: 连接池:管理缓冲用户连接、用户名、密码、权限校验、线程处理等需要。MySQL连接池用于管理连接,校验用户信息等。MySQL命令是并发处理的 - 网络处理流程:主线程接收连接,接收连接交由连接池处理 - 主要处理方式:IO多路复用 select + 阻塞的 io之所以用select ,是因为select 是跨平台的IO多路复用。
    3. SQL Interface: SQL接口:接受用户的SQL命令,并且返回用户需要查询的结果。比如select from就是调用SQL Interface。SQL接口组件,这个名词听上去似乎不太容易理解,其实主要作用就是负责处理客户端的SQL语句,当客户端连接建立成功之后,会接收客户端的SQL命令,比如DML、DDL、DQL、DCL、TCL语句以及存储过程、触发器等,当收到SQL语句时,SQL接口会将其分发给其他组件,然后等待接收执行结果的返回,最后会将其返回给客户端。
    4. Parser: 解析器, SQL命令传递到解析器的时候会被解析器验证和解析。主要功能: a . 将SQL语句分解成数据结构,并将这个结构传递到后续步骤,以后SQL语句的传递和处理就是基于这个结构的 b. 如果在分解构成中遇到错误,那么就说明这个sql语句是不合理的
    5. Optimizer: 查询优化器, SQL语句在查询之前会使用查询优化器对查询进行优化
    6. Cache和Buffer(高速缓存区): 查询缓存,如果查询缓存有命中的查询结果,查询语句就可以直接去查询缓存中取数据。 通过LRU算法将数据的冷端溢出,未来得及时刷新到磁盘的数据页,叫脏页。 这个缓存机制是由一系列小缓存组成的。比如表缓存,记录缓存, key缓存,权限缓存等
  3. 存储引擎层(Pluggable Storage Engines) :存储引擎。存储引擎是MySql中具体的与文件打交道的子系统。也是Mysql最具有特色的一个地方。InnoDB 是 MySQL 数据库中最常用的关系型数据库存储引擎,自 MySQL 5.5 版本以来成为默认存储引擎。
  4. 系统文件层(File System):该层负责将数据库的数据和日志存储在文件系统之上,并完成与存储引擎的交互,是文件的物理存储层。主要包含日志文件,数据文件,配置文件,pid 文件,socket 文件等。也就是与上层的存储引擎做交互,负责数据的最终存储与持久化工作。

存储引擎层(Pluggable Storage Engines

存储引擎也可以理解成MySQL最重要的一层,在前面的服务层中,聚集了MySQL所有的核心逻辑操作,而引擎层则负责具体的数据操作以及执行工作。

MySQL数据库区别于其他数据库的一个重要特点:插件式的表存储引擎。即存储引擎是基于表的,而不是基于库。

MySQL目前有非常多的存储引擎可选择,其中最为常用的则是InnoDBMyISAM引擎,可以通过show variables like '%storage_engine%';命令来查看当前所使用的引擎。其他引擎如下:

 存储引擎是MySQL数据库中与磁盘文件打交道的子系统,不同的引擎底层访问文件的机制也存在些许细微差异,引擎也不仅仅只负责数据的管理,也会负责库表管理、索引管理等,MySQL中所有与磁盘打交道的工作,最终都会交给存储引擎来完成。

Innodb引擎

MySQL的InnoDB存储引擎是为处理大容量数据而设计的,它提供了诸多高级数据库功能,其性能和可靠性使得InnoDB成为MySQL最受欢迎的存储引擎之一。

InnoDB 是 MySQL 数据库中最常用的关系型数据库存储引擎,自 MySQL 5.5 版本以来成为默认存储引擎。它支持事务处理、行级锁定、外键约束以及崩溃恢复能力,特别适合于高并发、高可靠性的应用场景。InnoDB 引擎还提供了对大容量数据的支持,通过聚簇索引实现数据和索引的紧密集成,优化了查询性能。

InnoDB存储引擎的优点

  1. 事务支持:InnoDB提供了完全的ACID(原子性、一致性、隔离性、持久性)事务支持。它可以进行提交、回滚和崩溃恢复功能,保证数据的完整性。
  2. 行级锁定:与表级锁相比,行级锁可以大幅度提高多用户并发操作的性能。它允许多个事务同时对不同的数据行进行修改,从而减少了锁争用。
  3. 外键约束:InnoDB支持外键,这使得数据库设计更加严格和标准化。外键约束用于维护关系数据的完整性,它们确保了参照完整性。(一般很少用外键约束,对主从复制不友好,它会破坏数据的一致性和完整性。)
  • Last_SQL_Error: Could not execute Delete_rows event on table hcy.t1;
  • Can’t find record in ‘t1’,
  • Error_code: 1032; handler error HA_ERR_KEY_NOT_FOUND;
  • the event’s master log mysql-bin.000006, end_log_pos 254
  1. MVCC(多版本并发控制) :InnoDB通过MVCC来同时处理高并发的读写操作,而无需依赖严格的读写锁定。这允许在不阻塞写操作的情况下执行读取查询。
  2. 聚簇索引:数据存储与主键索引相结合,加速数据检索速度。
  3. 崩溃恢复:InnoDB通过日志文件(redo log)记录数据的更改,发生崩溃时能够使用这些日志来恢复数据,因此具有崩溃恢复的能力。
  4. 缓冲池:InnoDB管理一个内存中的缓冲池来存放表和索引数据,这减少了对磁盘的I/O操作,极大提高了性能。
  5. 表空间管理:InnoDB有自己的表空间用于存储数据和索引。它支持自动的表空间扩展,可以动态管理磁盘空间。
  6. 支持大数据集:InnoDB支持大数据集,表和索引的大小只受限于操作系统文件的大小限制,轻松处理大量数据。
  7. 快速的次要索引创建:InnoDB允许在表上创建或删除次要索引而无需复制整个表的数据,这可以节省大量时间和资源。
  8. 数据和表空间加密:InnoDB提供了数据和表空间的加密功能,这有助于保护数据安全,防止未经授权的访问。
  9. 存储格式透明:对于表结构的修改操作(比如增加列)往往更加灵活且不影响性能。
  10. 热备份:在数据库运行时进行备份,不影响服务。

上述提到的特性和优势使得InnoDB引擎非常适合那些要求高可靠性、高性能和事务支持的场景。在使用MySQL进行数据管理时,InnoDB通常是优先考虑的存储引擎选项。

InnoDB存储引擎的缺点:

  1. 相对MyISAM,InnoDB占用更多的存储空间,因为它要存储行数据和索引。

  2. 对于只读事务或者简单的更新操作,性能可能较差,因为需要维护数据的一致性和持久性。

  3. 对于大量的随机写入,InnoDB的性能可能较差,因为它会频繁地进行磁盘I/O操作。

InnoDB存储引擎的使用场景:

  1. 对数据完整性和一致性要求较高的应用,如银行、金融等系统。

  2. 需要高并发和行级锁的应用,如web应用。

  3. 对数据库性能要求较高,同时需要备份和恢复机制的应用。

InnoDB体系结构

可参考:[MySQL分享]--MySQL体系结构(InnoDB引擎)-CSDN博客

 从Innodb的角度,将MySQL分为三部分:Innodb内存和磁盘、Innodb后台线程、Innodb文件。

  1. Innodb内存和磁盘:Innodb内存又分为线程共享内存和线程独享内存。如上图蓝色边框的是线程共享内存,草绿色边框的是线程独享内存。同时与Innodb内存相对应的则是Innodb磁盘结构,Innodb磁盘结构则对应着innodb文件。
  2. Innodb后台线程:上图中,没有画出purge thread和page cleaner thread,mysql5.6版本后,这2个线程从master thread里独立出来,缓解master thread的压力。
  3. Innodb文件:主要是日志文件和数据文件。

Innodb内存和磁盘

Innodb内存和磁盘:Innodb内存又分为线程共享内存和线程独享内存。如上图蓝色边框的是线程共享内存,草绿色边框的是线程独享内存。同时与Innodb内存相对应的则是Innodb磁盘结构,Innodb磁盘结构则对应着innodb文件。

再来一张详细点的结构图

InnoDB内存结构

内存结构主要包括Buffer Pool、Change Buffer、Adaptive Hash Index和Log Buffer四大组件。

  • Buffer Pool:缓冲池,简称BP。BP以Page页为单位,默认大小16K,BP的底层采用链表数据结构管理Page。在InnoDB访问表记录和索引时会在Page页中缓存,以后使用可以减少磁盘IO操作,提升效率。
    • Page管理机制
      • Page根据状态可以分为三种类型:
        1. free page : 空闲page,未被使用
        2. clean page:被使用page,数据没有被修改过
        3. dirty page:脏页,被使用page,数据被修改过,页中数据和磁盘的数据产生了不一致
      • 针对上述三种page类型,InnoDB通过三种链表结构来维护和管理:
        1. free list :表示空闲缓冲区,管理free page
        2. flush list:表示需要刷新到磁盘的缓冲区,管理dirty page,内部page按修改时间排序。脏页即存在于flush链表,也在LRU链表中,但是两种互不影响,LRU链表负责管理page的可用性和放,而flush链表负责管理脏页的刷盘操作。
        3. lru list:表示正在使用的缓冲区,管理clean page和dirty page,缓冲区以midpoint为基点,前面链表称为new列表区,存放经常访问的数据,占63%;后面的链表称为old列表区,存放使用较少数据,占37%。
    • 改进型LRU算法维护    
      • 普通LRU:末尾淘汰法,新数据从链表头部加入,释放空间时从末尾淘汰
      • 改性LRU:链表分为new和old两个部分,加入元素时并不是从表头插入,而是从中间midpoint位置插入,如果数据很快被访问,那么page就会向new列表头部移动,如果数据没有被访问,会逐步向old尾部移动,等待淘汰。
      • 每当有新的page数据读取到buffer pool时,InnoDb引擎会判断是否有空闲页,是否足够,如果有就将free page从free list列表删除,放入到LRU列表中。没有空闲页,就会根据LRU算法淘汰LRU链表默认的页,将内存空间释放分配给新的页。
    • Buffer Pool配置参数
      • show variables like ‘%innodb_page_size%’; //查看page页大小
      • show variables like ‘%innodb_old%’; //查看lru list中old列表参数
      • show variables like ‘%innodb_buffer%’; //查看buffffer pool参数
      • 建议:将innodb_buffer_pool_size设置为总内存大小的60%-80%,innodb_buffer_pool_instances可以设置为多个,这样可以避免缓存争夺。
  • Change Buffer:写缓冲区,简称CB。在进行DML操作时,如果BP没有其相应的Page数据,并不会立刻将磁盘页加载到缓冲池,而是在CB记录缓冲变更,等未来数据被读取时,再将数据合并恢复到BP中。
    • ChangeBuffer占用BufferPool空间,默认占25%,最大允许占50%,可以根据读写业务量来进行调整。参数innodb_change_buffer_max_size;
    • 当更新一条记录时,该记录在BufferPool存在,直接在BufferPool修改,一次内存操作。如果该记录在BufferPool不存在(没有命中),会直接在ChangeBuffer进行一次内存操作,不用再去磁盘查询数据,避免一次磁盘IO。当下次查询记录时,会先进性磁盘读取,然后再从ChangeBuffer中读取信息合并,最终载入BufferPool中。
    • 写缓冲区,仅适用于非唯一普通索引页
    • 如果在索引设置唯一性,在进行修改时,InnoDB必须要做唯一性校验,因此必须查询磁盘,做一次IO操作。会直接将记录查询到BufferPool中,然后在缓冲池修改,不会在ChangeBuffer操作。
  • Adaptive Hash Index:自适应哈希索引,用于优化对BP数据的查询。InnoDB存储引擎会监控对表索引的查找,如果观察到建立哈希索引可以带来速度的提升,则建立哈希索引,所以称之为自适应。InnoDB存储引擎会自动根据访问的频率和模式来为某些页建立哈希索引。(MySQL8.0 移除了自适应哈希
  • Log Buffer:日志缓冲区,用来保存要写入磁盘上log文件(Redo/Undo)的数据,日志缓冲区的内容定期刷新到磁盘log文件中。日志缓冲区满时会自动将其刷新到磁盘,当遇到BLOB或多行更新的大事务操作时,增加日志缓冲区可以节省磁盘I/O。
    • LogBuffer主要是用于记录InnoDB引擎日志,在DML操作时会产生Redo和Undo日志;
    • LogBuffer空间满了,会自动写入磁盘。可以通过将innodb_log_buffer_size参数调大,减少磁盘IO频率;
    • innodb_flush_log_at_trx_commit参数控制日志刷新行为,默认为1
      • 0 : 每隔1秒写日志文件和刷盘操作(写日志文件LogBuffer --> OS cache,刷盘OScache --> 磁盘文件),最多丢失1秒数据
      • 1:事务提交,立刻写日志文件和刷盘,数据不丢失,但是会频繁IO操作
      • 2:事务提交,立刻写日志文件,每隔1秒钟进行刷盘操作
线程共享内存

Mysql 内存分配规则是:用多少给多少,最高到配置的值,不是立即分配
全局缓存包括:
global buffer(全局内存分配总和) =
   innodb_buffer_pool_size                      -- InnoDB高速缓冲,行数据、索引缓冲,以及事务锁、自适应哈希等
+innodb_additional_mem_pool_size    -- InnoDB数据字典额外内存,缓存所有表数据字典
+innodb_log_buffer_size                      -- InnoDB REDO日志缓冲,提高REDO日志写入效率
+key_buffer_size                                   -- MyISAM表索引高速缓冲,提高MyISAM表索引读写效率
+query_cache_size                                -- 查询高速缓存,缓存查询结果,提高反复查询返回效率(MySQL8.0 已经移除查询缓存
+thread_cache_size                               --Thread_Cache 中存放的最大连接线程数
+table_cahce                                         -- 表空间文件描述符缓存,提高数据表打开效率
+table_definition_cache                        -- 表定义文件描述符缓存,提高数据表打开效率

线程独享内存

会话缓存包括:
 total_thread_buffers= max_connections  * (
  read_buffer_size             -- 顺序读缓冲,提高顺序读效率
+read_rnd_buffer_size   -- 随机读缓冲,提高随机读效率
+sort_buffer_size           -- 排序缓冲,提高排序效率
+join_buffer_size           -- 表连接缓冲,提高表连接效率
+binlog_cache_size       -- 二进制日志缓冲,提高二进制日志写入效率
+tmp_table_size            -- 内存临时表,提高临时表存储效率
+thread_stack                -- 线程堆栈,暂时寄存SQL语句/存储过程
+thread_cache_size       -- 线程缓存,降低多次反复打开线程开销,模拟连接池

InnoDB磁盘结构

InnoDB磁盘主要包含Tablespaces,InnoDB Data Dictionary,Doublewrite Buffer、Redo Log和Undo Logs。

这一部分和innodb文件有交叉,都是innodb内存和磁盘之间的映射。

  • 表空间(Tablespaces):用于存储表结构和数据。表空间又分为系统表空间、独立表空间、通用表空间、临时表空间、Undo表空间等多种类型;

    • 系统表空间(The System Tablespace)

      • 包含InnoDB数据字典,Doublewrite Buffer,Change Buffer,Undo Logs的存储区域。系统表空间也默认包含任何用户在系统表空间创建的表数据和索引数据。系统表空间是一个共享的表空间因为它是被多个表共享的。该空间的数据文件通过参数innodb_data_file_path控制,默认值是ibdata1:12M:autoextend(文件名为ibdata1、12MB、自动扩展)。
      • 1

        CREATE TABLESPACE ts1 ADD DATAFILE ts1.ibd Engine=InnoDB;

         //创建表空 间ts1 CREATE TABLE t1 (c1 INT PRIMARY KEY) TABLESPACE ts1; 

        //将表添加到ts1 表空间

    • 独立表空间(File-Per-Table Tablespaces)

      • 默认开启,独立表空间是一个单表表空间,该表创建于自己的数据文件中,而非创建于系统表空间中。当innodb_file_per_table选项开启时,表将被创建于表空间中。否则,innodb将被创建于系统表空间中。每个表文件表空间由一个.ibd数据文件代表,该文件默认被创建于数据库目录中。表空间的表文件支持动态(dynamic)和压缩(commpressed)行格式。
    • 通用表空间(General Tablespaces)

      • 通用表空间为通过create tablespace语法创建的共享表空间。通用表空间可以创建于mysql数据目录外的其他表空间,其可以容纳多张表,且其支持所有的行格式。
    • 撤销表空间(Undo Tablespaces)

      • 撤销表空间由一个或多个包含Undo日志文件组成。在MySQL 5.7版本之前Undo占用的是System Tablespace共享区,从5.7开始将Undo从System Tablespace分离了出来。
      • InnoDB使用的undo表空间由innodb_undo_tablespaces配置选项控制,默认为0。参数值为0表示使用系统表空间ibdata1;大于0表示使用undo表空间undo_001、undo_002等。
    • 临时表空间(Temporary Tablespaces)

      • 分为session temporary tablespaces 和global temporary tablespace两种:
        1. session temporary tablespaces 存储的是用户创建的临时表和磁盘内部的临时表。
        2. global temporary tablespace储存用户临时表的回滚段(rollback segments )。mysql服务器正常关闭或异常终止时,临时表空间将被移除,每次启动时会被重新创建。
  • 数据字典(InnoDB Data Dictionary)

    • InnoDB数据字典由内部系统表组成,这些表包含用于查找表、索引和表字段等对象的元数据。元数据物理上位于InnoDB系统表空间中。由于历史原因,数据字典元数据在一定程度上与InnoDB表元数据文件(.frm文件)中存储的信息重叠。
  • 双写缓冲区(Doublewrite Buffer)

    • 位于系统表空间,是一个存储区域。在BufferPage的page页刷新到磁盘真正的位置前,会先将数据存在Doublewrite 缓冲区。如果在page页写入过程中出现操作系统、存储子系统或mysqld进程崩溃,InnoDB可以在崩溃恢复期间从Doublewrite 缓冲区中找到页面的一个好备份。在大多数情况下,默认情况下启用双写缓冲区,要禁用Doublewrite 缓冲区,可以将innodb_doublewrite设置为0。使用Doublewrite 缓冲区时建议将innodb_flush_method设置为O_DIRECT。
      • MySQL的innodb_flush_method这个参数控制着innodb数据文件及redo log的打开、刷写模式。有三个值:fdatasync(默认),O_DSYNC,O_DIRECT。设置O_DIRECT表示数据文件写入操作会通知操作系统不要缓存数据,也不要用预读,直接从InnodbBuffer写到磁盘文件。
      • 默认的fdatasync意思是先写入操作系统缓存,然后再调用fsync()函数去异步刷数据文件与redo log的缓存信息。
  • 重做日志(Redo Log)

    • 重做日志是一种基于磁盘的数据结构,用于在崩溃恢复期间更正不完整事务写入的数据。MySQL以循环方式写入重做日志文件,记录InnoDB中所有对Buffer Pool修改的日志。当出现实例故障(像断电),导致数据未能更新到数据文件,则数据库重启时须redo,重新把数据更新到数据文件。读写事务在执行的过程中,都会不断的产生redo log。默认情况下,重做日志在磁盘上由两个名为ib_logfile0和ib_logfile1的文件物理表示。
  • 撤销日志(Undo Logs)

    • 撤消日志是在事务开始之前保存的被修改数据的备份,用于例外情况时回滚事务。撤消日志属于逻辑日志,根据每行记录进行记录。撤消日志存在于系统表空间、撤消表空间和临时表空间中。

新版本结构演变

image-20220515214541979

  • MySQL 5.7 版本
    • 将 Undo日志表空间从共享表空间 ibdata 文件中分离出来,可以在安装 MySQL 时由用户自行指定文件大小和数量。
    • 增加了 temporary 临时表空间,里面存储着临时表或临时查询结果集的数据。
    • Buffer Pool 大小可以动态修改,无需重启数据库实例。
  • MySQL 8.0 版本
    • 将InnoDB表的数据字典和Undo都从共享表空间ibdata中彻底分离出来了,以前需要ibdata中数据字典与独立表空间ibd文件中数据字典一致才行,8.0版本就不需要了。
    • temporary 临时表空间也可以配置多个物理文件,而且均为 InnoDB 存储引擎并能创建索引,这样加快了处理的速度。
    • 用户可以像 Oracle 数据库那样设置一些表空间,每个表空间对应多个物理文件,每个表空间可以给多个表使用,但一个表只能存储在一个表空间中。
    • 将Doublewrite Buffer从共享表空间ibdata中也分离出来了。

Innodb后台线程

在InnoDB的后台线程中,分为4类,分别是:Master Thread 、IO Thread、Purge Thread、Page Cleaner Thread。 

  • IO Thread
    • 在InnoDB中使用了大量的AIO(Async IO)来做读写处理,这样可以极大提高数据库的性能。在
      InnoDB共有10个IO Thread,分别是4个write,4个read,1个insert buffer和 1个log thread。
      • read thread : 负责读取操作,将数据从磁盘加载到缓存page页。4个
      • write thread:负责写操作,将缓存脏页刷新到磁盘。4个
      • log thread:负责将日志缓冲区内容刷新到磁盘。1个
      • insert buffer thread :负责将写缓冲内容刷新到磁盘。1个
  • Purge Thread
    • 事务提交之后,其使用的undo日志将不再需要,因此需要Purge Thread回收已经分配的undo页。
    • 细分自Master线程,可以减轻Master线程的压力。
    • show variables like ‘%innodb_purge_threads%’;
  • Page Cleaner Thread
    • 作用是将脏数据刷新到磁盘,脏数据刷盘后相应的redo log也就可以覆盖,即可以同步数据,又能达到redo log循环使用的目的。会调用write thread线程处理。
    • 细分自Master线程,协助 Master Thread 刷新脏页到磁盘的线程,它可以减轻 Master Thread 的工作压力,减少阻塞。
    • show variables like ‘%innodb_page_cleaners%’;
  • Master Thread
    • Master thread是InnoDB的主线程,负责调度其他各线程,优先级最高。作用是将缓冲池中的数据异步刷新到磁盘 ,保证数据的一致性。包含:脏页的刷新(page cleaner thread)、undo页回收(purge thread)、redo日志刷新(log thread)、合并写缓冲等。内部有两个主处理,分别是每隔1秒和10秒处理。
    • 每1秒的操作:
      • 刷新日志缓冲区,刷到磁盘
      • 合并写缓冲区数据,根据IO读写压力来决定是否操作
      • 刷新脏页数据到磁盘,根据脏页比例达到75%才操作(innodb_max_dirty_pages_pct,
        innodb_io_capacity)
    • 每10秒的操作:
      • 刷新脏页数据到磁盘
      • 合并写缓冲区数据
      • 刷新日志缓冲区
      • 删除无用的undo页

Innodb文件

Innodb文件:主要是日志文件和数据文件。

  • InnoDB数据文件存储结构

    • 分为 ibd数据文件 --> Segment(段)–>Extent(区)–> Page(页)–>Row(行)    
      • Tablesapce表空间,用于存储多个ibd数据文件,用于存储表的记录和索引。一个文件包含多个段。
      • Segment段,用于管理多个Extent,分为数据段(Leaf node segment)、索引段(Non-leaf node
        segment)、回滚段(Rollback segment)。一个表至少会有两个segment,一个管理数据,一个管理索引。每多创建一个索引,会多两个segment。
      • Extent区,一个区固定包含64个连续的页,大小为1M。当表空间不足,需要分配新的页资源,不会
        一页一页分,直接分配一个区。
      • Page页,用于存储多个Row行记录,大小为16K。包含很多种页类型,比如数据页,undo页,系统页,事务数据页,大的BLOB对象页。
      • Row行,包含了记录的字段值,事务ID(Trx id)、滚动指针(Roll pointer)、字段指针(Field
        pointers)等信息。
    • Page是文件最基本的单位,无论何种类型的page,都是由page header,page trailer和page body组成。如下图所示

  • InnoDB文件存储格式

    • 通过 SHOW TABLE STATUS 命令 查看

      • 一般情况下,如果row_format为REDUNDANT、COMPACT,文件格式为Antelope;如果row_format为DYNAMIC和COMPRESSED,文件格式为Barracuda。

      • 通过 information_schema 查看指定表的文件格式

        • 1

          select * from information_schema.innodb_sys_tables;

  • File文件格式(File-Format)

    • 在早期的InnoDB版本中,文件格式只有一种,随着InnoDB引擎的发展,出现了新文件格式,用于支持新的功能。目前InnoDB只支持两种文件格式:Antelope 和 Barracuda。
      • Antelope: 先前未命名的,最原始的InnoDB文件格式,它支持两种行格式:COMPACT和REDUNDANT,MySQL 5.6及其以前版本默认格式为Antelope。
      • Barracuda: 新的文件格式。它支持InnoDB的所有行格式,包括新的行格式:COMPRESSED和 DYNAMIC。
    • 通过innodb_file_format 配置参数可以设置InnoDB文件格式,之前默认值为Antelope,5.7版本开始改为Barracuda。
  • Row行格式(Row_format)

image-20220515220208534

  • 表的行格式决定了它的行是如何物理存储的,这反过来又会影响查询和DML操作的性能。如果在单个page页中容纳更多行,查询和索引查找可以更快地工作,缓冲池中所需的内存更少,写入更新时所需的I/O更少。

  • InnoDB存储引擎支持四种行格式:REDUNDANT、COMPACT、DYNAMIC和COMPRESSED。

  • DYNAMIC和COMPRESSED新格式引入的功能有:数据压缩、增强型长列数据的页外存储和大索引前缀。

  • 每个表的数据分成若干页来存储,每个页中采用B树结构存储;

  • 如果某些字段信息过长,无法存储在B树节点中,这时候会被单独分配空间,此时被称为溢出页,该字段被称为页外列。

    • REDUNDANT 行格式
      • 使用REDUNDANT行格式,表会将变长列值的前768字节存储在B树节点的索引记录中,其余
        的存储在溢出页上。对于大于等于786字节的固定长度字段InnoDB会转换为变长字段,以便
        能够在页外存储。
    • COMPACT 行格式
      • 与REDUNDANT行格式相比,COMPACT行格式减少了约20%的行存储空间,但代价是增加了
        某些操作的CPU使用量。如果系统负载是受缓存命中率和磁盘速度限制,那么COMPACT格式
        可能更快。如果系统负载受到CPU速度的限制,那么COMPACT格式可能会慢一些。
    • DYNAMIC 行格式
      • 使用DYNAMIC行格式,InnoDB会将表中长可变长度的列值完全存储在页外,而索引记录只包含指向溢出页的20字节指针。大于或等于768字节的固定长度字段编码为可变长度字段。DYNAMIC行格式支持大索引前缀,最多可以为3072字节,可通过innodb_large_prefix参数控制。
    • COMPRESSED 行格式
      • COMPRESSED行格式提供与DYNAMIC行格式相同的存储特性和功能,但增加了对表和索引
        数据压缩的支持。
  • 在创建表和索引时,文件格式都被用于每个InnoDB表数据文件(其名称与*.ibd匹配)。修改文件格式的方法是重新创建表及其索引,最简单方法是对要修改的每个表使用以下命令:

    • 1

      ALTER TABLE 表名 ROW_FORMAT=格式类型;

Undo Log日志

  • Undo:意为撤销或取消,以撤销操作为目的,返回指定某个状态的操作。

  • Undo Log:数据库事务开始之前,会将要修改的记录存放到 Undo 日志里,当事务回滚时或者数据库崩溃时,可以利用 Undo 日志,撤销未提交事务对数据库产生的影响。

  • Undo Log产生和销毁:Undo Log在事务开始前产生;事务在提交时,并不会立刻删除undo log,innodb会将该事务对应的undo log放入到删除列表中,后面会通过后台线程purge thread进行回收处理。Undo Log属于逻辑日志,记录一个变化过程。例如执行一个delete,undolog会记录一个insert;执行一个update,undolog会记录一个相反的update。

  • Undo Log存储:undo log采用段的方式管理和记录。在innodb数据文件中包含一种rollback segment回滚段,内部包含1024个undo log segment。可以通过下面一组参数来控制Undo log存储。

  • 1

    2

    3

    #相关参数命令

    show variables like '%innodb_undo%';

Undo Log作用

  • 实现事务的原子性
    • Undo Log 是为了实现事务的原子性而出现的产物。事务处理过程中,如果出现了错误或者用户执行了 ROLLBACK 语句,MySQL 可以利用 Undo Log 中的备份将数据恢复到事务开始之前的状态。
  • 实现多版本并发控制(MVCC)

    • Undo Log 在 MySQL InnoDB 存储引擎中用来实现多版本并发控制。事务未提交之前,Undo Log保存了未提交之前的版本数据,Undo Log 中的数据可作为数据旧版本快照供其他并发事务进行快照读。

image-20220515221157888

  • 事务A手动开启事务,执行更新操作,首先会把更新命中的数据备份到 Undo Buffer 中;
  • 事务B手动开启事务,执行查询操作,会读取 Undo 日志数据返回,进行快照读;

  • Redo Log 和 Binlog

Redo Log 日志

  • Redo Log 介绍

    • Redo:顾名思义就是重做。以恢复操作为目的,在数据库发生意外时重现操作。
    • Redo Log:指事务中修改的任何数据,将最新的数据备份存储的位置(Redo Log),被称为重做日志。
    • Redo Log 的生成和释放:随着事务操作的执行,就会生成Redo Log,在事务提交时会将产生Redo Log写入Log Buffer,并不是随着事务的提交就立刻写入磁盘文件。等事务操作的脏页写入到磁盘之后,Redo Log 的使命也就完成了,Redo Log占用的空间就可以重用(被覆盖写入)。
  • Redo Log工作原理

image-20220515221500277

  • Redo Log 是为了实现事务的持久性而出现的产物。防止在发生故障的时间点,尚有脏页未写入表
    的 IBD 文件中,在重启 MySQL 服务的时候,根据 Redo Log 进行重做,从而达到事务的未入磁盘
    数据进行持久化这一特性。
  • Redo Log写入机制

    Redo Log 文件内容是以顺序循环的方式写入文件,写满时则回溯到第一个文件,进行覆盖写。

image-20220515221600311

    • write pos 是当前记录的位置,一边写一边后移,写到最后一个文件末尾后就回到 0 号文件开头;
    • checkpoint 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件;
    • write pos 和 checkpoint 之间还空着的部分,可以用来记录新的操作。如果 write pos 追上checkpoint,表示写满,这时候不能再执行新的更新,得停下来先擦掉一些记录,把 checkpoint推进一下。
  • Redo Log相关配置参数

    • 每个InnoDB存储引擎至少有1个重做日志文件组(group),每个文件组至少有2个重做日志文件,默认为ib_logfile0和ib_logfile1。可以通过下面一组参数控制Redo Log存储:

      • 1

        show variables like '%innodb_log%';

    • Redo Buffer 持久化到 Redo Log 的策略,可通过 Innodb_flush_log_at_trx_commit 设置:

image-20220515221806576

  • 0:每秒提交 Redo buffer ->OS cache -> flush cache to disk,可能丢失一秒内的事务数据。由后台Master线程每隔 1秒执行一次操作。
  • 1(默认值):每次事务提交执行 Redo Buffer -> OS cache -> flush cache to disk,最安全,性能最差的方式。
  • 2:每次事务提交执行 Redo Buffer -> OS cache,然后由后台Master线程再每隔1秒执行OS cache -> flush cache to disk 的操作。
  • 一般建议选择取值2,因为 MySQL 挂了数据没有损失,整个服务器挂了才会损失1秒的事务提交数
    据。

Binlog日志

  • Binlog 记录模式

    • Redo Log 是属于InnoDB引擎所特有的日志,而MySQL Server也有自己的日志,即 Binary log(二进制日志),简称Binlog。Binlog是记录所有数据库表结构变更以及表数据修改的二进制日志,不会记录SELECT和SHOW这类操作。Binlog日志是以事件形式记录,还包含语句所执行的消耗时间。开启Binlog日志有以下两个最重要的使用场景。
      • 主从复制:在主库中开启Binlog功能,这样主库就可以把Binlog传递给从库,从库拿到Binlog后实现数据恢复达到主从数据一致性。
      • 数据恢复:通过mysqlbinlog工具来恢复数据。
    • Binlog文件名默认为“主机名_binlog-序列号”格式,例如oak_binlog-000001,也可以在配置文件中指定名称。文件记录模式有STATEMENT、ROW和MIXED三种,具体含义如下。
      • ROW(row-based replication, RBR):日志中会记录每一行数据被修改的情况,然后在slave端对相同的数据进行修改。
        • 优点:能清楚记录每一个行数据的修改细节,能完全实现主从数据同步和数据的恢复。
        • 缺点:批量操作,会产生大量的日志,尤其是alter table会让日志暴涨。
      • STATMENT(statement-based replication, SBR):每一条被修改数据的SQL都会记录到master的Binlog中,slave在复制的时候SQL进程会解析成和原来master端执行过的相同的SQL再次执行。简称SQL语句复制。
        • 优点:日志量小,减少磁盘IO,提升存储和恢复速度
        • 缺点:在某些情况下会导致主从数据不一致,比如last_insert_id()、now()等函数。
      • MIXED(mixed-based replication, MBR):以上两种模式的混合使用,一般会使用STATEMENT模式保存binlog,对于STATEMENT模式无法复制的操作使用ROW模式保存binlog,MySQL会根据执行的SQL语句选择写入模式。
  • Binlog 文件结构

    • MySQL的binlog文件中记录的是对数据库的各种修改操作,用来表示修改操作的数据结构是Log event。不同的修改操作对应的不同的log event。比较常用的log event有:Query event、Row event、Xid event等。binlog文件的内容就是各种Log event的集合。
    • Binlog文件中Log event结构如下图所示:

image-20220515222235118

  • Binlog写入机制

    • 根据记录模式和操作触发event事件生成log event(事件触发执行机制)
    • 将事务执行过程中产生log event写入缓冲区,每个事务线程都有一个缓冲区Log Event保存在一个binlog_cache_mngr数据结构中,在该结构中有两个缓冲区,一个是stmt_cache,用于存放不支持事务的信息;另一个是trx_cache,用于存放支持事务的信息。
    • 事务在提交阶段会将产生的log event写入到外部binlog文件中。
    • 不同事务以串行方式将log event写入binlog文件中,所以一个事务包含的log event信息在binlog文件中是连续的,中间不会插入其他事务的log event。
  • Binlog文件操作

    • 根据记录模式和操作触发event事件生成log event(事件触发执行机制)
    • 将事务执行过程中产生log event写入缓冲区,每个事务线程都有一个缓冲区
    • Log Event保存在一个binlog_cache_mngr数据结构中,在该结构中有两个缓冲区,一个是stmt_cache,用于存放不支持事务的信息;另一个是trx_cache,用于存放支持事务的信息。
    • 事务在提交阶段会将产生的log event写入到外部binlog文件中。
    • 不同事务以串行方式将log event写入binlog文件中,所以一个事务包含的log event信息在
      binlog文件中是连续的,中间不会插入其他事务的log event。
  • Binlog文件操作

    • Binlog状态查看

      • 1

        show variables like 'log_bin';

    • 开启Binlog功能

      • 1

        2

        set global log_bin = mysqllogbin; 

        ERROR 1238 (HY000): Variable 'log_bin' is a read only variable

      • 需要修改my.cnf或my.ini配置文件,在[mysqld]下面增加log_bin=mysql_bin_log,重启MySQL服务。

      • 1

        2

        3

        4

        #log-bin=ON 

        #log-bin-basename=mysqlbinlog 

        binlog-format=ROW 

        log-bin=mysqlbinlog

    • 使用show binlog events命令

      • 1

        2

        3

        4

        5

        6

        7

        show binary logs; //等价于show master logs; 

        show master status; 

        show binlog events; 

        show binlog events in 'mysqlbinlog.000001';

    • 使用 mysqlbinlog 命令

      • 1

        2

        3

        mysqlbinlog "文件名" 

        mysqlbinlog "文件名" > "test.sql"

    • 使用 binlog 恢复数据

      • 1

        2

        3

        4

        5

        //按指定时间恢复 

        mysqlbinlog --start-datetime="2020-04-25 18:00:00" --stop- datetime="2020-04-26 00:00:00" mysqlbinlog.000002 | mysql -uroot -p1234 

        //按事件位置号恢复 

        mysqlbinlog --start-position=154 --stop-position=957 mysqlbinlog.000002 | mysql -uroot -p1234

      • mysqldump:定期全部备份数据库数据。mysqlbinlog可以做增量备份和恢复操作。

    • 删除Binlog文件

      • 1

        2

        3

        4

        5

        purge binary logs to 'mysqlbinlog.000001'//删除指定文件 

        purge binary logs before '2020-04-28 00:00:00'//删除指定时间之前的文件 

        reset master; //清除所有文件

      • 可以通过设置expire_logs_days参数来启动自动清理功能。默认值为0表示没启用。设置为1表示超出1天binlog文件会自动删除掉。

  • Redo Log和 Binlog区别

    • Redo Log是属于InnoDB引擎功能,Binlog是属于MySQL Server自带功能,并且是以二进制文件记录。
    • Redo Log属于物理日志,记录该数据页更新状态内容,Binlog是逻辑日志,记录更新过程。
    • Redo Log日志是循环写,日志空间大小是固定,Binlog是追加写入,写完一个写下一个,不会覆盖使用。
    • Redo Log作为服务器异常宕机后事务数据自动恢复使用,Binlog可以作为主从复制和数据恢复使用。Binlog没有自动crash-safe能力。

Innodb引擎的MVCC特性

MVCC,是采用乐观锁思想的一种方式;在 MySQL 中,默认的隔离级别是可重复读,可以解决脏读和不可重复读的问题,但不能解决幻读问题。

如果想要解决幻读问题,就需要采用串行化的方式,也就是将隔离级别提升到最高,但这样一来就会大幅降低数据库的事务并发能力。

我们也可以采用MVCC+Next-Key Lock的方式来解决幻读问题。

MVCC 是什么

MVCC 的英文全称是 Multiversion Concurrency Control,即多版本并发控制技术。

MVCC 是通过数据行的多个版本管理来实现数据库的并发控制,简单来说它的思想就是保存数据的历史版本。

这样就可以通过比较版本号决定数据是否显示出来,读取数据的时候不需要加锁也可以保证事务的隔离效果。

MVCC可以解决什么

  • 读写之间阻塞的问题,通过 MVCC 可以让读写互相不阻塞,即读不阻塞写,写不阻塞读,这样就可以提升事务并发处理能力。
  • 降低了死锁的概率。这是因为 MVCC 采用了乐观锁的方式,读取数据时并不需要加锁,对于写操作,也只锁定必要的行。
  • 解决一致性读的问题。一致性读也被称为快照读,当我们查询数据库在某个时间点的快照时,只能看到这个时间点之前事务提交更新的结果,而不能看到这个时间点之后事务提交的更新结果。

快照读:快照读读取的是快照数据,不加锁的简单的 SELECT 都属于快照读。

当前读:当前读读取最新数据,而不是历史版本的数据。加锁的 SELECT,或者对数据进行增删改都会进行当前读。

快照读就是普通的读操作,而当前读包括了加锁的读取和 DML 操作。

MVCC实现的核心知识点

1、事务版本号
2、表的隐藏列。
3、undo log
4、 read view

MVCC的详细原理,这里就不展开了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-无-为-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值