Mysql存储引擎

存储引擎

MySQL 存储引擎是 MySQL 数据库的核心组件之一,负责数据的存储、管理和检索。不同的存储引擎提供了不同的功能和性能特性。

存储引擎作用

  • 数据存储
    • 决定数据如何存储在磁盘或内存中。
    • 管理数据的物理存储结构(如表空间、文件等)
  • 数据检索
    • 提供数据的读写接口。
    • 支持索引机制,优化查询性能。
  • 事务支持
    • 部分存储引擎(如 InnoDB)支持事务,确保数据的 ACID 特性(原子性、一致性、隔离性、持久性)
  • 并发控制
    • 提供锁机制(如行级锁、表级锁),管理多用户并发访问。
  • 数据完整性
    • 支持外键约束、唯一约束等,确保数据的完整性和一致性。
  • 性能优化
    • 不同的存储引擎针对不同的场景进行了优化(如高并发、高压缩、快速插入等)。

InnoDB

InnoDB 是 MySQL 中最常用、功能最强大的存储引擎之一。从 MySQL 5.5 版本开始,InnoDB 成为默认的存储引擎。

核心特性

  1. 事务支持(ACID 兼容)

  2. 行级锁

    • InnoDB 支持行级锁,允许多个事务同时读写不同的行,极大地提高了并发性能。
    • 与 MyISAM 的表级锁相比,行级锁更适合高并发场景
  3. 外键约束

    • InnoDB 支持外键约束,确保数据的完整性和一致性。
    • 外键约束可以防止无效数据的插入或更新
  4. 崩溃恢复

    • InnoDB 提供了崩溃恢复机制,通过日志文件(redo log)确保数据在系统崩溃后能够恢复到一致状态
  5. 多版本并发控制(MVCC)

    • InnoDB 使用 MVCC 机制实现非阻塞读操作,提高了并发性能。
    • MVCC 通过保存数据的多个版本来实现事务的隔离性
  6. 表空间管理

    • InnoDB 将数据存储在表空间(tablespace)中。
    • 支持独立表空间(每张表一个文件)和共享表空间(所有表共享一个文件)

存储结构

表空间

表空间是 InnoDB 存储结构的核心,用于存储表的数据和索引。InnoDB 的表空间分为以下几种

  1. 系统表空间(System Tablespace)

    • 存储 InnoDB 的元数据、undo 日志、双写缓冲区等。
    • 默认文件名为 ibdata1,可以通过参数 innodb_data_file_path 配置。
    • 如果启用了独立表空间(innodb_file_per_table=ON),则用户表的数据和索引不存储在系统表空间中
    • 如果没有启用独立表空间(innodb_file_per_table=OFF),用户表的数据和索引也会存储在系统表空间中
    • 默认文件名为 ibdata1,可以通过参数 innodb_data_file_path 配置
    • 文件可以自动扩展(通过 autoextend 配置)
  2. 独立表空间(File-Per-Table Tablespace)

    • 每个表的数据和索引存储在一个独立的 .ibd 文件中。
    • 文件名为 表名.ibd,存储在数据库目录下
    • 通过参数 innodb_file_per_table 启用(默认启用)。
      • 便于管理和维护。
      • 可以单独备份和恢复表。
      • 减少系统表空间的压力。
  3. 通用表空间(General Tablespace)

    • 多个表可以共享一个表空间文件。

    • 通过 CREATE TABLESPACE 语句创建

    • 在创建表时指定表空间

      CREATE TABLESPACE ts1 ADD DATAFILE 'ts1.ibd' ENGINE=InnoDB;
      CREATE TABLE t1 (id INT) TABLESPACE ts1;
      
      • 减少文件数量,便于管理。
      • 支持压缩表
  4. 临时表空间(Temporary Tablespace)

    • 存储临时表和临时数据。
    • 默认文件名为 ibtmp1
    • 临时表空间在 MySQL 启动时创建,关闭时删除
  5. Undo 表空间(Undo Tablespace)

    • 存储 undo 日志,用于事务回滚和多版本并发控制(MVCC)
    • 默认文件名为 undo_001、undo_002 等
    • 从 MySQL 5.7 开始,undo 日志可以存储在独立的 undo 表空间中,而不是系统表空间

