Mysql随笔
![](https://bu.dusays.com/2023/10/10/6524fcbbbec01.jpg)
Mysql随笔
SerMsInnoDB引擎
逻辑存储结构
表空间
表空间是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
5.5
内存结构
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机制来实现的
redo log
个人理解:
缓冲数据:
当我们进行一批SQL操作的时候,例如update、delete操作等,内存中的缓冲池(Buffer Pool)通过后台线程从磁盘结构中读取ibd文件中的数据缓冲到 Buffer Pool当中。
执行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
而上述的前两个字段是肯定会添加的, 是否添加最后一个字段DB_ROW_ID,得看当前表有没有主键, 如果有主键,则不会添加该隐藏字段。
undolog
回滚日志,在insert、update、delete的时候产生的便于数据回滚的日志。 当insert的时候,产生的undo log日志只在回滚时需要,在事务提交后,可被立即删除。 而update、delete的时候,产生的undo log日志不仅在回滚时需要,在快照读时也需要,不会立即 被删除。
版本链
RC:每一次提交都会生成一个新的ReadView
RR:可重复读也就是因为每次都是复用一个ReadView
readview
trx_id 代表当前undolog版本链对应事务ID。
不同的隔离级别,生成ReadView的时机不同:
- READ COMMITTED :在事务中每一次执行快照读时生成ReadView。
- REPEATABLE READ:仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView。
RC隔离级别
RC隔离级别下,在事务中每一次执行快照读时生成ReadView。
RR隔离级别
RR隔离级别下,仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView。 而RR 是可 重复读,在一个事务中,执行两次相同的select语句,查询到的结果是一样的。