6

MYSQL-->InnoDB引擎底层原理 - wdadwa

 1 year ago
source link: https://www.cnblogs.com/wdadwa/p/MYSQL_Learning_13.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

逻辑存储结构

逻辑存储结构图

image
  1. 表空间文件在Linux下存放在 /var/lib/mysql文件中的 xxx.ibd 文件就是表空间文件

    表空间文件用来存储,记录,索引等数据。

  2. 段分为,数据段(Leaf node segment)索引段(Non-leaf node segment)回滚段(Rollback segment),InnoDB是索引组织表,数据段就是B+树的叶子节点,索引段就是非叶子节点,段用来管理Extend(区)。

    一个段相当于一张表

  3. 区是表空间的单元结构,每个区大小为1M,默认情况下InnoDB存储引擎页大小为16k,一个区一共16个连续的页。

  4. 页,是InnoDB存储引擎磁盘管理的最小单元。

    每个区默认16KB,为了保证页的连续性,InnoDB存储引擎每次从磁盘申请4到5个区。

  5. 行指的是InnoDB存储的数据

    表结构中俩个隐藏字段

    Trx_id:最后一次操作事务的id

    Roll pointer:指针,指向增删改之前的数据,可以拿这个找到修改之前的数据。

MySQL5.5版本后,默认使用InoDB存储引擎。

它擅长事务处理,具有崩溃恢复性特性!

下图为InnoDB架构图,左边为内存结构,右边为磁盘结构。

image
image

Buffer Pool(缓冲池)

缓冲池是主内存的一个区域,里面可以缓存磁盘上经常操作的真实数据。

在执行增删改查操作的时候,先操作缓存池中的数据(如果没有,从磁盘加载并且缓存)

然后以一定频率刷新到磁盘,从而减少磁盘IO,加快处理速度

在缓存池中有一块一块的,这个是页。

缓存池以页为单位,底层采用链表数据结构管理Page。

根据状态将Page分为三类:

  1. free page :空闲页,未被使用的页。
  2. clean page :被使用的页,数据没有被修改过。
  3. dirty page :脏页,被使用的页,页中数据和磁盘中数据不一致。

Change Buffer(更改缓存区)

介绍

更改缓存区,主要针对非唯一的二级索引。

在执行DML语句时,如果这些数据页不在Buffer Poor中,不会直接操作磁盘,而是将数据变更在更改缓存区Change Buffer中

在未来数据被读取的时候,再将数据合并恢复到Buffer Pool中,再将合并后的数据刷新到磁盘中。

意义

和聚集索引不同,二级索引是非唯一的!

并且二级索引以相对随机的顺序插入。

同样的删除和更新可能会影响索引树中不相邻的二级索引页,如果每一次都操作磁盘,会造成大量磁盘IO。

有了ChangeBuffer后,我们可以在缓冲池中进行合并处理减少磁盘IO

Log Buffer(日志缓存区)

日志缓存区,用来保存要写入磁盘中的log日志数据(redo log 、undo log)。

默认大小16MB,日志缓存区的日志会定期刷新到磁盘中。如果需要更新、插入、或删除多行的事务,增加日志缓存区大小可以节约磁盘IO

在系统变量中设置即可

innodb_log_buffer_size 缓存区大小

innodb_flush_log_at_trx_commit 日志刷新到磁盘的时机

1代表的是日志在每次事务提交时写入并刷新到磁盘

0代表每秒将日志写入并刷新到磁盘一次

2代表日志在每次事务提交后写入,并且每秒刷新到磁盘一次

Adaaptive Hash index(自适应哈希索引)

自适应哈希索引,用于优化Buffer Pool数据的查询。

InnoDB存储引擎会监控表上各索引页的查询,如果观察到hash索引可以提高速度,就会建立hash索引。

这个叫自适应哈希索引

可以在系统变量中查询是否开启自适应哈希索引

关键字是 adaptive_hash_index

image

System Tablespace(系统表空间)

系统表空间是change Buffer的存放区域。

关键字:innodb_data_file_path

系统表空间存放路径

File-Per-Table Tablespaces

存放每个表的独立表空间

默认是开启的-->开启后代表每一张表都会生成对应的表空间文件。

xxx.ibd结尾的文件都是表的表空间文件

查看变量 innodb_file_per_table看是开的还是关的

里面存放了表结构,表数据,索引

General Tablespaces(通用表空间)

通用表空间,需要手动通过

create tablespace 表空间名 add datafile '表空间对应的磁盘文件.ibd' engine=innodb; 

创建通用表空间。

在创建表的时候可以指定该表空间

create table 表名(
	字段....
)engine=存储引擎 tablespace 表空间名;

Undo Tablespaces(撤销表空间)

撤销表空间,MYSQL实例在初始化的时候会自动创建俩个默认的undo表空间(初始大小16M)用来存放undo log日志

默认叫 undo_001和undo_002

Temporary Tablespaces(临时表空间)

用来存储用户创建的临时表。