数据页

  • InnoDB 将数据划分为固定大小的页(Page),页是磁盘和内存之间数据传输的最小单位。默认页大小为 16KB,可以通过参数 innodb_page_size 配置
  1. 页的类型

    • 数据页:存储表的数据行。
      • 存储表的数据和索引(B+ 树的叶子节点存储数据,非叶子节点存储索引)。
      • 是最常用的页类型
      • InnoDB 的数据页(叶子节点)通过双向链表连接,支持顺序访问,但物理存储可能分散在不同的页中
    • undo 页:存储 undo 日志,用于事务回滚和 MVCC。
    • 系统页:存储元数据和其他系统信息
    • 等等
  2. 页的结构

    每个页包含以下部分

    • 文件头(File Header):记录页的元信息,如页号、页类型等。
    • 数据行(Rows):存储实际的数据。
    • 页目录(Page Directory):记录数据行的位置,用于快速查找。
    • 文件尾(File Trailer):用于校验页的完整性

索引结构

  • InnoDB 使用 B+ 树作为索引结构,B+ 树是一种平衡多路搜索树,具有以下特点
    • 多层级结构:由根节点、非叶子节点和叶子节点组成。
    • 有序性:所有节点中的键值都是有序的。
    • 叶子节点链表:叶子节点通过双向链表连接,支持高效的范围查询。
    • 高扇出性:每个节点可以存储多个键值和指针,减少树的高度,提高查询效率
  • InnoDB 中有两种主要的索引类型
    • 聚集索引(Clustered Index)
    • 二级索引(Secondary Index)

聚集索引(Clustered Index)

  • 聚集索引是 InnoDB 中最重要的索引,它的特点如下
    • 数据存储方式
      • 聚集索引的叶子节点存储的是 完整的数据行,而不是指向数据的指针。
      • 数据和索引是存储在一起的,因此聚集索引决定了数据的物理存储顺序
      • 由于聚集索引决定了数据的物理存储顺序,当数据被插入、更新或删除时,数据库管理系统会调整数据的物理位置,以保持与聚集索引的顺序一致
      • 假设有一个包含员工信息的表,并以员工ID作为聚集索引。那么,表中的数据行会按照员工ID的顺序存储在磁盘上。如果插入一个新员工,数据库会将其放置在正确的位置,以保持员工ID的顺序
      • 为了保持聚集索引的顺序,数据库会重新排列数据的物理存储位置。这可能会导致一定的性能开销
    • 聚集索引的选择
      • 如果表定义了主键,主键就是聚集索引。
      • 如果没有主键,InnoDB 会选择第一个唯一的非空索引作为聚集索引。
      • 如果没有唯一索引,InnoDB 会隐式创建一个隐藏的 RowID 作为聚集索引
    • 查询效率
      • 聚簇索引的非叶子节点存储的是索引键值 + 指针,用于快速定位叶子节点
      • 通过聚集索引查询数据时,可以直接定位到叶子节点获取数据,效率非常高。
      • 范围查询(如 BETWEEN、>、<)也非常高效,因为叶子节点是有序的,且通过链表连接

二级索引(Secondary Index)

  • 二级索引是用户自定义的索引,它的特点如下:
    • 数据存储方式
      • 二级索引的叶子节点存储的是 索引列的值 + 主键值,而不是完整的数据行。

      • 非叶子节点存储的是索引列的值和指向下一级节点的指针(通常是页号或块号),用于快速定位叶子节点

      • 通过二级索引查询数据时,需要先找到主键值,然后再通过主键值到聚集索引中查找数据行(即 回表 操作)

    • 索引结构和查询效率
      • 二级索引也是基于 B+ 树的结构,但其叶子节点存储的是主键值和索引列的值
      • 二级索引适合用于等值查询
      • 避免过多的二级索引,因为每次数据更新都需要维护索引

