「MySQL - 深入理解RDBMS」 | 青训营笔记

34 阅读22分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 16 天

本堂课重点

RDBMS (关系型数据库)是目前使用最为广泛的数据库之一,同时也是整个信息化时代的基石。本节课程通过生活中常见的场景向大家介绍 RDBMS 的作用、发展历程及其核心技术,最后以字节为例,展示了 RDBMS 的企业级实践。本节课程主要包含以下内容:

  1. 经典案例
  2. 发展历史
  3. 关键技术
  4. 企业实践

1.经典案例

喜闻乐见:从一场抖音红包雨说起 ~

1.1 从一场红包雨说起

每一年的春节,抖音上都会下一场温暖人心的红包雨~

image-20230212185222195.png

image-20230212185331619.png

1.2 RDBMS事务 ACID

事务(Transaction) : 是由一组 SQL 语句组成的一个程序执行单元( Unit ),它需要满足 ACID 特性。

image-20230212185450500.png ACID :

  • 原子性(Atomicity):事务是一个不可再分割的工作单元,事务中的操作要么都发生,要么都不发生。
  • 一致性(Consistency):数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性。
  • 隔离性(Isolation):多个事务并发访问时,事务之间是隔离的,一个事务不应该影响其它事务运行效果。
  • 持久性(Durability):在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。

两条更新操作不再是独立的,而形成了一个整体,这个整体称为一个事务。

1.3 红包雨与 ACID

Case 1 : 抖音的账户上扣了一个亿之后,假设服务器挂了,还没来得及给羊老师账户上加一个亿

image-20230212190058075.png

1.4 红包雨与 ACID

Case 2 : 假设抖音的账户上只有 0.5 个亿,但是扣减 1 个亿的操作成功了。

image-20230212190155550.png Case 3 : 羊老师从抖音抢了一个亿红包,又从头条抢了一个亿,两个转账同时进行,假设他们都以为是从零开始,更新羊老师的账户余额,羊老师最后得到一个亿。

image-20230212190720175.png Case 4 : 抖音的账户上扣了一个亿,然后羊老师账户上加一个小目标,但都还没写到磁盘上。这个时候,如果服务器挂了:

image-20230212190848305.png

1.5 红包雨与高并发

Case 5 : 全国 14 亿人,假设有10亿人同时开抢红包,每秒处理一个请求,那需要31年才能完成。春节完了,抖音可能也被大家嫌弃了..---高并发 Concurrency

image-20230212191541354.png

1.6 红包雨与高可靠

Case 6 : 假设除夕晚上大家正在愉快的从抖音身上"薅羊毛”,这时候服务器挂了,程序员花了一个小时,头发都掉光了,终于修好了。这时候发现李谷一老师《难忘今宵》都唱完了。"抖音宕机”秒上热搜.---高可靠、高可用 High Reliability/Availability

image-20230212191515415.png

2.发展历史

源远流长:RDBMS 从 1970 年第一篇论文发布至今已有 50 余年,衍生出 Oracle,DB2,MySQL,SQL,Sever,Aurora 等一系列知名数据库产品.

2.1 前 DBMS 时代-人工管理

image-20230212191621301.png

2.2 前 DBMS 时代-文件系统

1950 s,现代计算机的雏形基本出现。1956 年 IBM 发布了第一个的磁盘驱动器--Model 305 RAMAC,从此数据存储进入磁盘时代。在这个阶段,数据管理直接通过文件系统来实现。

image-20230212192003422.png

2.3 DBMS 时代

1960 s,传统的文件系统已经不能满足人们的需要,数据库管理系统 (DBMS) 应运而生。

DBMS:按照某种数据模型来组织、存储和管理数据的仓库。

所以通常按照数据模型的特点将传统数据库系统分成网状数据库层次数据库关系数据库三类。

image-20230212192101110.png

传统的文件系统难以应对数据增长的挑战,也无法满足多用户共享数据和快速检索数据的需求。

