- 《MySQL是怎样运行的》
InnoDB 4种行格式
Compact、Redundant、Dynamic和Compressed
索引页(即数据页)
页作为磁盘与内存之间交互的基本单位(默认16KB)
各个数据⻚可以组成⼀个双向链表,⽽每个数据⻚中的记录会按照主键值从⼩到⼤的顺序组成⼀个单向链
表,每个数据⻚都会为存储在它⾥边⼉的记录⽣成⼀个⻚⽬录,在通过主键查找某条记录的时候可以在⻚⽬录中使⽤⼆分法快速定位到对应的槽,然后再遍历该槽对应分组中的记录即可快速找到指定的记录(实体书本90页)
由一个简单索引方案(页码-目录)逐渐形成B+树(索引即数据)
这部分强烈建议看原文(92页),娓娓道来,水到渠成的感觉!
不用二叉树、红黑树、B树、跳表的原因
- 二叉树可能会退化成链表,查询效率降低
- 红黑树、B树、跳表的高度会很高,IO次数较多(Redis用红黑树、跳表等结构,因为是内存操作)
MyISAM中索引(索引和数据独立)
-
数据文件
- 索引文件只包含数据的行号,不会包含原始数据
MVCC (多版本并发控制)
- 什么是多版本并发控制呢 ?其实就是在每一行记录的后面增加两个隐藏列,记录创建版本号和删除版本号
- MVCC是可重复读和读已提交的两个隔离级别的实现原理
- 可重复读的事务,每个事务只有一个版本号;而读已提交中,每个语句有一个版本号
- 在插入操作时 : 记录的创建版本号就是事务版本号
id | name | create_version | delete_version | undo_log_指针 |
---|---|---|---|---|
1 | test | 1 | xxx |
- 在更新操作的时候,采用的是先标记旧的那行记录为已删除,并且删除版本号是事务版本号;
然后插入一行新 的记录的方式。
id | name | create_version | delete_version | undo_log_指针 |
---|---|---|---|---|
1 | test | 1 | 2 | yyy |
1 | new_value | 2 | yyy |
- 删除操作的时候,就把事务版本号作为删除版本号。
id | name | create_version | delete_version | undo_log_指针 |
---|---|---|---|---|
1 | new_value | 2 | 3 | zzz |
- 读操作的筛选
4.1 InnoDB 只查找create_version早于当前事务版本的数据行
4.2 行的delete_version要么未定义,要么大于当前事务版本号
间隙锁
Select * from emp where empid > 100 for update;
假如符合该条件的empid只有101这一条数据,InnoDB不仅会对符合条件的empid值为101的记录加锁,也会对empid大于 101(这些记 录并不存在)的“间隙”加锁
为什么MVCC在可重复读的隔离级别下,不能完全解决“幻读”问题?
时间 | 事务1 | 事务2 |
---|---|---|
1 | select * from user where name = 'new_value',结果为1条 | |
2 | insert into user values (2,'new_value') | |
3 | select * from user where name = 'new_value',结果依然为1条 | |
4 | update user set name = 'new_value',此语句会将所有数据的version设置为事务1的version | |
5 | select * from user where name = 'new_value',结果变为2条 |
- 虽然并行写会出现数据与预期不一致的结果,MySQL这么设计,是为了减少回滚带来的性能下降
事务ACID的实现原理
- A原子性:事务过程中,一旦失败,undo_log恢复数据
- C一致性:数据库通过字段长度和类型等保证其数据的完整性约束
- I隔离性:MVCC 机制解决了脏读和不可重复读,行锁/表锁的方式解决了幻读
- D持久性:redo_log防止宕机丢失数据
RedoLog相关内容
保证“事务一旦提交,数据一定持久化”有两个办法:
- 在事务提交完成之前把该事务所修改的所有页面都刷新到磁盘
由于InnoDB中是以页为单位来进行磁盘IO的,且修改页可能并不相邻,存在大量随机IO,性能较低 - 持久化RedoLog
优点有:顺序IO,且占用空间小
事务中Redo Log流程
- 第1步:先将原始数据从磁盘中读入内存中来,修改数据的内存拷贝
- 第2步:生成一条重做日志并写入RedoLog Buffer,记录的是数据被修改后的值
- 第3步:当事务commit时,更新BinLog,然后将RedoLog Buffer中的内容刷新到 RedoLog File,对 RedoLog File采用追加写的方式
- 第4步:定期将内存中修改的数据刷新到磁盘中