B+ 树的具体结构

  • 非叶子节点(Non-Leaf Node)

    • 存储 索引键值指针
    • 指针指向子节点(可能是非叶子节点或叶子节点)。
    • 非叶子节点不存储实际数据,只用于导航到叶子节点。
  • 叶子节点(Leaf Node)

    • *聚集索引的叶子节点
      • **存储 完整的数据行
      • 叶子节点之间通过双向链表连接,支持高效的范围查询。
    • 二级索引的叶子节点
      • 存储 主键值和索引列的值
      • 叶子节点之间也通过双向链表连接。

缓冲池(Buffer Pool)

  • InnoDB 的 缓冲池(Buffer Pool) 是其核心组件之一,主要用于缓存数据和索引页,以减少磁盘 I/O 操作,提升数据库性能

缓冲池的主要作用

  • 缓存数据和索引页:将磁盘上的数据页和索引页加载到内存中,供查询和修改操作使用。
  • 减少磁盘 I/O:通过缓存数据,避免频繁访问磁盘,从而提升性能。
  • 支持事务的隔离性和一致性:通过缓冲池管理数据的修改和回滚操作

缓冲池的结构

  • 缓冲池是一个连续的内存区域,由多个 页(Page) 组成。每个页的大小通常为 16KB(与磁盘上的数据页大小一致)

缓冲池工作流程

  • 数据读取
    • 当查询数据时,InnoDB 首先检查缓冲池中是否存在所需的页。
    • 如果页在缓冲池中(缓存命中),则直接使用。
    • 如果页不在缓冲池中(缓存未命中),则从磁盘加载页到缓冲池,并更新 LRU 链表
  • 数据修改
    • 当修改数据时,InnoDB 首先在缓冲池中找到对应的页。
    • 修改页的内容,并将页标记为脏页。
    • 将脏页加入刷新链表,等待刷新到磁盘
  • 页的淘汰
    • 当缓冲池空间不足时,InnoDB 会从 LRU 链表的旧子链表中淘汰页。
    • 如果淘汰的页是脏页,则需要先将其写回磁盘
  • 页的刷新
    • InnoDB 会定期将刷新链表中的脏页写回磁盘(称为 Checkpoint)。
    • 刷新操作可以通过后台线程异步执行,以减少对性能的影响

双写缓冲区(Doublewrite Buffer)

双写缓冲区是 InnoDB 用于防止数据页写入不完整的一种机制。

部分写问题

  • 在 InnoDB 中,数据页的大小通常为 16KB,而磁盘的块大小通常为 4KB。
  • 如果在写入磁盘时发生崩溃,可能导致只有部分数据(如 4KB)被写入磁盘,而其他部分丢失,这种现象称为 部分写。
  • 部分写会导致数据页损坏,无法通过 InnoDB 的恢复机制修复

双写缓冲区的解决方案

  • 双写缓冲区通过在写入磁盘之前,先将数据页写入一个 临时区域(双写缓冲区),然后再写入实际的数据文件。
  • 如果在写入过程中发生崩溃,InnoDB 可以通过双写缓冲区中的副本来恢复损坏的数据页

工作流程

  • 写入双写缓冲区
    • 当需要将一个脏页写入磁盘时,InnoDB 首先将该页写入双写缓冲区。
    • 双写缓冲区的写入是 顺序写入,性能较高
  • 写入实际数据文件
    • 在双写缓冲区写入完成后,InnoDB 再将数据页写入实际的数据文件。
  • 刷新双写缓冲区
    • 双写缓冲区的数据会在后台定期刷新到磁盘