层次型、网状型和关系型数据库划分的原则是数据之间的联系方式。层次数据库是按记录来存取数据的;网状数据库是采用网状原理和方法来存储数据;关系型数据库是以行和列的形式存储数据。

2.3.1 DBMS 数据模型-网状模型

网状数据库所基于的网状数据模型建立的数据之间的联系,能反映现实世界中信息的关联,是许多空间对象的自然表达形式。

1964 年,世界上第一个数据库系统—-集成数据存储 (Integrated Data Storage,IDS) 诞生于通用电气公司。IDS 是世界上第一个网状数据库,奠定了数据库发展的基础,在当时得到了广泛的应用。在 1970 s 网状数据库系统十分流行,在数据库系统产品中占据主导地位。

image-20230212193009078.png

网状数据模型是以记录类型为结点的网络结构,即一个结点可以有一个或多个下级结点,也可以有一个或多个上级结点,两个结点之间甚全可以有多种联系,例如“教师”与”课程”两个记录类型,可以有”任课"和“辅导两种联系,称之为复合链。

两个记录类型之间的值可以是多对多的联系,例如一门课程被多个学生修读,一个学生选修多门课程。

2.3.2 DBMS 数据模型 - 层次模型

1968 年,世界上第一个层次数据库一信息管理系统 (Information Management System,IMS) 诞生于 IBM 公司,这也是世界上第一个大型商用的数据库系统。层次数据模型,即使用树形结构来描述实体及其之间关系的数据模型。

image-20230212193131441.png

层次数据库就是树结构。每棵树精都有且仅有一个根节点,其余的节点都是非根节点。每个节点表示一个记录类型对应与实体的概念,记录类型的各个字殿对应实体的格个属性。各个记录类型及其字段都必须记录。

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 等知名数据库产品也纷纷面世。

image-20230212193518009.png

使用表格表示实体和实体之间关系的数据模型称之为关系数据模型。

关系数据模型中,无论是是实体、还是实体之间的联系都是被映射成统的关系一张二维表,在关系模型中,操作的对象和结果都是张二维表,它由行和列组成:

关系型数据库可用于表示实体之间的多对多的关系,只是此时要借助第三个关系一表,来实现多对多的关系:

2.4 DBMS 数据模型

网状结构层次结构关系模型
优势1.能直接描述现实世界
2.存取效率较高
1.结构简单
2.查询效率高
3.可以提供较好的完整性支持
1.实体及实体间的的联系都通过二维表结构表示
2.可以方便的表示 M:N 关系
3.数据访问路径对用户透明
劣势1.结构复杂
2.用户不易使用
3.访问程序设计复杂
1.无法表示 M:N 的关系
2.插入、删除限制多
3.遍历子节点必须经过父节点
4.访问程序设计复杂
1.关联查询效率不够高
2.关系必须规范化

1974 年 ACM 牵头组织了一次研所讨会,会上开展了一场分别以 Codd 和 Bachman 为首的支持和反对关系数据库两派之间的辩论。这次菩名的辩论推动了关系数据库的发展,使其最终成为现代数据库产品的主流。

2.5 SQL语言

1974 年 IBM 的 Ray Boyce 和 Don Chamberlin 将 Codd 关系数据库的 12 条准则的数学定义以简单的关键字语法表现出来,里程碑式地提出了 SQL (Structured Query Language) 语言。

  • 语法风格接近自然语言;
  • 高度非过程化;
  • 面向集合的操作方式;
  • 语言简洁,易学易用。

image-20230212194201020.png

高度非过程化:

非关系数据摸型的数据操纵语言是面向过程的语言,用其完成用户请求时,必须指定存取路径。而用 SQL 进行数据操作,用户只需提出”做什么”,而不必指明”怎么做”,因此用户无须了解存取路径,存取路径的选择以及 SQL 语句的操作过程由系统自动完成。这不但大大减轻了用户负担,而且有利于提高数据独立性。

