设计数据密集型应用—数据系统的基石(1-3)

1. 可靠性、可伸缩性、可维护性

「许多应用程序都是数据密集型的,而非计算密集型的。因此 CPU 很少成为这类应用的瓶颈,更大的问题通常来自于数据量、数据复杂性、以及数据的变更速度。

数据密集型应用通常由标准组件构成,包括但是不限于:

  • 数据库(database)—— 存储数据
  • 缓存(cache)—— 加快读取速度
  • 搜索引擎(search indexes)—— 运行用户按照关键字搜索数据
  • 流处理(stream processing)—— 向其他进程发送消息,进行异步处理
  • 批处理(batch processing)—— 定期处理积累的大批量数据

1.1 关于数据系统的思考

越来越多的应用程序有着各种严格而广泛的需求,单个组件不足以满足所有数据处理和存储的需求。整个需求被拆分为一系列能被单个组件高效完成的任务,并通过应用代码将它们缝合起来。

如下图所示为一个组合使用多个组件的数据系统架构:

在这里插入图片描述

注:当你将多个工具组合在一起提供服务时,服务的接口或应用程序编程接口(API),通常向客户端隐藏这些底层实现的细节。

1.2 软件系统中重要的问题

在这里插入图片描述

设计数据系统或服务时可能会遇到很多棘手的问题,例如:

  • 当系统出现问题时,如何确保数据的正确性和完整性
  • 当部分系统退化级别时,如何为客户提供始终如一的良好性能
  • 当负载增加时,如何扩容应对
1.2.1 可靠性