Doublewrite Buffer Files(双写缓冲区)

双写缓冲区,innoDB存储引擎会将数据页冲Buffer Pool刷新到磁盘前,先将数据页写入双写缓冲区文件中,便于系统异常时恢复数据。

双写缓冲区文件:xxx.dblwr

Redo Log(重做日志)

用来实现事务的持久性。

该日志文件由俩个部分组成:重做日志缓冲区(redo log buffer)重做日志文件(redo log)

重做日志缓冲区:是在内存中

重做日志文件:是在磁盘在

事务提交后会把所有修改的信息存放在该日志中,用于刷新脏页到磁盘,发生错误时,进行数据恢复使用

以循环的方式写入重做文件,涉及到俩个文件:ib_logfile0和ib_logfile1

作用:将Innodb缓冲池的数据在合适的时机刷新到磁盘文件中。

分为四类:

  1. Master Thread

    核心后台线程,负责调度其他线程。还负责将缓冲池中的数据异步刷新到磁盘中,保持数据的一致性。

    还包括,脏页的刷新合并插入缓冲undo页的回收

  2. IO Thread

    在InoDB存储引擎中大量使用了AIO来处理IO请求,这样可以极大提高数据库性能,IO Thread主要负责以下IO请求的回调

    线程类型 默认个数 责任
    Read thread 4 负责读操作
    write thread 4 负责写操作
    Log thread 1 负责将日志缓冲区刷新到磁盘
    Insert buffer thread 1 负责将写缓冲区内容刷新到磁盘

    关于AIO(异步IO)和IO(同步IO)

    同步就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。也就是必须一件一件事做,等前一件事做完了才能做下一件事。

    异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果(在此期间,调用者可以去干一些别的事情)。实际处理这个调用的部件在完成后,通过状态、通知和回调。

  3. Purge Thread

    主要用于回收事务已经提交的undo log,在事务提交之后,undo log可能不需要了,就用这个来回收。

  4. Page Cleaner Thread

    协助Master Thread刷新脏页到磁盘的线程,他可以减轻Master Thread的工作压力,减少阻塞。

在增删改查的时候,会操作内存结构区域,如果里面没有数据就从。磁盘结构中加载,然后进行操作。

最后在特定时间会自动从内存结构中刷新到磁盘中。在磁盘中进行持久化保存下来

事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有操作作为一个整体一起向系统提交撤销操作请求。

这些操作要么同时成功要么同时失败。

事务的四大性质

  1. 原子性

    事务是不可分割的最小操作单元,要么全部成功,要么全部失败。

  2. 一致性

    事务完成时,必须使所有的数据保持一致性。

  3. 隔离性

    数据库系统提高隔离机制,保证事务在不受外部并发操作影响的独立环境运行。

  4. 持久性

    事务一旦提交或回滚,他对数据库的数据改变就是永久的。

事务的原理

事务的原子性,一致性,持久性都是由redo log和undo log实现的

事务的隔离性是由 锁机制和MVCC实现的。

redo log

重做日志,记录的是事务提交时数据页的物理修改用来实现事务的持久性

该日志文件由俩部分组成:

  1. 重做日志缓冲(redo log buffer)

  2. 重做日志文件(redo log file)

当事务提交后会把所有修改信息都存放到该日志文件中,用于在刷新脏页到磁盘中,发生错误时,进行数据的恢复。

大概流程:

  1. 客户端A对innoDB存储引擎的表进行增删改事务操作

  2. 先访问内存结构中的缓冲池,如果增删改数据在其中不存在,

    就会从磁盘中读取数据再刷新到缓冲池(这个数据必须是唯一索引,否则会先进入到更改缓冲区)

  3. 在缓冲池中变成脏页,并记录在redolog buffer中,后直接刷新到磁盘中

  4. 如果脏页在一段时间后刷新到磁盘中报错了,可以通过redo log进行恢复。

image

使用redo log直接刷新到磁盘结构的好处

事务一般是一组多条的增删改查操作,故事务提交的时候会随机的操作多条的记录。

这些记录会操作多条数据页,这样会产生大量的随机磁盘IO

而直接将redo log文件异步刷新到磁盘io中,由于它是日志文件,日志文件都是追加的,此时是顺序磁盘IO,这样会节约大量的磁盘IO

这种机制叫WAL(Write-Ahead Logging)(先写日志)

然后过一段时间脏页日志才会刷新到磁盘中。

故俩份日志是循环清理的

事务的redo log日志是为了解决脏页刷新到磁盘出错时进行数据的恢复使用的,用来保证数据的持久性

undo log

undo log日志是用来保证事务的原子性的。

undo log也叫回滚日志,用于记录数据被修改前的信息,作用为:提供回滚和MVCC

redo log记录的是物理日志!

undo log记录的是逻辑日志,可以认为当执行delete 一条记录时,undo log中会记录一条对应的insert记录,反之同理。

当执行rollback时,就可以从undo log中的逻辑记录读取到对应内容,从而进行回滚。