面向集合的操作方式:

SQL 采用集合操作方式,不仅查找结果可以是元组的集合,而且一次插入、删除、更新操作的对象也可以是元组的集合。

语言简洁,易学易用: SQL 功能极强,但由于设计巧妙,语言十分简洁,完成数据定义、数据操纵、数据控制的核心功能只用了 9 个动词:CREATE、ALTER、DROP、SELECT、INSERT、UPDATE、DELETE、GRANT、REVOKE。且 SQL 语言语法简单,接近英语口语,因此容易学习,也容易使用。

2.6 历史回顾

image-20230212194657302.png

3.关键技术

万变归宗:无论 RDBMS 如何演变,其核心都是 SQL 引擎、存储引擎、事务引擎。

3.1 一条 SQL 的一生

image-20230212194802343.png

SQL 引擎----

查询解析:SQL 语言接近自然语言,入门容易。但是各种关键字、操作符组合起来,可以表达丰富的语意。因此想要处理 SQL 命令,首先将文本解析成结构化数据,也就是抽象语法树(AST)

查询优化:SQL 是一门表意的语言,只是说『要做什么』,而不说『怎么做』。所以需要一些复杂的逻辑选择『如何拿数据』,也就是选择一个好的查询计划。优化器的作用根据 AST 优化产生最优执行计划 (Plan Tree)

查询执行:根据查询计划,完成数据读取、处理、写入等操作。

事务引擎:处理事务一致性、并发、读写隔离等

存储引擎:内存中的数据缓存区、数据文件、日志文件

3.2 SQL 引擎

SQL引擎包括了:

  • Paser:经过词法分析、语法分析生成语法树,然后对语法树进行合法性校验。
  • Optimizer:根据Parser产生的语法树,根据规则或者代价产生执行计划树。
  • Executor:根据计划树进行执行,常见的执行方式是火山模型。

3.2.1 SQL 引擎-Parser

解析器 (Parser) 一般分为问法分析 (Lexical analysis)、语法分析 (Syntax analysis)、语义分析 (Semantic analyzer) 等步骤。

image-20230212195123948.png

所有的代码在执行之前,都存在一个解析编译的过程,差异点无非在于是静态解析编译还是动态的。

SQL 语言也类似,在 SQL 查询执行前的第一步就是查询解析。

词法分析:将一条 SQL 语句对应的字符串分割为一个个 token ,这些 token 可以简单分类。

语法分析:把词法分析的结果转为语法树。根据 token 序列匹配不同的语法规则,比如这里匹配的是 update 语法规则,类似的还有 insert、delete、select、create、drop 等等语法规则。根据语法规则匹配 SQL 语句中的关键字,最终输出一个结构化的数据结构。

语义分析:对语法树中的信息进行合法性校验。

3.2.2 SQL 引擎-Optimizer

为什么需要一个优化器(Optimizer)?

image-20230212195848136.png 基于规则的优化(RBO Rule Base Optimizer)

image-20230212200158012.png

比如红绿灯最少,这就是一个规则。

再举个例子,大家知道中关村软件园附近有一条路叫后厂村路,非常的堵。网上有一个广为流传的段子:问:制约中国互联网未来 10 年发展最大的瓶颈是什么?答:后厂村路。那么这里可以加一个规则:不能走后厂村路。

基于代价的优化(CBO Cost Base Optimizer)

一个查询有多种执行方案,CBO 会选择其中代价最低的方案去真正的执行。

什么是代价?

image-20230212200509717.png

到达一个目的地,有不同的路线,选择不同的路线有不同的代价。这里的代价可能是时间,也可能是路程。比如我们赶时间的时候,就会选择时间最短的。如果时间没那么赶,那么我们可能选择路程最短的。因为这样省油啊,毕竟现在油价这么高。