与缓冲池的区别

  • 缓冲池(Buffer Pool)
    • 用于缓存数据和索引页,减少磁盘 I/O,提升查询性能。
    • 是内存中的一块区域,存储的是从磁盘加载的数据页。
  • 双写缓冲区(Double Write Buffer)
    • 用于保证数据页写入磁盘时的完整性,避免部分写问题。
    • 是磁盘上的一块区域,存储的是数据页的副本。

日志文件

  • 日志文件机制 是其实现事务 持久性(Durability) 和 崩溃恢复(Crash Recovery) 的核心组件。InnoDB 使用 重做日志(Redo Log) 和 回滚日志(Undo Log) 来确保数据的一致性和可靠性

1. Redo Log(重做日志)

Redo Log的作用

  • 持久性:确保事务提交后,即使系统崩溃,修改的数据也不会丢失。
  • 崩溃恢复:在系统崩溃后,通过重做日志恢复未写入磁盘的数据

Redo Log的结构

  • 重做日志由一组固定大小的文件组成,通常命名为 ib_logfile0 和 ib_logfile1。
  • 每个日志文件的大小通过参数 innodb_log_file_size 配置。
  • 日志文件是循环使用的,写满后会从头开始覆盖
  • 在系统崩溃后,InnoDB 会读取重做日志文件,重新应用未写入磁盘的修改操作,确保数据的一致性

Redo Log的写入流程

  • 日志记录生成
    • 当事务修改数据时,InnoDB 会生成一条重做日志记录,描述对数据页的修改。
  • 日志缓冲写入
    • 重做日志记录首先写入 日志缓冲(Log Buffer),日志缓冲是内存中的一块区域。
  • 日志刷盘
    • 日志缓冲满或事务提交时,InnoDB 会将日志缓冲中的内容写入磁盘的重做日志文件。
    • 日志刷盘的时机由参数 innodb_flush_log_at_trx_commit 控制:
      • =1:每次事务提交时都刷盘(默认,最安全)。
      • =2:每次事务提交时写入操作系统缓存,但不刷盘。
      • =0:每秒刷盘一次。

2. Undo Log(回滚日志)

回滚日志是 InnoDB 用于实现事务 原子性(Atomicity)多版本并发控制(MVCC) 的机制。它记录了事务修改前的数据状态,用于回滚事务和提供一致性视图

Undo Log的作用

  • 事务回滚:当事务需要回滚时,InnoDB 使用回滚日志将数据恢复到修改前的状态。
  • MVCC:为并发事务提供一致性视图,支持非锁定读
  • 在系统崩溃后,InnoDB 会检查未完成的事务,并使用回滚日志回滚这些事务,确保数据的一致性

Undo Log的结构

  • 回滚日志存储在 回滚段(Undo Segments) 中,回滚段位于系统表空间或独立的回滚表空间中。
  • 每个事务会分配一个回滚段,用于存储其修改前的数据

Undo Log的写入流程

  • 日志记录生成
    • 当事务修改数据时,InnoDB 会生成一条回滚日志记录,记录修改前的数据状态。
  • 日志存储
    • 回滚日志记录存储在回滚段中。
  • 日志清理
    • 当事务提交后,回滚日志不会立即删除,而是保留一段时间,用于支持 MVCC。
    • 当没有事务需要旧版本的数据时,回滚日志会被清理

锁机制

InnoDB 的 锁机制 是其实现事务 隔离性(Isolation) 和 并发控制 的核心组件。InnoDB 支持多种类型的锁,包括 行级锁、表级锁 和 意向锁,这些锁共同协作,确保并发事务的正确性和一致性。

