前言
学习笔记仅是自己在学习后,留下的认为需要留意的标记,方便我回忆,简单的点会直接舍弃解释。复杂的会有简单描述用于快速复习。
索引
- B+树
- 对原B+Tree结构修改
- 有序双向循环链表
- 索引左闭合区间
- 叶子节点头节点指针
- 聚簇/非聚簇索引叶子节点区别
- 回表
- 覆盖索引
- 对原B+Tree结构修改
- Page页
- 默认16KB(页分裂、页合并)
- 页数据加载至内存(二分查找)
- 组合索引
- 最左匹配原则
- 所有字段按索引顺序组合为一个宽列
- Explain中通过key_len长度判断组合索引一共使用到几个字段
- 范围查询"<"、">"导致组合索引无法使用全的问题
- 代码先行,索引后上
- 索引合并
事务
- ACID
- 原子性(Atomicity)
- 一致性(Consistent)
- 隔离性(Isolation)
- 持久性(Durable)
- 隔离级别
- 读未提交 (脏读、不可重复读、幻读)
- 读已提交(不可重复读、幻读)
- 可重复读(幻读:幻影行,查不到,但是无法插入成功幻影行数据,可以update幻影行数据)
- MySQL通过MVCC实现
- undo日志版本链
- 事务开启时记录当前未提交的最小事务ID、当前已创建的最大事务ID、所有未提交的事务ID
- 事务回滚基于undo log
- 数据删除也会在undo log中新增 日志,标记某一行数据被删除
- 可串行化(所有事务都加 X锁,解决幻读)
锁
- 乐观锁
- 自旋、ABA
- 悲观锁
- 读锁(共享锁,S锁(Shared)):针对同一份数据,多个读操作可以同时进行而不会互相影响
- 写锁(排它锁,X锁(eXclusive)):当前写操作没有完成前,它会阻断其他写锁和读锁
- 表锁
- 开销小,加锁快,不会出现死锁
- 锁定粒度大,发生锁冲突的概率最高,并发度低
- 一般用在整表数据迁移的场景
- 拿无索引的字段为update条件时会出现表锁
- 行锁
- 开销大,加锁慢,会出现死锁
- 锁定粒度最小,发生锁冲突的概率最低,并发度高
- 间隙锁、临键锁
- 加在索引上
- (L, R]
- R>当前索引最大值则 R = +∞(正无穷大)
InnoDB内存结构
- Buffer Pool:内存缓冲区,能够提高读写效率
- 缓冲池Page页LRU淘汰(Innodb对LRU进行了优化)
- 老生代和新生代(解决预读失效问题)
- 页被访问,且在老生代停留时间超过配置阈值的,才进入新生代,以解决批量数据访问,大量热数据淘汰的问题
- 修改数据页正好在缓冲池内
- 缓冲池页数据修改 -> redo log写入(redo log是顺序写)
- 定期刷磁盘 或 缓冲池LRU数据淘汰,会将“脏页”刷回磁盘
- 缓冲池Page页LRU淘汰(Innodb对LRU进行了优化)
- Change Buffer:当数据页不是唯一索引以及不存在重复数据的情况下,会缓存修改记录,提升更新语句(Insert、Delete、Update)的执行速度。
- 因为唯一索引需要判断唯一性,所以一定会将数据读到内存中(Buffer Pool)
- 非唯一普通索引页不在缓冲池中,对页进行了写操作
- 写入 Change Buffer -> 写入redo log
- 数据页被查询时合并至Buffer Pool
- 有一个后台线程,认为数据库空闲时缓冲池数据刷盘
- 数据库缓冲池不够用时缓冲池数据刷盘
- 数据库正常关闭时缓冲池数据刷盘
- redo log写满时缓冲池数据刷盘(几乎不会出现redo log写满,如果写满数据库处于无法写入的不可用状态)
- Adaptive Hash Index:InnoDB会监控对表上各索引页的查询,如果观察该数据被访问的频次符合规则,那么就建立哈希索引来加快数据访问的速度。
- Redo Log Buffer: 用于保存要写入磁盘的日志文件数据,日志缓冲区的内容会定期刷新到磁盘中。如果需要更新、插入、删除多行数据的事务,则增加日志缓冲区可以节省磁盘I/O。
- 数据库异常奔溃,能够从redo log中恢复数据;