Mysql随笔

InnoDB引擎

逻辑存储结构

image-20231113114622728

表空间

表空间是InnoDB存储引擎的最高层,在磁盘中每个表都有一张表空间文件(xxx.ibd),一个MYSQL可以有多个表空间,表空间主要用于存储记录、索引等数据。

段,分为:数据段、索引段、回滚段,InnoDB是索引组织表,数据段也就是B+树的叶子节点,索引段为B+树的非叶子节点,段用来管理多个区。

区,是表空间的单元结构,每个区大小为1M,默认情况下Mysql的InnoDB引擎页大小为16K,也就是一个区中一共有64个连续的页。

页,是InnoDB最小的磁盘管理单元,每个也为16KB,为了保证页的连续性,InnoDB引擎在申请页大小的时候会连续申请4-5个连续的页。

行,InnoDB 存储引擎数据是按行进行存放的。

在行中,默认有两个隐藏字段:

  • Trx_id:每次对某条记录进行改动时,都会把对应的事务id赋值给trx_id隐藏列。
  • Roll_pointer:每次对某条引记录进行改动时,都会把旧的版本写入到undo日志中,然后这个 隐藏列就相当于一个指针,可以通过它来找到该记录修改前的信息。

架构

之所以Mysql采用InnoDB作为默认的数据引擎,我想是以为InnoDB出众的事务处理能力,具有崩溃恢复的特性。

8.0

image-20231113115754673

5.5

image-20231113115823194

内存结构

Buffer Pool

是一个缓冲区,由于物理磁盘和内存之间的访问速度差距很大,为了尽可能的弥补两者之间的IO差值,就需要把经常用到的数据加载到缓冲池中,避免每次访问都进行磁 盘I/O。

在InnoDB的缓冲池中不仅缓存了索引页和数据页,还包含了undo页、插入缓存、自适应哈希索引以及 InnoDB的锁信息等等。

缓冲池 Buffer Pool,是主内存中的一个区域,里面可以缓存磁盘上经常操作的真实数据,在执行增 删改查操作时,先操作缓冲池中的数据(若缓冲池没有数据,则从磁盘加载并缓存),然后再以一定频 率刷新到磁盘,从而减少磁盘IO,加快处理速度。

缓冲池以Page页为单位,底层采用链表数据结构管理Page。根据状态,将Page分为三种类型:

  • free page:空闲page,未被使用。
  • clean page:被使用page,数据没有被修改过。
  • dirty page:脏页,被使用page,数据被修改过,也中数据与磁盘的数据产生了不一致。

事务

  • 原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败。
  • 一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态。
  • 隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环 境下运行。
  • 持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。

上述四大特性分为两部分:

  • 原子性、一致性、持久性是由InnoDB中的redo log、undo log日志来保证的。
  • 持久性是通过数据库的锁加上MVCC机制来实现的

image-20231113151752283

redo log

个人理解:

  1. 缓冲数据:

    当我们进行一批SQL操作的时候,例如update、delete操作等,内存中的缓冲池(Buffer Pool)通过后台线程从磁盘结构中读取ibd文件中的数据缓冲到 Buffer Pool当中。

  2. 执行SQL语句

    没有RedoLog:

    直接执行SQL语句操作缓冲区中的数据,此时缓冲区数据产生脏页,然后MySQL会通过一定的时候讲脏页刷新到磁盘当中,从而保证了数据的一致,但是脏页的数据并不是实时刷新的,而是通过一段时间后通过后台线程将脏页数据刷新到磁盘中,如果这时脏页数据写入磁盘出错呢?如何保证数据的持久性?一致性?

直接执行SQL语句操作缓冲区中的数据,此时缓冲区数据产生脏页,然后就会记录在RedoLogBuffer中,RedoLogBuffer中就会记录数据页的变化,当事务在提交的时候,会将数据页直接刷新到磁盘当中,持久化的保存在磁盘当中,如果在BufferPoll将脏页刷新数据到磁盘出错了,可以通过数据页来恢复数据,redoLog中主要是记录了当次数据的变化,所以能通过RedoLog日志文件进行数据恢复,总结来说RedoLog就是在缓冲区中脏页往磁盘中刷新数据出错的时候进行数据恢复,如果说每一次的事务操作都直接从缓冲区写入到磁盘,会导致IO效率问题,因为每一个事务操作操作一组数据的时候通常会操作很多条记录,这些记录都是随机操作数据页的,这个时候就会涉及到大量的随机磁盘IO,导致性能降低,如果通过RedoLog日志文件将每次的操作记录都异步的追加到磁盘中,那么他就是顺序的磁盘IO操作了,性能是要高于随机磁盘IO的,这种也叫做WAL(先写日志),