锁的类型

  1. 行级锁(Row-Level Lock)
    • 共享锁(S Lock,Shared Lock)
      • 允许事务读取一行数据。
      • 多个事务可以同时持有共享锁。
      • 语法:SELECT ... LOCK IN SHARE MODE
    • 排他锁(X Lock,Exclusive Lock)
      • 允许事务更新或删除一行数据。
      • 只有一个事务可以持有排他锁,其他事务不能加任何锁。
      • 语法:SELECT ... FOR UPDATE
  2. 表级锁(Table-Level Lock)
    • 表级共享锁(Table S Lock)
      • 允许事务读取整个表的数据。
      • 多个事务可以同时持有表级共享锁。
    • 表级排他锁(Table X Lock)
      • 允许事务更新或删除整个表的数据。
      • 只有一个事务可以持有表级排他锁
  3. 意向锁(Intention Lock)
    • 意向锁是表级锁,用于表明事务将在表中的某些行上加锁。
    • 意向共享锁(IS Lock,Intention Shared Lock)
      • 表明事务将在某些行上加共享锁。
    • 意向排他锁(IX Lock,Intention Exclusive Lock)
      • 表明事务将在某些行上加排他锁
  4. 间隙锁(Gap Lock)
    • 间隙锁是对 索引记录之间的间隙 加锁,而不是对具体的记录加锁
    • 间隙锁锁定的是一个范围,防止其他事务在这个范围内插入新的记录
    • 间隙锁是 共享锁,多个事务可以同时对同一个间隙加间隙锁
  5. 临键锁(Next-Key Lock)
    • 临键锁是 记录锁(Record Lock) 和 间隙锁(Gap Lock) 的结合
    • 它锁定的是 索引记录本身 以及 该记录之前的间隙
    • 通过锁定记录和间隙,确保在事务执行期间,不会有新的记录插入到锁定范围内
    • 在范围查询或等值查询时,临键锁可以防止其他事务插入或修改数据
    • 临键锁是 排他锁,只有一个事务可以持有临键锁

锁的实现

  1. 行级锁的实现

    • InnoDB 的行级锁是通过 索引 实现的。
      • 如果查询使用了索引,InnoDB 会对索引记录加锁。
      • 如果查询没有使用索引,InnoDB 会对整个表加锁(表级锁)。
      • 如果查询没有使用索引(例如全表扫描),InnoDB 会对整个表的 所有间隙 加间隙锁
    • 行级锁的粒度较小,支持高并发
  2. 表级锁的实现

    • 表级锁是对整个表加锁,粒度较大,通常用于 DDL 操作(如 ALTER TABLE)。
    • 表级锁会阻塞其他事务对表的访问,影响并发性能
  3. 意向锁的实现

    • 意向锁是表级锁,用于协调行级锁和表级锁。
    • 当事务需要在某些行上加锁时,首先在表上加意向锁,表明其意图

死锁处理

  • InnoDB 会自动检测死锁,并选择一个事务进行回滚(通常是影响较小的事务)。
  • 可以通过以下方式减少死锁:
    • 按照相同的顺序访问资源。
    • 使用 SELECT ... FOR UPDATELOCK IN SHARE MODE 明确加锁

MyISAM

特点:

  1. 表级锁

    • MyISAM 使用 表级锁,而不是行级锁。
      • 当对表进行写操作(如 INSERTUPDATEDELETE)时,会对整个表加锁。
      • 当对表进行读操作时,会对整个表加共享锁。
    • 表级锁的优点是实现简单,开销小;缺点是并发性能较差,写操作会阻塞读操作
  2. 不支持事务

    • MyISAM 不支持事务,也不支持 ACID 特性。
      • 如果操作过程中发生崩溃,数据可能会不一致。
      • 无法回滚操作
  3. 不支持外键

如何选择

  • 选择 InnoDB
    • 如果需要事务支持、高并发读写、数据一致性保证或外键约束。
    • 大多数现代应用都推荐使用 InnoDB。
  • 选择 MyISAM
    • 如果应用是读密集型、不需要事务支持、或者需要全文索引。
    • 适合小型项目或对性能要求不高的场景。
posted @ 2025-03-17 21:37  QAQ001  阅读(58)  评论(0)    收藏  举报