2
0
Fork 0
mirror of https://github.com/Vonng/ddia.git synced 2026-06-23 18:06:52 +08:00

Merge pull request #88 from kemingy/fix-typo

fix typo for ch1, ch2, ch3, ch4
This commit is contained in:
Feng Ruohang 2020-10-23 22:58:00 +08:00 committed by GitHub
commit 4ebcb8b7a2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 26 additions and 26 deletions

6
ch2.md
View file

@ -531,7 +531,7 @@ db.observations.aggregate([
可以将那些众所周知的算法运用到这些图上例如汽车导航系统搜索道路网络中两点之间的最短路径PageRank可以用在网络图上来确定网页的流行程度从而确定该网页在搜索结果中的排名。 可以将那些众所周知的算法运用到这些图上例如汽车导航系统搜索道路网络中两点之间的最短路径PageRank可以用在网络图上来确定网页的流行程度从而确定该网页在搜索结果中的排名。
在刚刚给出的例子中图中的所有顶点代表了相同类型的事物网页或交叉路口。不过图并不局限于这样的同类数据同样强大地是图提供了一种一致的方式用来在单个数据存储中存储完全不同类型的对象。例如Facebook维护一个包含许多不同类型的顶点和边的单个图顶点表示人地点事件签到和用户的评论;边缘表示哪些人是彼此的朋友哪个签到发生在何处谁评论了哪条消息谁参与了哪个事件等等【35】。 在刚刚给出的例子中图中的所有顶点代表了相同类型的事物网页或交叉路口。不过图并不局限于这样的同类数据同样强大地是图提供了一种一致的方式用来在单个数据存储中存储完全不同类型的对象。例如Facebook维护一个包含许多不同类型的顶点和边的单个图顶点表示人地点事件签到和用户的评论边缘表示哪些人是彼此的朋友哪个签到发生在何处谁评论了哪条消息谁参与了哪个事件等等【35】。
在本节中,我们将使用[图2-5](img/fig2-5.png)所示的示例。它可以从社交网络或系谱数据库中获得它显示了两个人来自爱达荷州的Lucy和来自法国Beaune的Alain。他们已婚住在伦敦。 在本节中,我们将使用[图2-5](img/fig2-5.png)所示的示例。它可以从社交网络或系谱数据库中获得它显示了两个人来自爱达荷州的Lucy和来自法国Beaune的Alain。他们已婚住在伦敦。
@ -558,7 +558,7 @@ db.observations.aggregate([
* 描述两个顶点之间关系类型的标签 * 描述两个顶点之间关系类型的标签
* 一组属性(键值对) * 一组属性(键值对)
可以将图存储看作由两个关系表组成:一个存储顶点,另一个存储边,如[例2-2]()所示该模式使用PostgreSQL json数据类型来存储每个顶点或每条边的属性)。头部和尾部顶点用来存储每条边;如果你想要一组顶点的输入或输出边,你可以分别通过`head_vertex`或`tail_vertex`来查询`edges`表。 可以将图存储看作由两个关系表组成:一个存储顶点,另一个存储边,如[例2-2]()所示该模式使用PostgreSQL JSON数据类型来存储每个顶点或每条边的属性)。头部和尾部顶点用来存储每条边;如果你想要一组顶点的输入或输出边,你可以分别通过`head_vertex`或`tail_vertex`来查询`edges`表。
**例2-2 使用关系模式来表示属性图** **例2-2 使用关系模式来表示属性图**
@ -926,7 +926,7 @@ Cypher和SPARQL使用SELECT立即跳转但是Datalog一次只进行一小步
虽然我们已经覆盖了很多层面,但仍然有许多数据模型没有提到。举几个简单的例子: 虽然我们已经覆盖了很多层面,但仍然有许多数据模型没有提到。举几个简单的例子:
* 使用基因组数据的研究人员通常需要执行**序列相似性搜索**这意味着需要一个很长的字符串代表一个DNA分子并在一个拥有类似但不完全相同的字符串的大型数据库中寻找匹配。这里所描述的数据库都不能处理这种用法这就是为什么研究人员编写了像GenBank这样的专门的基因组数据库软件的原因【48】。 * 使用基因组数据的研究人员通常需要执行**序列相似性搜索**这意味着需要一个很长的字符串代表一个DNA分子并在一个拥有类似但不完全相同的字符串的大型数据库中寻找匹配。这里所描述的数据库都不能处理这种用法这就是为什么研究人员编写了像GenBank这样的专门的基因组数据库软件的原因【48】。
* 粒子物理学家数十年来一直在进行大数据类型的大规模数据分析像大型强子对撞机LHC这样的项目现在可以工作在数百亿兆字节的范围内在这样的规模下需要定制解决方案来阻硬件成本的失控【49】。 * 粒子物理学家数十年来一直在进行大数据类型的大规模数据分析像大型强子对撞机LHC这样的项目现在可以工作在数百亿兆字节的范围内在这样的规模下需要定制解决方案来阻硬件成本的失控【49】。
* **全文搜索**可以说是一种经常与数据库一起使用的数据模型。信息检索是一个很大的专业课题,我们不会在本书中详细介绍,但是我们将在第三章和第三章中介绍搜索索引。 * **全文搜索**可以说是一种经常与数据库一起使用的数据模型。信息检索是一个很大的专业课题,我们不会在本书中详细介绍,但是我们将在第三章和第三章中介绍搜索索引。
让我们暂时将其放在一边。在[下一章](ch3.md)中,我们将讨论在**实现**本章描述的数据模型时会遇到的一些权衡。 让我们暂时将其放在一边。在[下一章](ch3.md)中,我们将讨论在**实现**本章描述的数据模型时会遇到的一些权衡。

10
ch3.md
View file

@ -105,7 +105,7 @@ $ cat database
**图3-3 同时执行压缩和分段合并** **图3-3 同时执行压缩和分段合并**
每个段现在都有自己的内存散列表,将键映射到文件偏移量。为了找到一个键的值,我们首先检查最近段的哈希映射;如果键不存在,我们检查第二个最近的段,依此类推。合并过程保持细分的数量,所以查找不需要检查许多哈希映射。 每个段现在都有自己的内存散列表,将键映射到文件偏移量。为了找到一个键的值,我们首先检查最近段的哈希映射如果键不存在,我们检查第二个最近的段,依此类推。合并过程保持细分的数量,所以查找不需要检查许多哈希映射。
大量的细节进入实践这个简单的想法工作。简而言之,一些真正实施中重要的问题是: 大量的细节进入实践这个简单的想法工作。简而言之,一些真正实施中重要的问题是:
***文件格式*** ***文件格式***
@ -201,7 +201,7 @@ Lucene是Elasticsearch和Solr使用的一种全文搜索的索引引擎它使
#### 性能优化 #### 性能优化
与往常一样大量的细节使得存储引擎在实践中表现良好。例如当查找数据库中不存在的键时LSM树算法可能会很慢您必须检查内存表然后将这些段一直回到最老的可能必须从磁盘读取每一个然后才能确定键不存在。为了优化这种访问存储引擎通常使用额外的Bloom过滤器【15】。 (布隆过滤器是用于近似集合内容的内存高效数据结构,它可以告诉您数据库中是否出现键,从而为不存在的键节省许多不必要的磁盘读取操作。 与往常一样大量的细节使得存储引擎在实践中表现良好。例如当查找数据库中不存在的键时LSM树算法可能会很慢您必须检查内存表然后将这些段一直回到最老的可能必须从磁盘读取每一个然后才能确定键不存在。为了优化这种访问存储引擎通常使用额外的Bloom过滤器【15】。 (布隆过滤器是用于近似集合内容的内存高效数据结构,它可以告诉您数据库中是否出现键,从而为不存在的键节省许多不必要的磁盘读取操作。)
还有不同的策略来确定SSTables如何被压缩和合并的顺序和时间。最常见的选择是大小分层压实。 LevelDB和RocksDB使用平坦压缩LevelDB因此得名HBase使用大小分层Cassandra同时支持【16】。在规模级别的调整中更新和更小的SSTables先后被合并到更老的和更大的SSTable中。在水平压实中关键范围被拆分成更小的SSTables而较旧的数据被移动到单独的“水平”这使得压缩能够更加递增地进行并且使用更少的磁盘空间。 还有不同的策略来确定SSTables如何被压缩和合并的顺序和时间。最常见的选择是大小分层压实。 LevelDB和RocksDB使用平坦压缩LevelDB因此得名HBase使用大小分层Cassandra同时支持【16】。在规模级别的调整中更新和更小的SSTables先后被合并到更老的和更大的SSTable中。在水平压实中关键范围被拆分成更小的SSTables而较旧的数据被移动到单独的“水平”这使得压缩能够更加递增地进行并且使用更少的磁盘空间。
@ -243,7 +243,7 @@ Lucene是Elasticsearch和Solr使用的一种全文搜索的索引引擎它使
#### 让B树更可靠 #### 让B树更可靠
B树的基本底层写操作是用新数据覆盖磁盘上的页面。假定覆盖不改变页面的位置;当页面被覆盖时对该页面的所有引用保持完整。这与日志结构索引如LSM树形成鲜明对比后者只附加到文件并最终删除过时的文件但从不修改文件。 B树的基本底层写操作是用新数据覆盖磁盘上的页面。假定覆盖不改变页面的位置当页面被覆盖时对该页面的所有引用保持完整。这与日志结构索引如LSM树形成鲜明对比后者只附加到文件并最终删除过时的文件但从不修改文件。
您可以考虑将硬盘上的页面覆盖为实际的硬件操作。在磁性硬盘驱动器上这意味着将磁头移动到正确的位置等待旋转盘上的正确位置出现然后用新的数据覆盖适当的扇区。在固态硬盘上由于SSD必须一次擦除和重写相当大的存储芯片块所以会发生更复杂的事情【19】。 您可以考虑将硬盘上的页面覆盖为实际的硬件操作。在磁性硬盘驱动器上这意味着将磁头移动到正确的位置等待旋转盘上的正确位置出现然后用新的数据覆盖适当的扇区。在固态硬盘上由于SSD必须一次擦除和重写相当大的存储芯片块所以会发生更复杂的事情【19】。
@ -259,7 +259,7 @@ B树的基本底层写操作是用新数据覆盖磁盘上的页面。假定覆
* 一些数据库如LMDB使用写时复制方案【21】而不是覆盖页面并维护WAL进行崩溃恢复。修改的页面被写入到不同的位置并且树中的父页面的新版本被创建指向新的位置。这种方法对于并发控制也很有用我们将在“[快照隔离和可重复读](ch7.md#快照隔离和可重复读)”中看到。 * 一些数据库如LMDB使用写时复制方案【21】而不是覆盖页面并维护WAL进行崩溃恢复。修改的页面被写入到不同的位置并且树中的父页面的新版本被创建指向新的位置。这种方法对于并发控制也很有用我们将在“[快照隔离和可重复读](ch7.md#快照隔离和可重复读)”中看到。
* 我们可以通过不存储整个键来节省页面空间,但可以缩小它的大小。特别是在树内部的页面上,键只需要提供足够的信息来充当键范围之间的边界。在页面中包含更多的键允许树具有更高的分支因子,因此更少的层次 * 我们可以通过不存储整个键来节省页面空间,但可以缩小它的大小。特别是在树内部的页面上,键只需要提供足够的信息来充当键范围之间的边界。在页面中包含更多的键允许树具有更高的分支因子,因此更少的层次
* 通常,页面可以放置在磁盘上的任何位置;没有什么要求附近的键范围页面附近的磁盘上。如果查询需要按照排序顺序扫描大部分关键字范围那么每个页面的布局可能会非常不方便因为每个读取的页面都可能需要磁盘查找。因此许多B树实现尝试布局树使得叶子页面按顺序出现在磁盘上。但是随着树的增长维持这个顺序是很困难的。相比之下由于LSM树在合并过程中一次又一次地重写存储的大部分所以它们更容易使顺序键在磁盘上彼此靠近。 * 通常,页面可以放置在磁盘上的任何位置;没有什么要求附近的键放在页面附近的磁盘上。如果查询需要按照排序顺序扫描大部分关键字范围那么每个页面的布局可能会非常不方便因为每个读取的页面都可能需要磁盘查找。因此许多B树实现尝试布局树使得叶子页面按顺序出现在磁盘上。但是随着树的增长维持这个顺序是很困难的。相比之下由于LSM树在合并过程中一次又一次地重写存储的大部分所以它们更容易使顺序键在磁盘上彼此靠近。
* 额外的指针已添加到树中。例如,每个叶子页面可以在左边和右边具有对其兄弟页面的引用,这允许不跳回父页面就能顺序扫描。 * 额外的指针已添加到树中。例如,每个叶子页面可以在左边和右边具有对其兄弟页面的引用,这允许不跳回父页面就能顺序扫描。
* B树的变体如分形树【22】借用一些日志结构的思想来减少磁盘寻道而且它们与分形无关 * B树的变体如分形树【22】借用一些日志结构的思想来减少磁盘寻道而且它们与分形无关
@ -267,7 +267,7 @@ B树的基本底层写操作是用新数据覆盖磁盘上的页面。假定覆
尽管B树实现通常比LSM树实现更成熟但LSM树由于其性能特点也非常有趣。根据经验通常LSM树的写入速度更快而B树的读取速度更快【23】。 LSM树上的读取通常比较慢因为它们必须在压缩的不同阶段检查几个不同的数据结构和SSTables。 尽管B树实现通常比LSM树实现更成熟但LSM树由于其性能特点也非常有趣。根据经验通常LSM树的写入速度更快而B树的读取速度更快【23】。 LSM树上的读取通常比较慢因为它们必须在压缩的不同阶段检查几个不同的数据结构和SSTables。
然而,基准通常对工作量的细节不确定敏感。 您需要测试具有特定工作负载的系统,以便进行有效的比较。 在本节中,我们将简要讨论一些在衡量存储引擎性能时值得考虑的事情。 然而,基准通常对工作量的细节不确定敏感。 您需要测试具有特定工作负载的系统,以便进行有效的比较。 在本节中,我们将简要讨论一些在衡量存储引擎性能时值得考虑的事情。
#### LSM树的优点 #### LSM树的优点

8
ch4.md
View file

@ -191,7 +191,7 @@ Thrift有一个专用的列表数据类型它使用列表元素的数据类
Apache Avro 【20】是另一种二进制编码格式与Protocol Buffers和Thrift有趣的不同。 它是作为Hadoop的一个子项目在2009年开始的因为Thrift不适合Hadoop的用例【21】。 Apache Avro 【20】是另一种二进制编码格式与Protocol Buffers和Thrift有趣的不同。 它是作为Hadoop的一个子项目在2009年开始的因为Thrift不适合Hadoop的用例【21】。
Avro也使用模式来指定正在编码的数据的结构。 它有两种模式语言一种Avro IDL用于人工编辑一种基于JSON更易于机器读取。 Avro也使用模式来指定正在编码的数据的结构。 它有两种模式语言一种Avro IDL用于人工编辑一种基于JSON更易于机器读取。
我们用Avro IDL编写的示例模式可能如下所示 我们用Avro IDL编写的示例模式可能如下所示
@ -225,7 +225,7 @@ record Person {
**图4-5 使用Avro编码的记录** **图4-5 使用Avro编码的记录**
为了解析二进制数据,您按照它们出现在架构中的顺序遍历这些字段,并使用架构来告诉您每个字段的数据类型。这意味着如果读取数据的代码使用与写入数据的代码完全相同的模式,则只能正确解码二进制数据。阅读器和作者之间的模式不匹配意味着错误地解码数据。 为了解析二进制数据,您按照它们出现在架构中的顺序遍历这些字段,并使用架构来告诉您每个字段的数据类型。这意味着如果读取数据的代码使用与写入数据的代码完全相同的模式,则只能正确解码二进制数据。读者和作者之间的模式不匹配意味着错误地解码数据。
那么Avro如何支持模式演变呢 那么Avro如何支持模式演变呢
@ -300,7 +300,7 @@ Avro为静态类型编程语言提供了可选的代码生成功能但是它
正如我们所看到的Protocol BuffersThrift和Avro都使用模式来描述二进制编码格式。他们的模式语言比XML模式或者JSON模式简单得多它支持更详细的验证规则例如“该字段的字符串值必须与该正则表达式匹配”或“该字段的整数值必须在0和100之间“。由于Protocol BuffersThrift和Avro实现起来更简单使用起来也更简单所以它们已经发展到支持相当广泛的编程语言。 正如我们所看到的Protocol BuffersThrift和Avro都使用模式来描述二进制编码格式。他们的模式语言比XML模式或者JSON模式简单得多它支持更详细的验证规则例如“该字段的字符串值必须与该正则表达式匹配”或“该字段的整数值必须在0和100之间“。由于Protocol BuffersThrift和Avro实现起来更简单使用起来也更简单所以它们已经发展到支持相当广泛的编程语言。
这些编码所基于的想法绝不是新的。例如它们与ASN.1有很多相似之处它是1984年首次被标准化的模式定义语言【27】。它被用来定义各种网络协议其二进制编码DER仍然被用于编码SSL证书X.509例如【28】。 ASN.1支持使用标签号码的模式演进类似于Protocol Buf-fers和Thrift 【29】。然而这也是非常复杂和严重的文件记录所以ASN.1可能不是新应用程序的好选择。 这些编码所基于的想法绝不是新的。例如它们与ASN.1有很多相似之处它是1984年首次被标准化的模式定义语言【27】。它被用来定义各种网络协议其二进制编码DER仍然被用于编码SSL证书X.509例如【28】。 ASN.1支持使用标签号码的模式演进类似于Protocol Buffers和Thrift 【29】。然而这也是非常复杂和严重的文件记录所以ASN.1可能不是新应用程序的好选择。
许多数据系统也为其数据实现某种专有的二进制编码。例如大多数关系数据库都有一个网络协议您可以通过该协议向数据库发送查询并获取响应。这些协议通常特定于特定的数据库并且数据库供应商提供将来自数据库的网络协议的响应解码为内存数据结构的驱动程序例如使用ODBC或JDBC API 许多数据系统也为其数据实现某种专有的二进制编码。例如大多数关系数据库都有一个网络协议您可以通过该协议向数据库发送查询并获取响应。这些协议通常特定于特定的数据库并且数据库供应商提供将来自数据库的网络协议的响应解码为内存数据结构的驱动程序例如使用ODBC或JDBC API
@ -382,7 +382,7 @@ Web浏览器不是唯一的客户端类型。例如在移动设备或桌面
此外服务器本身可以是另一个服务的客户端例如典型的Web应用服务器充当数据库的客户端。这种方法通常用于将大型应用程序按照功能区域分解为较小的服务这样当一个服务需要来自另一个服务的某些功能或数据时就会向另一个服务发出请求。这种构建应用程序的方式传统上被称为**面向服务的体系结构service-oriented architectureSOA**,最近被改进和更名为**微服务架构**【31,32】。 此外服务器本身可以是另一个服务的客户端例如典型的Web应用服务器充当数据库的客户端。这种方法通常用于将大型应用程序按照功能区域分解为较小的服务这样当一个服务需要来自另一个服务的某些功能或数据时就会向另一个服务发出请求。这种构建应用程序的方式传统上被称为**面向服务的体系结构service-oriented architectureSOA**,最近被改进和更名为**微服务架构**【31,32】。
在某些方面,服务类似于数据库:它们通常允许客户端提交和查询数据。但是,虽然数据库允许使用我们在第2章 中讨论的查询语言进行任意查询但是服务公开了一个特定于应用程序的API它只允许由服务的业务逻辑应用程序代码预定的输入和输出【33】。这种限制提供了一定程度的封装服务可以对客户可以做什么和不可以做什么施加细粒度的限制。 在某些方面,服务类似于数据库:它们通常允许客户端提交和查询数据。但是,虽然数据库允许使用我们在[第2章](./ch2.md)中讨论的查询语言进行任意查询但是服务公开了一个特定于应用程序的API它只允许由服务的业务逻辑应用程序代码预定的输入和输出【33】。这种限制提供了一定程度的封装服务可以对客户可以做什么和不可以做什么施加细粒度的限制。
面向服务/微服务架构的一个关键设计目标是通过使服务独立部署和演化来使应用程序更易于更改和维护。例如每个服务应该由一个团队拥有并且该团队应该能够经常发布新版本的服务而不必与其他团队协调。换句话说我们应该期望服务器和客户端的旧版本和新版本同时运行因此服务器和客户端使用的数据编码必须在不同版本的服务API之间兼容——正是我们所做的本章一直在谈论。 面向服务/微服务架构的一个关键设计目标是通过使服务独立部署和演化来使应用程序更易于更改和维护。例如每个服务应该由一个团队拥有并且该团队应该能够经常发布新版本的服务而不必与其他团队协调。换句话说我们应该期望服务器和客户端的旧版本和新版本同时运行因此服务器和客户端使用的数据编码必须在不同版本的服务API之间兼容——正是我们所做的本章一直在谈论。