总结:

在Mysql中RedoLog是一种事务日志,用于记录已经提交的事务所做的修改操作。它的主要作用是在数据库发生崩溃或意外关闭时,通过重新执行已提交的事务来恢复数据库的一致性。

当一个事务开始时,MySQL会将该事务所做的修改操作记录到redo log中,而不是立即将数据写入磁盘。这样可以提高数据库的性能,因为磁盘写入是比较耗时的操作

redo log的写入是顺序的,按照事务的顺序将日志写入磁盘。这样可以减少磁盘寻道的时间,提高写入性能

整理执行过程是这样子的,在进行一个事务操作的时候,首先内存中的bufferPoll会从磁盘中的ibd文件获取数据到缓冲池,在执行增删改操作,执行完了之后,会将缓冲池中的脏页也就是已修改的数据页,记录到RedoLog日志文件中去,当事务提交完了之后过了一段时间,缓冲池中的脏页才会写入到磁盘中去,如果此时磁盘写入出错,那么就会重新执行事务,将redolog中的数据从磁盘中读取到缓冲区在重新执行写入到磁盘的操作,这样就保证了数据的持久性。

MySQL会定期将redo log的数据刷新到磁盘,以保证数据的持久性。刷新操作可以在事务提交时触发,当redo log的空间不足时触发,或者由后台线程定期触发。

当数据库发生崩溃或意外关闭时,MySQL可以通过redo log来恢复数据的一致性。在数据库重新启动时,MySQL会检查redo log,将未完成的事务重新执行,将数据恢复到崩溃前的状态。综上所述,redo log在MySQL中起着重要的作用,用于记录已提交事务的修改操作,并在数据库崩溃时保证数据的一致性恢复。

因为在业务操作中,我们操作数据一般都是随机读写磁盘的,而不是顺序读写磁盘。 而redo log在 往磁盘文件中写入数据,由于是日志文件,所以都是顺序写的。顺序写的效率,要远大于随机写。

undo log

回滚日志,用于记录数据被修改前的信息 , 作用包含两个 :

  • 提供回滚(保证事务的原子性) 和 MVCC(多版本并发控制) 。 undo log和redo log记录物理日志不一样,它是逻辑日志。可以认为当delete一条记录时,undo log中会记录一条对应的insert记录,反之亦然,当update一条记录时,它记录一条对应相反的 update记录。当执行rollback时,就可以从undo log中的逻辑记录读取到相应的内容并进行回滚。
  • Undo log销毁:undo log在事务执行时产生,事务提交时,并不会立即删除undo log,因为这些 日志可能还用于MVCC。 Undo log存储:undo log采用段的方式进行管理和记录,存放在前面介绍的 rollback segment 回滚段中,内部包含1024个undo log segment。

MVCC

当前读

当前读读到的就是最新的数据,

当前读

在测试中我们可以看到,即使是在默认的RR隔离级别下,事务A中依然可以读取到事务B最新提交的内 容,因为在查询语句后面加上了 lock in share mode 共享锁,此时是当前读操作。当然,当我们 加排他锁的时候,也是当前读操作。

快照读

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

  • Read Committed:每次select,都生成一个快照读。
  • Repeatable Read:开启事务后第一个select语句才是快照读的地方。
  • Serializable:快照读会退化为当前读。

MVCC

MVCC主要实现依赖于表中的三个隐式字段,undolog ,readview

image-20231113174337202

而上述的前两个字段是肯定会添加的, 是否添加最后一个字段DB_ROW_ID,得看当前表有没有主键, 如果有主键,则不会添加该隐藏字段。

undolog

回滚日志,在insert、update、delete的时候产生的便于数据回滚的日志。 当insert的时候,产生的undo log日志只在回滚时需要,在事务提交后,可被立即删除。 而update、delete的时候,产生的undo log日志不仅在回滚时需要,在快照读时也需要,不会立即 被删除。

版本链

image-20231113174626924

RC:每一次提交都会生成一个新的ReadView

RR:可重复读也就是因为每次都是复用一个ReadView

readview

image-20231113174644430

image-20231113174654543

trx_id 代表当前undolog版本链对应事务ID。

不同的隔离级别,生成ReadView的时机不同:

  • READ COMMITTED :在事务中每一次执行快照读时生成ReadView。
  • REPEATABLE READ:仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView。

RC隔离级别

RC隔离级别下,在事务中每一次执行快照读时生成ReadView。

image-20231113174815901

RR隔离级别

RR隔离级别下,仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView。 而RR 是可 重复读,在一个事务中,执行两次相同的select语句,查询到的结果是一样的。

image-20231113174838621