在某些情况下,企业可能会选择牺牲可靠性来降低开发成本或运营成本,但是在做这些的同时,希望企业能够清楚的意识到其影响的范围及其是否可控。

  • 硬件故障 —— 硬件冗余
  • 软件故障 —— 充分测试、进程隔离、解耦、监控
  • 人为错误 —— 良好的管理实践和充分的培训(ps 即使他们怀有最大的善意,人类也是不可靠的

注:一项关于大型互联网服务的研究发现,运维配置错误是导致服务中断的首要原因,而硬件故障(服务器或网络)仅导致了 10% - 25% 的服务中断

1.2.2 可伸缩性

可伸缩性用来描述系统应对负载增长能力的术语。讨论可伸缩性意味着考虑诸如「如果系统以特定方式增长,有什么选项可以应对增长?」和「如何增加计算资源来处理额外的负载?」

描述负载

负载可以用负载参数的数字来描述,参数的最佳选择取决于架构:

  • 每秒 web 服务器发出的请求
  • 数据库的读写比率
  • 聊天室中同时活跃的用户数量
  • 缓存命中率
  • other

描述性能

系统的负载被描述好以后,研究负载增加会发生什么?

  • 增加负载并保持系统资源(CPU、内存、网络带宽)不变时,会对系统性能产生什么影响
  • 增加负载并保持性能不变时,需要增加多少系统资源

注:如何描述系统性能,比如批处理系统,通常关系的是吞吐量(每秒可以处理的记录量)比如 hadoop,对于在线系统,通常更重要的是服务的响应时间,即客户端发送到接收请求之间的时间。

1.2.3 可维护性

软件的大部分开销并不在最初的开发阶段,而是在持续的维护阶段,包括

  • 修复漏洞
  • 保持系统正常运行
  • 调查失效、适配新的平台
  • 为新的场景进行修改
  • 偿还基础债
  • 增加新功能

在设计之初就尽量考虑尽可能减少维护期间的痛苦,为此,我们将特别关注软件系统的三个设计原则:

  • 可操作性 —— 便于运维团队保持系统评完运行
  • 简单性 —— 从系统中消除尽可能多的复杂度,使新的工程师也能轻松理解系统
  • 可演化性 —— 使工程师在未来轻松地对系统进行修改,当需求变化时为新的应用场景做适配

2. 数据模型与查询语言

多数应用使用层层叠加的数据模型构建。对于每层数据模型的关键问题是:它如何用更低一层数据模型来表示的?

  • 应用开发人员,你观察现实世界(里面有人员、组织、货物等)并采用对象或数据结构,以及操控那些数据结构的 API 来进行建模。那些结构通常是基于特定的应用程序。
  • 当要存储那些数据结构时,你可以利用通用数据模型来表示他们,如 JSON 或 XML 文档,关系数据库中的表或图模型。
  • 数据库软件的工程师选定如何以内存、磁盘或网络上的字节来表示 JSON、XML、关系、图数据。这类表示形式使数据有可能以各种方式来查询、搜索、操纵和处理。
  • 在更低的层次上,硬件工程师已经想出了使用电流,光脉冲,磁场或其他东西来表示字节的方法。

注:一个复杂的应用程序可能会有更多的中间层,每个层都通过提供一个明确的数据模型来隐藏更低层次中的复杂性。这些抽象允许不同的人群有效地协作。

2.1 关系模型与文档模型

最著名的数据模型是 SQL ,数据被组织成关系(SQL 中称作表),其中每个关系是元组(SQL 中称为行)的无序集合。

2.1.1 NoSQL 的诞生

NoSQL 被追溯性地重新解释为不仅是 SQL(Not Only SQL)

采用 NoSQL 数据库的背后有以下几个驱动因素:

  • 需要比关系数据库更好的可伸缩性,包括非常大的数据集或非常高的写入吞吐量
  • 相比商业数据库产品,免费和开源软件更受偏爱
  • 关系模型不能很好地支持一些特殊的查询操作
  • 受挫于关系模型的限制性,渴望一种更具多动态性与表现力的数据模型
2.1.2 对象关系不匹配

如果数据存储在关系表中,那么需要一个笨拙的转换层,处于应用程序代码中的对象和表,行,列的数据库模型之间。像 Hibernate 这样的对象关系映射(ORM)框架可以减少这个转换层所需的样板代码数据量,但是它不能完全隐藏这两个模型之间的差异。

注:文档模型比关系模型具有更好的局部性。因为关联信息存储在同一条记录中,一个查询就足够了。

2.1.3 多对一和多对多的关系

使用 ID 的好处是,ID 对人类没有任何意义,因而永远不需要改变; ID 可以保持不变,即使它标识的信息发生变化。

在关系数据库中,通过 ID 来引入其他表中的行是正常的,因为连接很容易。在文档数据库中,一对多树结构没有必要用连接,对连接的支持通常很弱。

注:任何对人类有意义的东西都可能需要在将来的某个时候改变——如果这些信息被复制,所有的冗余副本也需要更新。这会导致写入开销,也存在不一致风险。

2.1.4 文档数据库是否在重蹈覆辙

在多对多的关系和连接已经常规用在关系数据库时,文档数据库和 NoSQL 重启了辩论:如何更好地在数据库中表示多对多关系?

无论是关系模型还是文档模型都没有重走「网络模型」来标识多对多关系的老路,所以并没有重蹈覆辙。(ps 网络模型很像字典树,详细请参考第二章

注:在表示多对一和多对多的关系时,关系数据库和文档数据库并没有根本的不同:在这两种情况下,相关项目都被一哥唯一的标识符引用,这个标识符在关系模型中称为外键,在文档模型中称为文档引用

2.1.5 关系型数据库和文档数据库在今日的对比

支持文档数据模型的主要论据是架构灵活性,因局部性而拥有更好的性能,以及对于某些应用程序而言更接近于应用程序使用的数据结构。关系模型通过为连接提供更好的支持以及支持多对一和多对多的关系来反击。

哪种数据模型更有助于简化应用代码?

没有办法说那种数据模型更有助于简化应用代码,因为它取决于数据项之间的关系种类。对高度关联的数据而言,文档模型是极其糟糕的,关系模型是可以接受的,而选用图形模型是最自然的

文档模型中的架构灵活性?

读时模式:文档数据库在读取数据时通常假定某种结构——即隐式模式,但不由数据库强制执行。

写时模式:关系数据库模式明确,确保所有数据多符合其模式。

注:读时模式类似于编程语言中的动态类型检查,而写时模式类似于静态类型检查。

查询的数据局限性?

局部性仅仅适用于同时需要文档绝大部分内容的情况。数据库通常需要加载整个文档,即使只访问其中的一小部分,这对于大型文档来说是浪费的。更新文档时,通常需要重写,只有不改变文档大小的修改才可以容易地原地执行。因此,通常建议保持相对小的文档,并避免增加文档大小的写入。

文档和关系数据库的融合进?

随着时间的推移,关系数据库和文档数据库变得越来越相似,这是一件好事:数据模型互相补充,如果一个数据库能够处理类似文档的数据,并能够对其执行关系查询,那么应用程序就可以使用最符合其需要的功能组合。

2.2 数据查询语言

在声明式查询语言(SQL或关系代数)中,只需要指定所需数据的模式 — 结果必须符合哪些条件,以及如何将数据转换(例如,排序,分组和集合)— 但不是如何实现这个目标。数据库系统的查询优化器决定使用哪些索引和哪些连接方法,以及何时顺序执行查询的各个部分。

Web 上的声明式查询

在 Web 浏览器中,使用声明式 CSS 样式比使用 JavaScript 命令式地操作样式要好得多。类似地,在数据库中,使用像 SQL 这样的声明式语言比使用命令式查询 API 要好得多。

MapReduce 查询

MapReduce 即不是一个声明式的查询语言,也不是一个完全命令式的查询 API ,而是处于两者之间,查询的逻辑用代码片段来表示,这些代码片段会被处理框架重复性调用。它基于 map 和 reduce 函数,两个函数存在于许多函数式编程语言中。

2.3 图数据模型

关系模型可以处理多对多关系的简单情况,但是随着数据之间的连接变得复杂,将数据建模为图形显得更加适合。典型的例子:

  • 社交图谱:顶点是人,边指示哪些人彼此认识。
  • 网络图谱:顶点是网页,边缘表示指向其他页面的 HTML 链接。
  • 公交或铁路网络:顶点是交叉路口,边线代表它们之间的道路或铁路线。

注:汽车导航系统搜索道路网络中两点之间的最短路径,PageRank 可以用在网络图上来确定网页的流行程度,从而确定该网页在搜索结果中的排名。

3. 存储与检索

一个数据库系统在最基础的层次上需要完成两件事:

  • 当你把数据交给数据库时,它应当把数据存储起来
  • 当你向数据库要数据时,它应当把数据返回给你

作为程序员,为什么要关系数据库内部存储和检索的机制?

你可能不会从头开始实现自己的存储引擎,但是确实要你从许多可用的存储引擎中选择一个合适的。而为了协调存储引擎以适配应用工作负载,也需要你大致了解存储引擎底层究竟在做什么?

注:本章研究两大类存储引擎,日志结构的存储引擎,以及面向页面的存储引擎(B 树)

3.1 驱动数据库的数据结构

世界上最简单的数据库可以用两个 bash 函数来实现:

#!/bin/bash
db_set () {
	echo "$1,$2" >> database
}

db_get () {
	grep "^$1," database | sed -e "s/^$1,//" | tail -n 1
}

db_set:执行 db_set key value,会将键(key)和值(value)存储在数据库,每次对 db_set 的调用都会向文件末尾追加记录,所以更新键的时候旧版本的值不会被覆盖——因而查找最新值的时候,需要找到文件中键最后一次出现的位置。(ps:注意 db_get 中使用了 tail -n 1

注:与 db_set 做的事情类似,许多数据库在内部使用了日志(log),也就是一个仅追加(append-only)的数据文件。真正的数据库有更多的问题需要处理,比如并发控制、回收磁盘空间以及避免日志无限增长、处理错误与部分写入的记录

db_get:必须从头到尾扫描整个数据库文件来查找键的出现,用算法语言来说,查找的开销是 O(n),

注:为了高效查找数据库中的特定键的值,需要一个数据结构——索引。索引是从主数据衍生的附加结构。许多数据库允许添加与删除索引,这不会影响数据的内容,它只影响查询的性能。维护额外的结构会产生开销,特别是在写入时。任何类型的索引通常都会减慢写入速度,因为每次写入数据时都需要更新索引

3.1.1 哈希索引

键值索引不是唯一的索引类型,但键值数据是很常见的。对于更复杂的索引来说,这是一个有用的构建模块。

注:键值存储与大多数编程语言中提供的字典(dictionary)类型非常相似,通常字典都是用散列映射实现的。

在这里插入图片描述

哈希索引需要考虑的问题:

  • 文件格式: csv 不是日志的最佳格式。使用二进制格式更快,更简单。
  • 删除记录:删除一个键及其关联的值,必须要在数据文件中附加一个特殊的删除记录。当日志段被合并时,逻辑删除告诉合并过程放弃删除键的任何以前的值。
  • 崩溃恢复:如果数据库重新启动,则内存散列映射将丢失。比如 Bitcask 通过存储加速恢复磁盘上每个段的哈希映射的快照,可以更快地加载到内存中。
  • 部分写入记录:数据库可能随时崩溃,包括将记录附加到日志中途。比如 Bitcask 文件包含校验和,允许检测和忽略日志的这些损坏部分。
  • 并发控制:由于写操作以严格的顺序附加到日志中,所以常见的实现选择是只有一个写入器线程。数据文件段是附加的,或者是不可变的,所以它们可以同时被多个线程同时读取。

注:哈希表索引存在以下局限性

  1. 散列表必须能放进内存
  2. 磁盘哈希映射很难表现优秀,它需要大量的随机访问 I/O ,当它变满时增长是很昂贵的,并且散列冲突需要很多的逻辑。
3.1.2 SSTable 和 LSM 树

哈希索引中,每个日志结构的存储段都是一系列键值对。这些对按照它们的写入顺序出现,日志中稍后的值优先于日志中较早的相同键的值。除此之外,文件中键值对的顺序并不重要。

在这里插入图片描述

可以使用排序字符串表(Sorted String Table)对段文件的格式做简单的变化,要求键值对的序列按键排序,同时保证每个键只在每个合并的段文件中出现一次,使用排序字符串表的优势如下:

  • 合并段是简单而高效的,即使文件大于可用内存。
  • 为了在文件找到一个特定的键,不再需要保存内存中的所有键的索引。

构建和维护 SSTables

如何让数据按照键排序?大致的工具流程如下:

  • 写入时,将其添加到内存中的平衡树数据结构(例如,红黑树)。这个内存树被称为内存表
  • 当内存表大于某个阈值时,将其作为 SSTable 文件写入磁盘。这可以高效的完成。因为树已经维护了按键排序的键值对。新的 SSTable 文件成为数据库的最新部分。当 SSTable 被写入磁盘时,写入可以继续到一个新的内存表实例。
  • 为了提供读取请求,首先尝试在内存表中找到关键字,然后在最近的磁盘段中,然后在下一个较旧的段中找到该关键字。
  • 有时会在后台运行合并和压缩的过程以组合段文件并丢弃覆盖或删除的值。

用 SSTable 制作 LSM 树

LSM 树(日志结构合并树),基于合并和压缩排序文件原理的存储引擎通常被称为 LSM 存储引擎。在 LevelDB 和 RocksDB 中均有实现。

LSM 树的基本思想——保存一系列在后台合并的 SSTables —— 简单而高效。即使数据集比可用内存大得多,它仍能继续正常工作。由于数据按排序顺序存储,因此可以高效地执行范围查询(扫描所有高于某些最小值和最高值的的所有键),并且因为磁盘写入是连续的,所以 LSM 树可以支持非常高的写入吞吐量。

性能优化

当查找数据库中不存在的键时,LSM 树算法可能会很慢:必须先检查内存表,然后将这些段一直回到最老的(可能必须从磁盘上读取每一个)然后才能确定键不存在。

注:为了优化这种访问,存储引擎通常使用额外的 Bloom 过滤器。

3.2 B树

像 SSTables 一样,B树保持按键排序的键值对,这允许高效的键值查询和范围查询。 前面的日志结构索引将数据库分解为可变大小的段,通常是几兆字节或更大的大小,并且总是按顺序编写段。相比之下,B 树将数据库分解成固定大小的块或页面,传统上大小为 4KB(或更大),并且一次只能读取或写入一个页面。

在这里插入图片描述

注:在 B 树的一个页面中对子页面的引用的数量成为分支因子,例如,上图的分支因子为 6,在实践中,分支因子取决于存储页面参考和范围边界所需要的空间量,通常是几百个。

让 B树更可靠

B 树的基本底层写操作是用新的数据覆盖磁盘上的页面。假定覆盖不改变页面的位置:即,当页面被覆盖时,对该页面的所有引用保持完整。这与日志结构索引(LSM 树)形成鲜明对比,后者只附加到文件(并最终删除过时的文件)但从不修改文件。

为了使数据库对崩溃具有韧性,B树实现通常会带有一个额外的磁盘数据结构:预写式日志(WAL)(也称作重做日志(redo log)。这是一个仅追加的文件,每个 B 树修改都可以应用到树本身的页面上。当数据库在崩溃恢复时,这个日志被用来使 B树恢复到一致的状态。

B树优化

  • 一些数据库使用写时复制方案,而不是覆盖页面并维护 WAL 进行崩溃恢复。修改的页面被写入到不同的位置,并且树中的父页面的新版本被创建,指向新的位置。
  • 可以通过不存储整个键来节省页面空间,但可以缩小它的大小。在树内部的页面上,键只需要提供足够的信息来充当键方位之间的边界。在页面中包含更多的键允许树具有更高的分支因子,因此更少的层次。
  • B 树实现尝试布局树,使得叶子页面按顺序出现在磁盘上。但是,随着树的增长,维持这个顺序很困难。
  • 在树中添加额外的指针,例如,每个叶子页面可以在左边和右边具有对其兄弟页面的引用,这允许不跳回父页面就能顺序扫描。
  • B 树的变种如分形树借用一些日志结构的思想来减少磁盘寻道。

3.3 比较 B树和 LSM树

通常 LSM树的写入速度更快,而 B树的读取速度更快。LSM树上读取通常比较慢,因为它们必须在压缩的不同阶段检查几个不同的数据结构和 SSTables。

LSM树的优点

  • LSM树通常比 B 树支持更高的写入吞吐量,部分原因是因为它们具有较低的写放大,部分是因为它们顺序地写入紧凑的 SSTable 文件而不是必须覆盖树中的几个页面。这种差异在磁盘硬盘驱动器上尤其重要,顺序写入比随机写入快得多。

  • LSM树可以被压缩得更好,因此经常比 B树在磁盘上产生更小的文件。B 树存储引擎会由于分割而留下一些未被使用的磁盘空间:当页面被拆分或某行不能放入现有页面时,页面中的某些空间仍未被使用。由于 LSM树不是面向页面的,并且定期重写 SSTables 以去除碎片,所以它们具有较低的存储开销,特别是当使用平坦压缩时。

注:在数据库的生命周期中写入数据库导致对磁盘的多次写入——被称为写放大。

LSM树的缺点

  • 日志结构的存储的缺点是压缩过程有时会干扰正在进行的读写操作。尽管存储引擎尝试逐步执行压缩而不影响并发访问,但是磁盘资源有限,所以很容易发生因为压缩操作请求等待磁盘。对吞吐量和平均响应时间影响通常很小,但是在更高百分比的情况下,对日志结构化存储引擎的查询响应时间有时会相当长,而 B 树的行为则相对于更可测。

  • 如果写入吞吐量很高,并且压缩没有仔细配置,压缩跟不上写入速率。这种情况下,磁盘上未合并段的数量不断增加,直到磁盘空间用完,读取速度也会减慢,因为它们需要检查更多段文件。

3.4 其他索引结构

将值存储在索引中

索引中的关键字是查询索引的内容,但是该值可以有以下两种情况:

  • 存储实际的行、文档、顶点
  • 存储在别处的行的引用

注:例如在 MySQL 的 InnoDB 存储引擎中,表的主键总是一个聚簇索引,二级索引引用主键

多列索引

多列索引又称为连接索引,它通过将一个列的值追加到另一个列的后面,简单地将多个字段组合成一个键。

多维索引

一种查询多个列的更一般的方法,这对于地理空间的数据尤为重要。例如,餐厅搜索网站可能有一个数据库,其中包含每个餐厅的经度和纬度。

注:一个标准的 B 树和 LSM 树索引不能高效地响应这种查询,一种选择是使用空间填充曲线将二维位置转换为单个数字,然后使用常规的 B树索引。更普遍的是,使用特殊化的空间索引,例如 R树。

全文搜索和模糊索引

  • 全文索引:通常允许一个单词以扩展为该单词的同义词,忽略单词的语法变体,并且搜索相同文档中彼此靠近的单词的出现,并且支持各种其他功能取决于文本的语言分析。
  • 模糊索引:朝着文档分类和机器学习的方式发展。

在内存中存储一切

  • 内存数据库的性能优势并不是因为它们不需要从磁盘读取的事实。因为即使是基于磁盘的存储引擎也不可能永远需要从磁盘读取,因为操作系统缓存最近在内存中使用的磁盘块。它们更快的原因在于省去了将内存数据结构编码为磁盘数据结构的开销
  • 内存数据库的另一个有趣的领域是提供难以用基于磁盘的索引实现的数据模型,例如,Redis 为各种数据结构(如优先级队列和集合)提供了类似数据库的接口。因为它将所有数据保存在内存中,所以它的实现相对简单。

3.5 事务处理还是分析

比较事务处理和分析的特点*

属性事务处理 OLTP分析系统 OLAP
主要读取模式查询少量记录,按键读取在大批量记录上聚合
主要写入模式随机访问,写入要求低延时批量导入(ETL),事件流
主要用户终端用户,通过Web应用内部数据分析师,决策支持
处理的数据数据的最新状态(当前时间点)随时间推移的历史事件
数据集尺寸GB ~ TBTB ~ PB

注:在二十世纪八十年代末和九十年代初期,公司有停止使用 OLTP 系统进行分析的趋势,而是在单独的数据库上进行分析。这个单独的数据库被称为数据仓库。

数据仓库

数据仓库包含公司各种 OLTP 系统中所有的只读数据副本。从 OLTP 数据库中抽取刷数据,转换成合适的分析模式,清理并加载到数据仓库中。将数据存入仓库的过程称为"抽取—转换—加载(ETL)"

在这里插入图片描述

OLTP数据库和数据仓库之间的分歧

表面上,一个数据仓库和一个关系 OLTP 数据库看起来很相似,因为它们都有一个 SQL 查询接口。然而,系统的内部看起来可能完全不同,因为他们针对非常不同的查询模式进行优化。现在许多数据库供应商都将重点放在支持事务处理货分析工作负载上,而不是两者都支持。

星型和雪花型:分析的模式

星型模型:当表关系可视化时,事实表在中间,由维表包围;与这些表的连接就像星星的光芒。

注:这个模式的变体称为雪花模式

3.6 列存储

在大多数 OLTP 数据库中,存储都是以面向行的方式进行布局的:表格的一行中的所有值都是相邻存储。文档数据库是相似的:整个文档通常存储为一个连续的字节序列。

面向列的存储背后的想法很简单:不是将所有来自一行的值存储在一起,而是将来自每一列的所有值存储在一起。如果每个列存储在一个单独的文件中,查询只需要读取和解析查询中使用的列,这可以节省大量的工作。

列压缩

除了仅从磁盘加载查询所需要的列以外,我们还可以通过压缩数据来进一步降低对磁盘吞吐量的需求。面向列的存储通常很适合压缩,在数据仓库中特别有效的一种技术是位图编码。

注:面向列的存储和列族

Cassandra 和 HBase 有一个列族的概念,他们从 Bigtable 继承。然而,把它们称为面向列是非常具有误导性的:在每个列族中,它们将一行中的所有列于行键一起存储,并且不使用列压缩。因此,Bigtable 模型仍然主要是面向行的。

内存带宽

对于需要扫描数百万行的数据仓库查询来说,一个巨大的瓶颈是从磁盘获取数据到内存的带宽。除了减少需要从磁盘加载的数据量以外,面向列的存储布局也可以有效利用 CPU 周期。

3.7列存储中的排序顺序

在列存储中,存储行的顺序并不一定很重要。按插入顺序存储它们是最简单的,因为插入一个新行就意味着附加到每个列文件。但是,我们可以选择强制执行一个命令,就像我们之前对 SSTables 所做的那样,并将其用作索引机制。

注:每列独自排序是没有意义的,也需一次对整行进行排序。数据库的管理员可以使用他们对常见查询的知识来选择表格应该被排序的列。

几个不同的排序顺序

在一个面向列的存储中有多个排序顺序有点类似于在一个面向行的存储中有多个二级索引。但最大的区别在于面向行的存储将每一行保存在一个地方(在堆文件或聚簇索引中),二级索引只包含指向匹配行的指针。在列存储中,通常在其他地方没有任何指向数据的指针,只有包含值的列。

3.8 写入列存储

以上的优化在数据仓库中是有意义的,因为大多数负载由分析人员运行的大型只读查询组成。面向列的储存,压缩和排序都有助于更快地读取这些查询。然而,他们有写更加困难的缺点。

3.9 聚合:数据立方体和物化视图

数据仓库的另一个值得一提的是物化汇总。如前所述,数据仓库查询通常涉及一个聚合函数,如 SQL 中的 COUNT、SUM、AVG、MIN 或 MAX。如果相同的聚合被许多不同的查询使用,那么每次都可以通过原始数据来处理。为什么不缓存一些查询使用最频繁的计数或综合?

针对于上面创建这种缓存的方式是物化视图。在关系数据模型中,还有一种通常被定义为一个虚拟视图:一个类似于表的对象,其内容是一些查询的结果。

虚拟视图:只是写入查询的捷径。从虚拟视图读取时,SQL 引擎会将其展开到视图的底层查询中,然后处理展开的查询。

物化视图:当底层数据发生变化时,物化视图需要更新,因为它是数据的非规范化副本。数据库可以自动完成,但是这样的更新使得写入成本更高,这就是在 OLTP 数据库中不经常使用物化视图的原因。

3.10 总结

在 事务处理(OLTP)中,我们能看到两派主流的存储引擎:

  • 日志结构学派:

    只允许附加到文件和删除过时的文件,但不会更新已经写入的文件。Bitcask,SSTables,LSM 树,LevelDB,Cassandra,HBase,Lucene 等都属于这个类别。

  • 就地更新学派

    将磁盘视为一组可以重写的固定大小的页面。B树是这种哲学的典范,用在所有主要的关系数据库和许多非关系数据库。

4. 碎碎念

断断续续的摘抄了半个月,才写完了前三章,大概心情不好的时候不适合写技术文章吧🥺

  • 我们要拼尽全力,在这不是很愉快的日子里去搜刮藏在生活里的所有温柔呀!
  • 慢慢的,慢慢的,我们总会长大的,只是在未来自己会变成什么样子,可能我们自己也想不明白。

就这样吧,第四章的总结拖到下次吧,嗯,应该来的急。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值