对于数据库也是这样,一个查询有不同的执行方案。

那对于数据库而言,什么是一条 SQL 执行的代价呢?

其实,对于用户只能感知到查询时间这个代价,底层用了多少资源他是不在乎的。但是在并发的情况下,就得考虑资源消耗了,这个用户的查询占用的资源多了,其他用户的资源就少了。所以资源也是必须考虑的一点。

对于 InnoDB 存储引擎来说,全表扫描的意思就是把聚簇索引中的记录都依次和给定的搜索条件做一下比较,把符合搜索条件的记录加入到结果集,所以需要将聚簇索引对应的页面加载到内存中,然后再检测记录是否符合搜索条件。

3.2.3 SQL引擎-Executor

火山模型:

每个 Operator 调用 Next 操作,访问下层 Operator,获得下层 Operator 返回的一行数据,经过计算之后,将这行数据返回给上层。

优点:

  • 每个算子独立抽象实现,相互之间没有耦合,逻辑结构简单

缺点:

  • 每计算一条数据有多次函数调用开销,导致 CPU 效率不高。

image-20230212200725870.png

Plan Tree 为基础,调用关系是由根到叶,数据流是从叶到根

向量化:

每个 Operator 每次操作计算的不再是一行数据,而是一批数据(Batch N 行数据),计算完成后向上层算子返回一个 Batch。

