MySQL存储引擎InnoDB原理拆解以及设计深度剖析
MySQL记录存储以Page(页)来划分。
页头:记录页面的控制信息,共占56字节,包含页的左右兄弟页面指针、页面空间使用情况等。
虚记录:最大虚记录:比页面最大主键还大。 最小虚记录:比页面最大主键还小。
记录堆:行记录存储区
自由空间链表:链表关联被删除的记录用来重复利用空间
未分配空间:页面未使用的存储空间。
Slot区:
页尾:存储页面的校验信息8个字节。
页内记录维护
- 顺序保证
- 物理有序:非顺序插入需要移动数据,插入效率较低,查找效率高。
- 逻辑有序:物理无序,链表连接逻辑数据,插入效率高,查找效率低。
- InnoDB采用的逻辑有序,在查询中有进行优化。
page 与 page关联, 主键和主键关联组成顺序。
- 插入策略
- 自由空间链表:InnoDB优先使用
- 未使用空间:
- 页内查询
- 遍历:逻辑有序
通过最小虚记录->最小rec->逐渐递增->最大虚记录
Slot 按照一定规律指向rec,可以对记录按照一定规律进行做类似二分加快查找。
MySQL InnoDB存储引擎内存管理
- 预分配内存空间 以块形式加载
- 数据以页为单位加载
- 数据内外存交换 将内存块内容发生改变后写入磁盘,从磁盘加载新的块加载入硬盘
- 内存池
- 页面映射 磁盘动态映射到内存上
- 页面数据管理
- 空闲页
- 数据页
- 脏页:被修改后的内存页,要将其写入磁盘。
- 数据淘汰(数据内外存交换) 内存页都被使用的情况下或需要加载新的数据时。
- LRU 最长时间最久没有被使用的数据淘汰掉,使用链表结构。
- 保证链尾最长时间没有使用:每次访问链表数据时,将该数据放到链表头,就可以保证链表最尾为最长时间没有被使用。
- 为了解决避免热数据被淘汰:访问时间+频率。(redis采用方式)
- 两个LRU表。一级LRU做内存加载,二级LRU存放一级LRU特定等级的热数据。
8.InnoDB采用的方式:
Buffer Pool 预分配的内存池
Page Buffer Pool的最小单位
Free List 空间page组成的链表
Flush list 脏页链表
Page hash表 维护内存page和文件page的映射关系
LRU 内存淘汰算法:
LRU
(head)LRU_new(tail):(head)LRU_old(tail)以5比3的比例Midpoint指针进行冷热分离
LRU_new
LRU_old
Midpoint
- 页面装载: 磁盘数据到内存,插入到LRU_old头部
- 页面淘汰: Free list中取> LRU中淘汰 > LRU Flush,将要淘汰的数据放入 Free List
3. old 到 new:定义热数据的移动 innodb_old_blocks_time: old区存活时间大于此值,有机会进入new区。存活时间+访问频率 - new 到 old:由Midpoint始终指向8/5位置来区分冷热数据,并结合
free_page_clock:Buffer Pool淘汰页数
LRU_new长度1/4
当前freed_page_clock-上次移动到Header时freed_page_clock LRU_new 长度1/4
MySQL设计思路:减少移动次数
MySQL事务实现原理拆解以及设计深度剖析
事务特性
- A:原子性 全部成功或全部失败
- I:隔离性 并行事务之间互不干扰
- D:持久性 事务提交后数据改变是永久性的
- C:一致性 数据中间状态对外不可见,只有最初状态和最终状态可见
并发问题
- 脏读: 读取到未提交的数据
- 不可重复读: 两次读取结果不同
- 幻读: select操作的到的结果所表征的数据状态无法支持后续的业务操作
隔离级别
- 读取未提交内容 RU:最低隔离级别,会读到其他事务未提交的数据(脏读)
- 读取提交内容 :事务过程中可以读取到其他事务已提交的数据(不可重复读)
- 可重复读 RR:每次读取相同结果集,不管其他事务是否提交(幻读)
- 串行化:事务排队,隔离级别最高,性能最差
MySQL事务实现原理
-
MVCC
- 多版本并发控制:解决读写冲突,有两个隐藏列
- 当前读:读当前版本
- 快照读:读历史版本
- 可见性判断:创建快照这一刻,还未提交的事务。 创建快照之后创建的事务。
- Read View: 快照读 活跃事务列表。 列表中最小事务ID。 列表中最大事务ID。
-
undo log
- 回滚日志
- 保证事务原子性
- 实现数据多版本
- delete undo log;用于回滚,提交即清理
- update undo log:用于回滚,同时实现快照读,不能随便删除
-
redo log
-
实现事务持久性
-
记录修改
-
用于异常恢复
-
循环写文件
- Write Pos:写入位置
- Check Point:刷盘位置
- Check Point -> Write pos: 待落盘数
-
写入流程:
- 记录页的修改, 状态为prepare。
- 事务提交,讲事务记录为commit状态。
-
刷盘时机:
- innodb_fulsh_log_at_trx_commit 0/1/2
-
意义:
- 体积小,记录页的修改,比写入页代价低。
- 末尾追加,随机写变顺序写,发生改变的页不固定。
-
MySQL锁实现原理拆解以及设计深度剖析
InnoDB锁种类
- 锁粒度:
- 行级锁
- 作用在索引上
- 聚簇索引和二级索引
- 间隙锁:锁一个范围
- 解决可重复读模式下的幻读问题
- GAP锁不是加在记录上
- GAP锁锁住的位置,是两条记录之间的GAP
- 保证两次当前读返回一致的记录。
- 表级锁
- RC级别全表扫描“锁表”,所有记录加锁后返回,然后由MySQL Server层进行过滤。
- RR级别全锁,会造成性能大幅下降。
- 行级锁
- 类型:
-
共享锁
-
排它锁(写锁)
当前读:select for update 、update 、delete,为了更新需要加行级锁。
有没有索引/唯一索引/非唯一索引/隔离等级(RC/RR)四种情况来分析锁
RC会出现幻读(删除时插入非唯一索引记录,删除后还可以查到)。 -
死锁:
并发事务,互相拥有对方需要加锁对象的锁,导致加锁失败。
-
网友评论