Undo log销毁

undo log在事务执行时产生,事务提交时,并不会马上删除undo log,因为这些日志可能还用于MVCC

Undo log存储

undo log采用,的方式进行管理和记录,存放在前面介绍的rollback segment回滚中,内部包含了1024个undo log segment

这个段是逻辑存储结构的段哦~

MVCC(多版本并发控制)(高频面试题)

MVCC的几个基本概念

我们读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁,

对于我们日常的操作,如:select...lock in share mode(共享锁),select...for update,update,insert,delete(排他锁)

都是一种当前读。

案例:

在RR的隔离级别下

  1. 事务A进行查询操作,事务B进行更新操作并提交事务
  2. 事务A使用当前读select...此时读取的是事务B更新之前的数据(原因是隔离级别)
  3. 事务A使用select...lock in share mode(当前读)此时读取的是事务B更新之后的数据。

简单的select(不加锁)就是快照读,读取的是记录数据的可见版本,可能是历史数据,不加锁是非阻塞读。

  1. Read Committed隔离级别:每次select都生成一个快照读
  2. Repeatable Read隔离级别:开启事务后第一个select语句才是快照读的地方(后续查的就是这个快照数据)
  3. Serializable隔离级别:快照读会退化为当前读

MVCC介绍

全称Multi-Version Concurrency Control 多版本并发控制。

指的是维护一个数据的多个版本,使得读写操作没有冲突

快照读为MYSQL实现MVCC提供了一个非阻塞读功能,MVCC的具体实现,还需要依赖,数据库的三个隐藏字段

undo log日志readView

MVCC-实现原理

记录中的隐藏字段

当我们创建了表除了自己本身创建的字段,innoDB引擎会自动给我们创建三个字段

  1. DB_TRX_ID
  2. DB_ROLL_PTR
  3. DB_ROW_ID
隐藏字段 含义
DB_TRX_ID 最近修改事务ID,记录插入这条记录或者最后一次修改该记录的ID(事务id)
DB_ROLL_PTR 回滚指针,指向这条记录的上一个版本,用于配合undo log指向上一个版本
DB_ROW_ID 隐藏主键,如果表结构没有指定主键,就会生成该隐藏字段

可以查看表空间文件内容来查看隐藏字段信息

事务id是自增的!

在MYSQL中提供了一个命令来查看表空间文件的记录信息

ibd2sdi xxx.ibd

undo log版本链

回滚日志,在insert、update、delete的时候产生的便于数据回滚的日志

当insert的时候,产生的undo log日志只在回滚时需要,在事务提交后,可被立即删除。

当update、delete的时候,产生的undo log日志不只是回滚时需要,在快照读时也需要,不会被立即删除。

Undo log版本链

不同事务或相同事务对同一条记录进行修改,会导致该记录的undo log生成一个记录的版本链表,链表的头部是最新的旧记录,链表的尾部是最早的旧记录。

image

执行顺序:

从上到下代表执行顺序。在同一行代表同一时间执行。

ReadView(读视图)

ReadView(读视图)是快照读SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务(未提交的)id

ReadView中包含了四个核心字段

字段 含义
m_ids 当前活跃的事务id集合
min_trx_id 最小活跃的事务ID
max_trx_id 预分配事务ID,当前最大事务ID+1(因为事务ID是自增的)
creator_trx_id ReadView创建者的事务ID

版本链数据访问规则

trx_id 代表的是当前事务的id

用这个id和ReadView的四个核心字段进行比对

  1. trx_id == creator_trx_id 可以访问该版本 这个条件成立代表数据是当前这个事务更改的

  2. trx_id < min_trx_id 可以访问该版本 这个条件成立,说明数据已提交

  3. trx_id > max_trx_id 不可以访问该版本 这个条件成立,说明事务是在ReadView生成之后才开启的

  4. min_trx_id <= trx_id <= max_trx_id 如果trx_id不在m_ids中,就可以访问该版本

    这个条件成立,说明数据已经提交。

根据隔离级别的不同ReadView生成时机不同

  1. 在Read Committed隔离级别下

    在事务第一次执行快照读时都生成一个Read view

  2. 在Repeatable read隔离级别下

    在事务第一次执行快照读时生成,后续复用这个Read view

在RC隔离级别下,版本链访问原理分析

在事务第一次执行快照读时都生成一个Read view

第一个快照读,读取情况

image

这个快照读,根据规则,读取的是0x00002这个地址的数据,对应着事务2修改后的数据内容。

第二个快照读,读取情况

image

这个快照读,根据规则读取到的是,0x00003地址的数据内容,就是事务3修改后的数据。

在RR隔离级别下,版本链访问原理分析

在RR隔离级别下,只有事务在第一次执行快照读的时候生成ReadView,后续复用这个ReadView

image

具体的和RC规则一致,不重复讲解。

隐藏字段+Undo log版本链+ReadView组成MVCC

MVCC+锁构成事务的隔离机制

事务的一致性由Redo log和undo log共同保证


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK