这是我参与「第五届青训营 」伴学笔记创作活动的第19天。
发展历程
DBMS数据模型
网状模型
多对多的关系,子节点可以有多个父节点。
优势
- 能直接描述现实世界
- 存储效率高
劣势:
- 结构复杂
- 用户不易使用
- 访问程序设计复杂
层次模型
使用树形结构来描述实体及其之间的关系的数据模型。
优势
- 结构简单
- 查询效率高
- 可以提供较好的的完整性支持
劣势
- 无法表示M:N的关系
- 插入、删除限制多
- 遍历子节点必须经过父节点
- 访问程序设计复杂
关系模型
所有的数据都是一张二维表。
优势
实体及实体间的联系都通过二维表结构表示 可以方便的表示M:N的关系 数据访问路径对用户透明
劣势
关系查询效率不够高 关系必须规范化
SQL语言
将Codd关系数据库的12条准则的数学定义以简单的关键字语法表现出来,里程碑式的提出了SQL(Structured Query Language)语言。
优点
- 语法风格接近自然语言
- 高度非过程化
- 面向集合的操作方式
- 语言简洁,易学易用
历史概览
关键技术
一条SQL的一生
终端 -> Router -> RDBMS -> Parser(语法解析器) -> Optimizer(优化器) -> Executor -> Data File
- AST: 语法树
- Plan: 也是一种树状结构
SQL引擎
解析器(Parser)
一般分为词法分析(Lexical analysis)、语法分析(Syntax analysis)、语义分析(Semantic analyzer)等步骤。
词法分析
- 关键字:UPDATE/SET/WHERE
- 表列名: account_table/balance/name
- 常量: '小目标' / '抖音'
- 运算符: '=' / '-'
- 结束符: ';'
语法分析(生成一个语法树):
struct UpdateStmt {
Table: account_table;
Fields: balance;
Values: balance - '小目标';
Where: name = '抖音';
OrderBy: null;
Limit: null;
}
语义分析(合法性分析)
- 检查account_table是否存在
- 检查balance/name是否存在
- 场景常量的数据类型是否合法
优化器(Optimizer)
基于规则的优化(RBO Rule Base Optimizer)
- 条件化简
- 表连接优化
- 总是小表先进行连接
- Scan优化
- 唯一索引
- 普通索引
- 全表扫描
基于代价的优化(CBO Cost Base Optimizer)
- 代价是诸如 时间、I/O、CPU、NET、MEM等
- 但基于用户体验比较重要的是时间
执行器(Executor)
火山模型
每个Operator(算子)调用Next操作,访问下层Operator,获得下层Operator返回的一行数据,经过计算之后,将这行数据返回给上层。
- 优点
- 每个算子独立抽象实现,相互之间没有耦合,逻辑结构简单
- 缺点
- 每计算一条函数有多次函数调用开销,导致CPU效率不高
优化方式:向量化
每个Operator每次操作计算的不再是一行数据,而是一批数据(Batch N行数据),计算完成后向上层算子返回一个Batch。
- 优点
- 函数调用次数降低为1/N
- CPU cache命中率更高
- 可以利用CPU提供的SIMD(Single Instruction Multi Data)机制
优化方式:编译执行
将所有的操作封装到一个函数里,函数调用的代价也能大幅降低。
实现方法是使用LLVM动态编译执行技术。
存储引擎
InnoDB(MySQL中)
In-Memory(内存部分)
做内存缓冲。
- Buffer Pool(缓冲池)
- Change Buffer
- Adaptive Hash Index
- Log Buffer(I/O会写到这上面然后再写到磁盘上)
On-Disk(磁盘部分)
- System Tablespace(ibdata1)
- 存储元信息:表名、列名、用户权限等
- General Tablespaces(xxx.ibd)
- 存储普通字段
- Undo Tablespaces(xxx.ibu)
- Temporary Tablespaces(xxx.ibt)
- Redo Log(ib_logfileN)
Buffer pool
- 每个页面(Page)是16K大小,是对数据管理的最小单元
- 每个chunk是128M向系统申请内存
通过HashMap快速找到block。
- LRU算法
- 把最近最常使用的数据保留下来,淘汰不常使用的数据,达到释放内存的目的
Page
- delete_mask: 标识此条数据是否被删除
- next_round: 下一条数据的位置
- record_type: 标识当前记录的类型
B+ tree
B+树作为索引,是二分查找树(B树)的扩展。
- 在一个页面内:
- 页目录中使用二分法快速定位到对应的槽,然后再遍历该槽对应分组中的记录即可快速找到指定的记录
- 从根到叶:
- 中间节点存储
事务引擎
Atomicity 与 Undo Log
如何将数据库回退到修改之前的状态?
通过Undo Log实现。
- Undo Log是逻辑日志,记录的是数据的增量变化,利用Undo Log可以进行事务回滚,从而保证事务的原子性。
- 同时也实现了多版本并发控制(MVCC),解决读写冲突和一致性读的问题
Isolation 与 锁
- 同时读的时候加Share Lock
- 同时写的时候加Exclusive Lock
- 同时读写的时候MVCC
Isolation 与 MVCC
MVCC的意义
- 读写互不阻塞
- 降低死锁概率
- 实现一致性读
老版本的数据记录在Undo里(自下而上版本越高),新数据记录在数据页里,通过roll pointer来逐渐形成链表。
Undo Log在MVCC的作用
- 每个事务有一个单增的事务ID
- 数据页的行记录包含了DB_ROW_ID,DB_TRX_ID,DB_ROLL_PTR
- DB_ROLL_PTR将数据行的所有快照记录都通过链表的结构串联了起来
Durability 与 Redo Log
如何保证事务结束后,对数据的修改永久的保存?
- 事务提交前页面写盘
- 问题:随机I/O、写放大
- WAL(Write-ahead-logging)
- redo log是物理日志,记录的是页面的变化,它的作用是保证事务持久化,如果数据写入磁盘前发生故障,重启Mysql后会根据redo log重做
以上内容若有不正之处,恳请您不吝指正!