InnoDB是MySQL的默认存储引擎,支持ACID事务、行级锁定和外键约束,通过多版本并发控制(MVCC)实现高并发性能。InnoDB使用聚簇索引存储数据,具备崩溃恢复能力,确保数据一致性和完整性。其主要特性包括数据和索引的高效管理、可靠的事务处理和强大的并发控制,是处理复杂查询和高并发应用的理想选择。
一、逻辑存储结构
InnoDB 的逻辑存储结构主要包括以下几个层次:表空间、段、区和页。通过这种多层次的逻辑存储结构,实现了高效的数据存储和管理,支持复杂的事务操作和高并发访问。表空间、段、区和页的分层结构确保了数据的有序组织和快速访问,同时减少了磁盘碎片,提高了整体性能和可靠性。
表空间(Tablespace)
├── 段(Segment)
│ ├── 数据段(Data Segment)
│ ├── 索引段(Index Segment)
│ └── 回滚段(Undo Segment)
├── 区(Extent)
│ ├── 页(Page 0)
│ ├── 页(Page 1)
│ └── ...
└── 页(Page 63)
1. 表空间(Tablespace)
表空间是 InnoDB 存储数据的最高层次的逻辑结构。InnoDB 有两种类型的表空间:
- 共享表空间:所有表的数据和索引存储在一个或多个共享表空间文件(如
ibdata1
)。- 独立表空间:每个表的数据和索引存储在各自独立的表空间文件(即
.ibd
文件)。配置参数
innodb_file_per_table
用于设置是使用共享表空间还是独立表空间。
2. 段(Segment)
每个表和索引在表空间中都有自己的段。段是存储特定类型数据的逻辑结构,包括以下几种:
- 数据段:存储表的实际行数据。
- 索引段:存储二级索引的数据。
- 回滚段:存储事务的撤销日志,用于回滚和MVCC。
3. 区(Extent)
区是 InnoDB 分配磁盘存储空间的基本单位,每个区通常为 1MB。区由连续的页组成,用于减少碎片化,提高存储和检索效率。每个区包含 64 个页(每页 16KB)。
4. 页(Page)
页是 InnoDB 存储和管理数据的最小单位,每页大小为 16KB。不同类型的数据存储在不同类型的页中,主要包括:
- 数据页(Data Page):存储实际行数据。
- 索引页(Index Page):存储 B+ 树结构的索引数据。
- 系统页(System Page):存储系统内部信息。
- 撤销页(Undo Page):存储撤销日志,用于事务回滚。
- 插入缓冲位图页(Insert Buffer Bitmap Page):存储插入缓冲相关信息。
- 插入缓冲空闲列表页(Insert Buffer Free List Page):管理插入缓冲的空闲空间。
二、事务的原理
在事务篇1中我们简单的提到过如何保持事务的原子性、一致性、持久性和隔离性,在这章中我们来详细的探讨一下这些都是如何实现的。
2.1 redo log
redo log又叫重做日志,记录的是事务提交时数据页的物理修改,是用来实现事务的持久性。
该日志文件由两部分组成:重做日志缓冲(redolog buffer)以及重做日志文件(redo log file),前者是在内存中,后者在磁盘中。当事务提交之后会把所有修改信息都存到该日志文件中,用于在刷新脏页到磁盘,发生错误时,进行数据恢复使用。
- 首先,客户端向MySQL服务器发起一次请求,它包含多条SQL语句,然后MySQL服务器的内存缓存池中会判断其中是否有即将修改的数据
- 如果在缓存区中没有相关的数据则会向磁盘中加载相关的数据
- 一段时间后,会通过后台线程将数据刷新到磁盘当中
- 但是数据的刷新并不是实时的,如果数据刷新出错,此时事务已经提交成功,返回给用户端成功信息,则持久性就没有得到保证
- redolog的出现保证了持久化
问题:为什么在事务提交之后,不直接持久化到磁盘当中呢?
如果每一次事务提交都将缓存区的数据刷新到磁盘当中会有严重的性能问题,因为在一次事务中会操作大量的随机数据页,这些数据页也会产生大量的随机磁盘IO,性能非常低。
如果先异步刷新redolog日志文件,log日志文件都是顺序追加的,不会有很多的随机IO,他是顺序磁盘IO,这种机制就是WAL机制(先写日志)。
2.2 undo log
undo log又叫回滚日志,用于记录数据被修改前的信息,他是保证事务原子性的重要手段,他的作用包含两个:提供回滚 和 MVCC(多版本并发控制)。
undoloq和redoloq记录物理日志不一样,它是逻辑日志。可以认为当delete一条记录时,undolog中会记录一条对应的insert记录,反之亦然,当update一条记录时,它记录一条对应相反的update记录。当执行rollback时,就可以从undolog中的逻辑记录读取到相应的内容并进行回滚。
Undo log销毁:undolog在事务执行时产生,事务提交时,并不会立即删除undolog,因为这些日志可能还用于MVCC。
Undo log存储:undolog采用段的方式进行管理和记录,存放在前面介绍的 rollback segment 回滚段中,内部包含1024个undolog segment。
三、MVCC
3.1 读取数据
mysql读取数据实际上有两种读取模式:当前读和快照读
- 当前读:每次读取的都是当前最新的数据,但是读的时候不允许写,写的时候也不允许读。对于我们日常的操作,如:select .. lock in share mode(共享锁),select....for update、update、insert、delete(排他锁)都是一种当前读。
- 快照读:读写不冲突,每次读取的是快照数据,简单的select(不加锁)就是快照读,读取的是记录数据的可见版本,有可能是历史数据,不加锁,是非阻塞读。
- Read Committed:每次select,都生成一个快照读。
- Repeatable Read:开启事务后第一个select语句才是快照读的地方,
- Serializable:快照读会退化为当前读。
隔离级别Repeatable Read下(默认隔离级别可重复读):有可能读取的不是最新的数据
Read Committed隔离级(读提交)别下:快照读和当前读读取的数据是一样的,都是最新的。
3.2 共享锁和排它锁
共享锁(S锁):共享 (S) 用于不更改或不更新数据的操作(只读操作),如 SELECT 语句。
如果事务T仅对数据A进行读取,那么会对数据A加上共享锁,之后则其他事务如果要读取数据A的话可以对其继续加共享锁,但是不能加排他锁(也就是无法修改数据)。获准共享锁的事务只能读数据,不能修改数据。
排他锁(X锁):用于数据修改操作,例如 INSERT、UPDATE 或 DELETE。确保不会同时同一资源进行多重更新。
如果事务T对数据A要进行修改,则需要对其添加排它锁,加上排他锁后,则其他事务不能再对A加任任何类型的封锁。获准排他锁的事务既能读数据,又能修改数据。
3.3 MVCC实现原理
1、隐藏字段
隐藏字段可能有三个也可能有两个,这取决于这张表是否有主键,如果有主键则不会生产DB_ROW_ID这个隐藏主键。
2、undo log版本链
一条在数据库中的数据都一定会有两个隐藏字段,事务ID与回滚指针,每提交一个事务就会有一个修改之前的数据提交到undolog中并且新数据回滚指针指向它。
3、readview
接下来看版本链数据访问规则,这readview的核心,也是快照读的核心
来看RC隔离级别下具体用法,首先在执行的快照读生产ReadView,ReadView会有上面那四个信息,然后去undolog中对比可使用版本进行访问。
讲解到这应该就可以明白了,事务篇1中我们提到过,MySQL的RR隔离级别也能避免大部分的幻读,这就是因为MVCC中readview与undolog日志配合来实现的,RR隔离级别下的快照读定格了一条数据,即使后续有并发事务修改,读取的也是之前快照读定格的那一条数据。
事务篇2InnoDB引擎结束,感谢观看,如有错误欢迎斧正。