优点:

  • 函数调用次数降低为 1/N:
  • CPU cache 命中率更高:
  • 可以利用 CPU 提供的 SIMD (Single Instruction Multi Data 机制。

image-20230212200906616.png

向量化执行更适合于大批量数据处理,对于很多单行数据处理并没有优势。而目往往搭配列式存储使用。

编译执行:

将所有的操作封装到一个函数里面,函数调用的代价也能大幅度降低。

用户 SQL 千变万化怎么办?难道要穷举用户的所有 SQL 儿,给每一个 SQL 都预先写好一个执行函数吗?

image-20230212201028423.png

image-20230212201040779.png

代码生成之后数据库运行时仍然是个 for 循环,只不过这个循环内部的代码从简单的一个虚函数调用 plan.next() 展开成了一系列具体的运算逻辑,这样数据就不用再各个 operator 之间进行传递,而且有些数据还可以直接被存放在寄存器中,进步提升系统性能。整个操作有点像 inline 函数,把所有的操作 inline 到一个函数中去。

LLVM 动态编译执行技术,根据优化器产生的计划,动态的生成执行代码。

3.3 存储引擎

存储引擎负责了数据的底层存储、管理和访问工作。各大 RDBMS 存储引擎的设计都有不少的差异,这里选择 MySQL 的 InnoDB 存储引擎来向大家做一个介绍:

  • Buffer Pool:存储引擎位于内存中的重要结构,用于缓存数据,减少磁盘IO的开销。
  • Page:数据存储的最基本单位,一般为 16 KB。
  • B+u Tree:InnoDB中最常用的索引结构。

3.3.1 存储引擎-InnoDB

image-20230212201225179.png

3.3.2 存储引擎-Buffer Pool

image-20230212201329394.png

MySQL 中每个 chunk 的大小一般为 128 M ,每个 block 对应一个 page ,一个 chunk 下面有 8192 个 block 。这样可以避免内存碎片化。

分成多个 instance,可以有效避免并发冲突。 Page id % instance num 得到它属于哪个 instance

image-20230212201903480.png

当 buffer pool 里的页面都被使用之后,再需要换存其他页面怎么办?淘汰已有的页面。

基于什么规则淘汰:淘汰那个最近段时间最少被访问过的缓存页了,这种思想就是典型的 LRU 算法了。

普通的 LRU 算法存在缺陷,考虑我们需要扫描 100 GB 的表,而我们的 buffer pool 只有 1 GB,这样就会因为全表扫描的数据量大,需要淘汰的 缓存页多,导致在淘汰的过程中,极有可能将需要频繁使用到的缓存页给淘汰了,而放进来的新数据却是使用频率很低的数据。

MySQL 确实没有直接使用 LRU 算法,而是在 LRU 算法上进行了优化。

MySQL 的优化思路就是:对数据进行冷热分离,将 LRU 链表分成两部分,一部分用来存放冷数据,也就是刚从磁盘读进来的数据,另一部分用来存放热点数据,也就是经常被访问到数据。

3.3.3 存储引擎-Page

image-20230212201932521.png

User Records 在页面上实际是无序的,通过一个单向链表连接

3.3.4 存储引擎一B+ Tree

页面内:

  • 页目录中使用二分法快速定位到对应的槽,然后再遍历该槽对应分组中的记录即可快速找到指定的记录。

从根到叶:

  • 中间节点存储

点查: Select from table wehre id 2000;

范围查询: Select from table wehre id 2000;

image-20230212202022121.png

3.4 事务引擎

事务引擎实现了数据库的 ACID 能力,这里还是以 MySQL 的 InnoDB 为例来介绍数据库内部是通过哪些技术来实现 ACID:

  • Atomicity:InnoDB 中通过 undo 日志实现了数据库的原子性,通过 Undo Log,数据库可以回滚到事务开始的状态;
  • Isolation:通过Undo Log 实现 MVCC(多版本并发控制),降低读写冲突。
  • Durability:通过Redo Log(一种 WAL 实现方式)来保证事务在提交后一定能持久化到磁盘中。
  • Consistency:一致性本质上是一种业务层的限制。

前情提要:如何帮助羊老师从抖音薅一个亿的羊毛?

image-20230212202223788.png

3.4.1 事务引擎-Atomicity 与 Undo Log

image-20230212202344310.png 如何将数据库回退到修改之前的状态? Undo Log

Undo Log : 是逻辑日志,记录的是数据的增量变化。利用 Undo Log 可以进行事务回滚,从而保证事务的原子性。同时也实现了多版本并发控制 (MVCC) 解决读写冲突和一致性读的问题。

image-20230212202547982.png

原子性:一个事务 (transaction) 中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被恢复(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。

需要记录数据修改前的状态,一边在事务失败时进行回滚。

Undo log:是逻辑日志,记录的是数据的增量变化,它的作用是保证事务的原子性和事务并发控制。可以用于事务回滚,以及提供多版本机制 (MVCC) ,解决读写冲实和一致性读的问题。

3.4.2 事务引擎-Isolation 与锁

前情提要:羊老师从抖音抢了一个亿红包,又从头条抢了一个亿。抖音和头条都要往羊老师的账户转一个亿,如果两个操作同时进行,发生冲突怎么办?

image-20230212202709967.png

Isolation (隔离性):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。 如果多个并发事务方问同一行记录,就需要锁机制来保证了。

读写是否冲突?读写互不阻塞,MVCC 机制。

3.4.3 事务引擎-Isolation 与 MVCC

MVCC 的意义:

  • 读写互不阻塞;
  • 降低死锁概率;
  • 实现一致性读。

Undo Log 在 MVCC 的作用:

  • 每个事务有一个单增的事务 ID:
  • 数据页的行记录中包含了 DB ROW ID , DB TRX ID , DB ROLL PTR;
  • DB ROLL PTR : 将数据行的所有快照记录都通过链表的结构串联了起来。

image-20230212203115884.png

脏读:事务还没提交之前,它对数据做的修改,不应该被其他人看到。

万一抖音给我的账户转账的事务还没完成,羊老师就查到了账户上有一个亿,后来抖音发现不对,把这个事务回滚掉了。过一会羊老师发现自己账户的一个亿又没了,去找银行要个说法,结果被保安赶了出来。

3.4.4 事务引擎-Durability 与 Redo Log

image-20230212203418473.png

持久化:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

WAL :修改并不直接写入到数据库文件中,而是写入到另外一个称为 WAL 的文件中;如果事务失败,WAL 中的记录会被忽略,散销修改;如果事务成功,它将在随后的某个时间被写回到数据库文件中,提交修改。

优点:

  • 只记录增量变化,没有写放大
  • Append only,没有随机 IO

4.企业实践

繁花以锦:RDBMS 广泛的应用于互联网、金融、电信、电力等领域,成为了各类企业级应用的数据基石。

4.1 春节红包雨挑战

image-20230212203516647.png

4.2 大流量-Sharding

问题背景解决方案实施效果
1.单节点写容易成为瓶颈
2.单机数据容量上限
1.业务数据进行水平拆分
2.代理层进行分片路由
1.数据库写入性能线性扩展
2.数据库容量线性扩展

image-20230212203919689.png

当数据库中的数据量越来越大时,不论是还是写,压力都会变得越来越大。虽然上面的方案可以扩展读节点,但是对于写流量增勖加,以及数据量的增加却没有办法。

4.3 流量突增-扩容

问题背景解决方案实施效果
1.活动流量上涨
2.集群性能不满足要求
1.扩容 DB 物理节点数量
2.利用影子表进行压测
1.数据库集群提供更高的吞吐
2.保证集群可以承担预期流量

image-20230212204117571.png

4.4 流量突增-代理连接池

问题背景解决方案实施效果
1.突增流量导致大量建联
2.大量建联导致负载变大,延时上升
1.业务侧预热连接池
2.代理侧预热连接池
3.代理侧支持连接队列
1.避免 DB 被突增流量打死
2.避免代理和 DB 被大量建联打死

image-20230212204318521.png

4.5 稳定性 & 可靠性

为什么要高可用:

恶意事故:程序员删库跑路?哪个程序员不想执行一把 rm -f* ?

偶然事故:如果一个机房断电?断网?

某施工队,施工的时候挖掘机把某游戏公司的光纤挖断了,一下午的时间,保守估计损失一个亿。

4.5.1 稳定性 & 可靠性-3AZ 高可用

image-20230212204818706.png

BinLog : binlog 是 mysql 用来记录数据库表结构变更以及表数据修改的的二进制日志,它只会记录表的变更操作,但不会记录 select 和 show 这种查询操作。

数据恢复:误删数据,之后可以通过 mysqlbinlog 工具恢复数据

主从复制:主库将 binlog 传给从库,从库接收到之后读取内容写入从库,实现主库和从库数据一致性

审计:可以通过二进制旧志中的信息进行审计,判断是否对数据库进行注入攻击

4.5.2 稳定性 & 可靠性-HA 管理

问题背景解决方案实施效果
1.db 所在机器异常宕机
2.db 节点异常宕机
1.ha 服务监管、切换宕机节点
2.代理支持配置热加载
3.代理自动屏蔽宕机读节点
1.读节点宕机秒级恢复
2.写节点宕机 30 s 内恢复服务

image-20230212205158370.png

总结

image-20230212205237860.png

课后作业

  1. WAL 日志到底是如何保证数据的持久化,宕机后数据不丢失的?相比于其他方案,WAL 日志都有什么优势?
  2. 除了 Undo Log 之外,是否还有其他方案可以实现 MVCC?
  3. 基于代价的优化器一般需要考虑哪些代价?
  4. 执行器的执行模型,除了本课中提到的火山模型是否还有其他模型?相比于火山模型有什么优劣势?
  5. InnoDB 的 B+ Tree 是怎么实现的?
  6. InnoDB 的 buffer pool 是怎么实现页面管理和淘汰的?

个人总结

  • DBMS 发展历史
  • RDBMS 核心技术 --- SQL 引擎、存储引擎、事务引擎
  • 使用 RDBMS 中遇到的挑战 --- 大流量、流量突增、稳定性

参考引用

后端专场 学习资料六