这是我参与「第五届青训营 」伴学笔记创作活动的第 11 天
上节回顾
存储系统
- 块存储、文件存储、对象存储、key-value存储 数据库系统
- 关系型数据库、非关系型数据库 分布式架构
- 数据分布策略、数据复制协议、分布式事务算法
01.经典案例
1.1 从一场红包雨说起
每一年的春节,抖音上都会下一场温暖人心的红包雨~
实现过程
1.2 RDBMS 事务 ACID
- 事务(Transaction):是由一组SQL语句组成的一个程序执行单元(Unit),它需要满足ACID特性。
ACID:
- 原子性(
Atomicity):事务是一个不可再分割的工作单元,事务中的操作要么都发生,要么都不发生。 - 一致性(
Consistency):数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性。 - 隔离性(
Isolation):多个事务并发访问时,事务之间是隔离的,一个事务不应该影响其它事务运行效果。 - 持久性(
Durability):在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。
1.3 红包雨 与 ACID
1.3 红包雨 与 ACID
1.3 红包雨 与 ACID
1.3 红包雨 与 ACID
1.4 红包雨 与 高并发
1.5 红包雨 与 高可靠
02.发展历史
2.1 前DBMS时代 - 人工管理
2.2 前DBMS时代 - 文件系统
2.3 DBMS时代
1960s,传统的文件系统已经不能满足人们的需要,数据库管理系统(DBMS)应运而生。
DBMS:按照某种数据模型来组织、存储和管理数据的仓库。
所以通常按照数据模型的特点将传统数据库系统分成网状数据库、层次数据库和关系数据库三类。
2.3.1 DBMS数据模型 - 网状模型
网状数据库所基于的网状数据模型建立的数据之间的联系,能反映现实世界中信息的关联,是许多空间对象的自然表达形式。
1964年,世界上第一个数据库系统——集成数据存储(Integrated Data Storage,IDS)诞生于通用电气公司。IDS是世界上第一个网状数据库,奠定了数据库发展的基础,在当时得到了广泛的应用。在1970s网状数据库系统十分流行,在数据库系统产品中占据主导地位。
2.3.2 DBMS数据模型 - 层次模型
1968年,世界上第一个层次数据库——信息管理系统(Information Management System,IMS)诞生于IBM公司,这也是世界上第一个大型商用的数据库系统。层次数据模型,即使用树形结构来描述实体及其之间关系的数据模型。
2.3.3 DBMS数据模型 - 关系模型
1970年,IBM的研究员E.F.Codd博士发表了一篇名为“A Relational Model of Data for large Shared Data Banks”的论文,提出了关系模型的概念,奠定了关系模型的理论基础。1979年Oracle首次将关系型数据库商业化,后续DB2, SAP Sysbase ASE, and Informix等知名数据库产品也纷纷面世.
2.4 DBMS数据模型
2.5 SQL语言
1974年IBM的Ray Boyce和Don Chamberlin将Codd关系数据库的12条准则的数学定义以简单的关键字语法表现出来,里程碑式地提出了SQL(Structured Query Language语言。
语法风格接近自然语言;
高度非过程化;
面向集合的操作方式;
语言简洁,易学易用。
高度非过程化
非关系数据模型的数据操纵语言是面向过程的语言,用其完成用户请求时,必须指定存取路径。而用SQL进行数据操作,用户只需提出做什么”,而不必指明"怎么做,因此用户无须了解存取路径,存取路径的选择以及SQL语句的操作过程由系统自动完成。这不但大大减轻了用户负担,而且有利于提高数据独立性。面向集合的操作方式
SQL采用集合操作方式,不仅查找结果可以是元组的集合,而且一次插入、删除、更新操作的对象也可以是元组的集合。语言简洁,易学易用SQL功能极强,但由于设计巧妙,语言十分简洁,完成数据定义.数据操纵、数据控制的核心功能只用了9个动词:CREATE、 AITER、DROP、SEECT、INSERT.UPDATE、DELETE、GRANT、 REVOKE。且SQL语言语法简单,接近英语口语,因此容易学习,也容易使用。
2.6 历史回顾
03.关键技术
3.1 一条SQL的一生
3.2 SQL引擎
- 查询解析:SQL语言接近自然语言,入门容易。但是各种关键字、操作符组合起来,可以表达丰富的语意。因此想要处理SQL命令,首先将文本解析成结构化荔据,也就是抽象语法树(AST) 。
- 查询优化: SQL是一门表意的语言,只是说『要做什么』,而不说【怎么做』。所以需要一些复杂的逻导选择『如何拿数据」,也就是选择一个好的查询计划,优化翠的作用根据AST优化产生最优执行计划(Plan Tree) 。
- 查询执行:根据查询计划,完成数据读取、处理、写入等操作。
- 事务引擎:处理事务—致性、并发、读写隔离等
- 存储引擎:内存中的数据缓存区、数据文件、日志文件
3.2.1 SQL引擎 - Parser
所有的代码在执行之前,都存在一个解析编译的过程,差异点无非在于是静态解析编译还是动态的。SQL语言也类似,在SQL查询执行前的第一步就是查询解析。
- 词法分析:将一条SQL语句对应的字符串分割为一个个token,这些token可以简单分类。
- 语法分析:把词法分析的结果转为语法树。根据tocken序列匹配不同的语法规则,比如这里匹配的是update语法规则,类似的还有insert.delete、select、create、drop等等语法规则。根据语法规则匹配SQL语句中的关键字,最终输出一个结构化的数据结构。
- 语义分析:对语法树中的信息进行合法性校验。
3.2.2 SQL引擎 - Optimizer
比如红绿灯最少,这就是—个规则。 再举个例子,大家知道中关村软件园附近有一条路叫后厂村路,非常的堵。网上有一个广为流传的段子:问:制约中国互联网未来10年发展最大的瓶颈是什么?答:后厂村路。那么这里可以加——个规则:不能走后厂村路。
到达一个目的地,有不同的路线,选择不同的路线有不同的代价。这里的代价可能是时间,也可能是路程。比如我们赶时间的时候,就会选择时间最短的。如果时间没那么赶,那么我们可能选择路程最短的。因为这样省油啊,毕竟现在油价这么高。
对于数据库也是这样,—个查询有不同的执行方案。
那对于数据库而言,什么是一条SQL执行的代价呢? 其实,对于用户只能感知到查询时间这个代价,底层用了多少资源他是不在乎的。但是在并发的情况下,就得考虑资源消耗了,这个用户的查询占用的资源多了,其他用户的资源就少了。所以资源也是必须考虑的─点。
对于innoDB存储引擎来说,全表扫描的意思就是把聚簇索引中的记录都依次和给定的搜索条件做一下比较,把符合搜索条件的记录加入到结果集,所以需要将聚簇索引对应的页面加载到内存中,然后再检测记录是否符合搜索条件。
对于使用二级索引+回表方式的查询,设计MySQL的大叔计算这种查询的成本依赖两个方面的数据:范围区间数量,需要回表数据量
3.2.3 SQL引擎 - Executor
Plan Tree为基础调用关系是由根到叶数据流是从叶到根
3.2.4 SQL引擎 - Executor
向量化执行更适合于大批量数据处理,对于很多单行数据处理并没有优势。而且往往搭配列式存储使用。
3.2.5 SQL引擎 - Executor
代码生成之后数据库运行时仍然是一个for循环,只不过这个循环内部的代码从简单的一个盘函数调用iplan.nextl)展开成了一系列具体的运算逻辑,这样数据就不用再各个operator之间进行传递,而且有些数据还可以直接被存放在寄存器中,进一步提升系统性能。整个操作有点像inline函数,把所有的操作nline到一个函数中去。LLVM动态编译执行技术,根据优化器产生的计划,动态的生成执行代码。
3.3 存储引擎
3.3.1 存储引擎 - InnoDB
3.3.2 存储引擎 - Buffer Pool
MySQL中每个chunk的大小一般为128M,每个block对应一个page,一个chunk下面有8192个block。这样可以避免内存碎片化分成多个instance,可以有效避免并发冲突。
Page id % instance num得到它属于哪个instance
3.3.3 存储引擎 - Buffer Pool
当buffer pool里的页面都被使用之后,再需要换存其他页面怎么办?淘汰已有的页面
基于什么规则淘汰:淘汰那个最近一段时间最少被访问过的缓存页了,这种思想就是典型的LRU算法了。
普通的LRU算法存在缺陷,考虑我们需要扫描100GB的表,而我们的buffer poo只有1GB,这样就会因为全表扫描的数据量大,需要淘汰的缓存页多,导致在淘汰的过程中,极有可能将需要频繁使用到的缓存页给淘汰了,而放进来的新数据却是使用频率很低的数据。
MySQL确实没有直接使用LRU算法,而是在LRU算法上进行了优化。 MySQL的优化思路就是:对数据进行冷热分离,将LRU链表分成两部分,一部分用来存放冷数据,也就是刚从磁盘读进来的数据,另一部分用来存放热点数据,也就是经常被访问到数据。
当从磁盘读取数据页后,会先将数据页存放到LRU链表冷数据区的头部,如果这些缓存页在1秒之后被访问,那么就将缓存页移动到热数据区的头部;如果是1秒之内被访问,则不会移动,缓存页仍然处于冷数据区中。
淘汰时,首先淘汰冷数据区。
3.3.4 存储引擎 - Page
User Records在页面上实际是无序的,通过—个单向链表连接
3.3.5 存储引擎 - B + Tree
3.4 事务引擎
3.4.1 事务引擎 - Atomicity 与 Undo Log
原子性:一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被恢复(Rillack)到事务开始前的状态,就像这个事务从来没有执行过—样。
需要记录数据修改前的状态,一边在事务失败时进行回滚。
undo log是逻辑日志,记录的是数据的增星变化,它的作用是保证事务的原子性和事务并发控制。可以用于事努回滚,以及提供多版本机制(MVCC),解决读写冲突和—致性读的问题。
3.4.2 事务引擎 - Isolation 与 锁
lsolation(隔离性)∶数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致如果多个并发事务访问同—行记录,就需要锁机制来保证了。
读写是否冲突?读写互不阻塞,MVCC机制。
3.4.3 事务引擎 - Isolation 与 MVCC
脏读:事务还没提交之前,它对数据做的修改,不应该被其他人看到。
万一抖音给我的账户转账的事务还没完成,羊老师就查到了账户上有一个亿,后来抖音发现不对,把这个事务回滚掉了。过一会羊老师发现自己账户的一个亿又没了去找银行要个说法,结果被保安赶了出来。
3.4.4 事务引擎 - Durability 与 Redo Log
持久化:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
WAL:修改并不直接写入到数据库文件中,而是写入到另外一个称为WAL的文件中;如果事努失败,WAL中的记录会被忽略,撤销修改;如果事务成功,它将在随后的某个时间被写回到数据库文件中,提交修改。
优点: 只记录增量变化,没有写放大 Append only,没有随机IO
04. 企业实战
4.1 春节红包雨挑战
4.2 大流量 - Sharding
当数据库中的数据量越来越大时,不论是读还是写,压力都会变得越来越大。虽然上面的方案可以扩展读节点,但是对于写流星增加,以及数据量的增加却没有办法
4.3 流量突增-扩容
4.4 流量突增 - 代理连接池
4.5 稳定性 & 可靠性
为什么要高可用:
恶意事故:程序员删库跑路?哪个程序员不想执行—把rm -rf*?偶然事故:如果─个机房断电?断网?
某施工队,施工的时候挖掘机把某游戏公司的光纤挖断了,一下午的时间,保守估计损失一个亿。
4.5.1 稳定性&可靠性一3AZ高可用
BinLog: binlog是mysql用来记录数据库表结构变更以及表数据修改的的二进制日志,它只会记录表的变更操作,但不会记录select和how这种查询操作数据恢复:误删数据之后可以通过mysqlbinlog工具恢复数据
主从复制:主库将binlog传给从库,从库接收到之后读取内容写入从库,实现主库和从库数据一致性审计:可以通过二进制日志中的信息进行审计,判断是否对数据库进行注入攻击