这是我参与「第三届青训营 -后端场」笔记创作活动的第5篇笔记
基本概念
事务
是一组SQL语句组成的程序执行单元,它需要满足ACID特性
DBMS数据模型
网状模型:每个节点之间是多对多的关系。
层次模型:一对多关系。
关系模型:所有的数据都是一张二维表,无论实体还是关系。
SQL语言
1974年提出
结构化查询语言
关键技术
一条SQL的生命周期
Parser语法解析器解析SQL,生成一个语法树AST,给到优化器生成Plan,告诉Executor执行器到底怎么执行,从文件中读写数据、写入日志。
Parser, Optimizer, Executor 都是SQL引擎
DataFile, Log File 都是事务引擎
SQL引擎
Parser
一般分为词法分析、语法分析、语义分析等步骤
Optimizer
为什么需要优化器?
在可能的路径中,选择一个最优的路径。
一般先做一些条件化简,然后用基于代价的优化器。
基于规则的优化器(RBO Rule Base Optimizer)
- 条件化简
- 表连接优化
-
- 总是小表先进行连接
- Scan 优化
-
- 唯一索引
- 普通索引
- 全表搜索
基于代价的优化器(CBO Cost Base Optimizer)
一个查询有多种执行方案,CBO会选择其中代价最低的方案去真正的执行。
什么是代价?时间(最主要)、IO、CPU、NET、MEM
不仅要考虑单点时间,往往要考虑分布式时延最低。
Executor
火山模型
查询树投影、过滤、请求,函数栈一层一层往下调用,数据是一层一层网上返回。
每个Operator调用Next操作,访问下层Opertor,获得下层Operator返回的一行数据。经过计算,返回结果给上一层。
优点:每个「算子」独立抽象实现,互相之间没有耦合,逻辑结构简单
缺点:每计算一条数据有多次函数调用开销,导致CPU效率不高。
向量化
和火山模型类似,但是每次返回的不再是一行数据,而是一批数据,计算完成后向上层算子返回一个Batch。
优点:函数调用次数降低为1/N,CPU cache命中率更高,可以利用CPU提供的SIMD(signle instruction multi data)机制
编译执行
用户SQL千变万化,不可能说穷举用户的所有SQL,给每一个SQL都预先写好一个函数。
将所有的操作封装到一个函数里,函数调用的代价大大降低。
存储引擎
InnoDB
分为两部分:内存中的结构和磁盘中的结构。
内存中结构主要是Buffer Pool,每次写入之前是先放到Buffer Pool中再进行写入操作的。
磁盘结构中东西很多,首先是System Tablespace 系统表空间,数据库使用要存储很多元数据,create table 叫什么,有什么索引,都是元信息。
Gerneral Tablespaces 存表数据的地方。
Buffer Pool
每个页面16k大小的内存空间
LRU机制:磁盘数据比内存远大,当访问数据时,内存数据肯定不足,需要从磁盘获取数据,放不下的话,就要淘汰的机制。把最常使用的数据留下来,很少使用的数据淘汰。管理内存空间。
Page
大小16K,在磁盘存储是按Page存储的
右下查找数据:二分查找+ 小的遍历
B+ tree 数据结构查找索引
页面中:
页目录中使用二分法快速定位到对应的槽,然后再遍历该槽对应分组中的记录即可快速找到指定的记录。
从根到叶的遍历:
中间节点存储。中间加点中是一个双向链表,如果是范围查询,可以很方便地进行遍历操作,获取范围数据。
\
事务引擎
Atomicity 与 Undo Log
如何将数据库回退到修改之间的状态?
Undo Log 实现
Undo Log 是「逻辑日志」,记录的是数据的增量变化。利用Undo Log可以进行事务回滚,从而保证事务的「原子性」。同时也实现了「多版本并发控制」(MVCC),解决读写冲突和一致性读的问题。
实现类似于执行sql的反义词。
Isolation 与 锁
如果两个操作同时进行,发生冲突怎么办?
读读:Share Lock 加共享锁
写写:Exclusive Lock 排他锁
读写:Share Lock Exclusive Lock
Isolation 与 MVCC
数据的多版本
通过数据的roll ptr属性形成一个链表,便于查询上一个版本,使得读写互不阻塞。
MVCC的意义:
- 读写互不阻塞
- 降低死锁概率
- 实现一致性读
Durability 与 Redo Log
怎么保证事务结束后,对数据的修改永久的保存?
方案一:事务提交前Page写入disk
问题:
随机IO:随机写数据需要随机访问磁盘,代价很大;
写放大:数据可能只有10几个字节,但要写这个页面的数据(16KB)
方案二:WAL (Write-ahead logging)
redo log 是物理日志,记录的是页面的变化,作用是保证事务持久化。提交事务前一定要把日志写了。如果故障,重启后根据redo log重做。
企业实践
红包雨:
难点:流量大、流量突增、稳定性
大流量 - sharding
问题背景:
- 单节点写容易成为瓶颈
- 单机数据容量上限
解决方案:
- 业务数据进行水平拆分
- 代理层进行分片路由
实施效果:
- 数据库写入性能线性扩展
- 数据库容量线性扩展
流量突增 - 扩容
问题背景:
- 活动流量上涨
- 集群性能不满足要求
解决方案:
- 扩容DB物理节点数量
- 利用影子表进行压测
流量突增 - 代理连接池
问题背景:
- 突增流量导致大量建联
- 大量建联导致负载变大,延时上升,可能导致的DB连接被打死
解决方案:
- 业务侧预热连接池
- 代理侧预热连接池
- 代理侧支持连接队列
实施效果:
- 避免DB被突增流量打死
- 避免代理和DB被大量建联打死
稳定性 & 可靠性
3AZ高可用
3A:在3个城市建立3个不同的机房,不同机房之间同过日志同步。
proxy:读写分离,分库分表,限流,流量调度
监控报警:实时监控集群运行状态
HA管理
问题背景:
- db所在机器宕机
- db节点异常宕机
解决方案:
- ha服务监管、切换宕机节点
- 代理支持配置热加载
- 代理自动屏蔽宕机读节点
把机器下线去运维了,代理其他服务器。
\