经典案例
RDBMS 事务(Transaction) :是由一组 SQL 语句组成的一个程序执行单元(Unit),它需要满足 ACID 特性:
- Atomicity 原子性:事务是一个不可再分割的工作单元,事务中的操作要么都发生,要么都不发生。
- Consistency 一致性 :数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性。
- Isolation 隔离性 :多个事务并发访问时,事务之间是隔离的,一个事务不应该影响其它事务运行效果。
- Durablility 持久性:在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。
“红包雨” case 解读:
- 资方扣了一个亿后,个人账户还没加上->A
- 资方只有0.5亿,但是扣减1个亿成功了->C
- 资方1扣了1亿,资方2扣了1亿,但是账户只增加了1亿->I
- 都没写到磁盘上,宕机,结果都没变->D
- 10亿人抢红包,每秒处理一个请求,得31年->高并发
- 10亿人抢红包,服务器宕机->高可靠、高可用
发展历史
前 DBMS 时代:手工管理->文件系统
DBMS 数据模型:网状、层次、关系
SQL(Structured Query Language)结构化查询语言 ( www.runoob.com/sql/sql-tut… )
历史回顾\
关键技术
SQL 引擎
SQL 引擎-解析器 Parser: 词法分析、语法分析、语义分析\
SQL 引擎-优化器 Optimizer: 基于规则的优化 RBO or 基于代价的优化 CBO
- 基于规则的优化 Rule Based Optimizer(RBO)
- 条件化简 eg. a = 5 and b > a => a = 5 and b > 5;
- 表连接优化->优先小表连接
- Scan 优化
- 唯一索引
- 普通索引
- 全表扫描 数据库索引: 是数据库管理系统中辅助数据结构,以协助快速查询、更新数据库表中数据。目前数据库中最常用的索引是通过 B +树实现的。
- 基于代价的优化 Cost Based Optimizer (CBO)
- 时间 or 资源 IO + CPU + Net + MEM
- eg. innodb(MySQL 的数据库引擎之一)全表扫描的意思就是把聚簇索引中的记录都依次和给定的搜索条件做一下比较, 把符合搜索条件的记录加入到结果集,所以需要将聚簇索引对应的页面加载到内存中,然后再检测记录是否符合搜索条件。那么对于二级索引与回表方式查询,这种查询方式的成本依赖于两个方面的数据:范围区间数量与需要回表数据量。
SQL 引擎-执行器 Executor
-
火山模型
- 每个 Operator 调用 Next 操作,访问下层 Operator,获得下层 Operator 返回的一行数据,经过计算之后,将这行数据返回给上层。
- 优点:每个算子独立抽象实现,相互之间没有耦合,逻辑结构简单
- 缺点:每计算一条数据有多次函数调用开销,导致CPU效率不高
-
向量化
- 每个 Operator 每次操作计算的不再是一行数据,而是一批数据 (Batch N 行数据),计算完成后向上层算子返回一个 Batch。
- 优点:函数调用次数降低为1/N;CPU cache 命中率更高;可以利用 CPU 提供的 SIMD (Single Instruction Multi Data)机制。
- ps:向量化执行更适合于大批量数据处理,对于很多单行数据处理并没有优势。而且往往搭配列式存储使用。
-
编译执行
- 将所有的操作封装到一个函数里面函数调用的代价也能大幅度降低。
- 代绍生或之后教据库运行仍然是一个 for 循环,只过这循环内部的代码从简单的虚函数调用 plan. next () 属开成了一系列具的运算逻辑,这样数据就不在各个 operator 之间进行传递 ,而且有些数据以直放在寄存器中,进一步提升系统性能。整个操作有点像 inline 函数,把所有的操作 inline 到一个函数中去。
- 用户 SQL 千变万化怎么办? 难道要穷举用户的所有 SQL,给每一个 SQL 都预先写好一个执行函数吗? -> LLVM 动态编译执行技术
- 根据执行器产生的计划,动态的生成执行代码
- 将所有的操作封装到一个函数里面函数调用的代价也能大幅度降低。
存储引擎
存储引擎-InnoDB
- In-Memory:
- Buffer Pool
- Change Buffer
- Adaptive Hash Index
- Log Buffer
- On-Disk:
- System Tablespace(ibdata1)
- General Tablespaces(xxx.ibd)
- Undo Tablespaces(xxx.ibu)
- Temporary Tablespaces(xxx.ibt)
- Redo Log (ib_ logfileN)
存储引擎-Buffer Pool
- MySQL 中每个 chunk 的大小一般为128M,每个 block 对应一个 page,一个 chunk 下面有8192个 block。这样可以避免内存碎片化。
- 分成多个 instance,可以有效避免并发冲突。
- Page id % instance num 得到它属于哪个 instance。
存储引擎-Page
存储引擎-B+树
事务引擎
原子性 与 Undo Log
隔离性 与 Lock
隔离性 与 MVCC
持久性 与 Redo Log
企业实践
大流量 - Sharding
- 问题背景
- 单节点写容易成为瓶颈
- 单机数据容量上限
- 解决方案
- 业务数据进行水平拆分
- 代理层进行分片路由
- 实施效果
- 数据库写入性能线性扩展
- 数据库容量线性扩展
ps:当数据库中的数居量越来越大时,不论是读还是写,压力都会变得越来越大。虽然上面的方案可以扩展读节点,但是对于写流量增加,以及数据量的增加却没有办法。
流量突增 - 扩容 与 代理连接池
-
问题背景 活动流量上涨 集群性能不满足要求
-
解决方案
- 扩容 DB 物理节点数量
- 利用影子表进行压测
-
实施效果
- 数据库集群提供更高的吞吐
- 保证集群可以承担预期流量、
-
问题背景
- 突增流量导致大量建联
- 大量建联导致负载变大,延时上升
-
解决方案
- 业务侧预热连接池
- 代理侧预热连接池
- 代理侧支持连接队列
-
实施效果
- 避免 DB 被突增流量打死
- 避免代理和 DB 被大量建联打死
稳定性&可靠性 - 3AZ 部署 与 HA 管理
binlog
数据恢复
主从复制
审计
- 问题背景
- db 所在机器异常宕机
- db 节点异常宕机
- 解决方案
- ha 服务监管、切换宕机节点代理支持配置热加载
- 代理自动屏蔽宕机读节点
- 实施效果
- 读节点岩机秒级恢复写节点宕机 30s 内恢复服务