fix punctuation marks

This commit is contained in:
Gang Yin 2022-01-06 10:48:36 +08:00
parent c6210b95d1
commit 072814f29e
22 changed files with 64 additions and 64 deletions

12
ch1.md
View File

@ -120,7 +120,7 @@
### 人为错误
设计并构建了软件系统的工程师是人类维持系统运行的运维也是人类。即使他们怀有最大的善意人类也是不可靠的。举个例子一项关于大型互联网服务的研究发现运维配置错误是导致服务中断的首要原因而硬件故障服务器或网络仅导致了10-25的服务中断【13】。
设计并构建了软件系统的工程师是人类维持系统运行的运维也是人类。即使他们怀有最大的善意人类也是不可靠的。举个例子一项关于大型互联网服务的研究发现运维配置错误是导致服务中断的首要原因而硬件故障服务器或网络仅导致了10-25%的服务中断【13】。
尽管人类不可靠,但怎么做才能让系统变得可靠?最好的系统会组合使用以下几种办法:
@ -222,11 +222,11 @@
通常使用**百分位点percentiles** 会更好。如果将响应时间列表按最快到最慢排序,那么**中位数median** 就在正中间举个例子如果你的响应时间中位数是200毫秒这意味着一半请求的返回时间少于200毫秒另一半比这个要长。
如果想知道典型场景下用户需要等待多长时间那么中位数是一个好的度量标准一半用户请求的响应时间少于响应时间的中位数另一半服务时间比中位数长。中位数也被称为第50百分位点有时缩写为p50。注意中位数是关于单个请求的如果用户同时发出几个请求在一个会话过程中或者由于一个页面中包含了多个资源则至少一个请求比中位数慢的概率远大于50
如果想知道典型场景下用户需要等待多长时间那么中位数是一个好的度量标准一半用户请求的响应时间少于响应时间的中位数另一半服务时间比中位数长。中位数也被称为第50百分位点有时缩写为p50。注意中位数是关于单个请求的如果用户同时发出几个请求在一个会话过程中或者由于一个页面中包含了多个资源则至少一个请求比中位数慢的概率远大于50%
为了弄清异常值有多糟糕可以看看更高的百分位点例如第95、99和99.9百分位点缩写为p95p99和p999。它们意味着9599或99.9的请求响应时间要比该阈值快例如如果第95百分位点响应时间是1.5秒则意味着100个请求中的95个响应时间快于1.5秒而100个请求中的5个响应时间超过1.5秒。如[图1-4](img/fig1-4.png)所示。
为了弄清异常值有多糟糕可以看看更高的百分位点例如第95、99和99.9百分位点缩写为p95p99和p999。它们意味着95%、99%或99.9%的请求响应时间要比该阈值快例如如果第95百分位点响应时间是1.5秒则意味着100个请求中的95个响应时间快于1.5秒而100个请求中的5个响应时间超过1.5秒。如[图1-4](img/fig1-4.png)所示。
响应时间的高百分位点(也称为**尾部延迟**,即**tail latencies**非常重要因为它们直接影响用户的服务体验。例如亚马逊在描述内部服务的响应时间要求时以99.9百分位点为准,即使它只影响一千个请求中的一个。这是因为请求响应最慢的客户往往也是数据最多的客户,也可以说是最有价值的客户 —— 因为他们掏钱了【19】。保证网站响应迅速对于保持客户的满意度非常重要亚马逊观察到响应时间增加100毫秒销售量就减少1【20】而另一些报告说慢 1 秒钟会让客户满意度指标减少16%【2122】。
响应时间的高百分位点(也称为**尾部延迟**,即**tail latencies**非常重要因为它们直接影响用户的服务体验。例如亚马逊在描述内部服务的响应时间要求时以99.9百分位点为准,即使它只影响一千个请求中的一个。这是因为请求响应最慢的客户往往也是数据最多的客户,也可以说是最有价值的客户 —— 因为他们掏钱了【19】。保证网站响应迅速对于保持客户的满意度非常重要亚马逊观察到响应时间增加100毫秒销售量就减少1%【20】而另一些报告说慢 1 秒钟会让客户满意度指标减少16%【2122】。
另一方面优化第99.99百分位点(一万个请求中最慢的一个)被认为太昂贵了,不能为亚马逊的目标带来足够好处。减小高百分位点处的响应时间相当困难,因为它很容易受到随机事件的影响,这超出了控制范围,而且效益也很小。
@ -242,7 +242,7 @@
>
> 如果你想将响应时间百分点添加到你的服务的监视仪表板则需要持续有效地计算它们。例如你可能希望在最近10分钟内保持请求响应时间的滚动窗口。每一分钟你都会计算出该窗口中的中值和各种百分数并将这些度量值绘制在图上。
>
> 简单的实现是在时间窗口内保存所有请求的响应时间列表并且每分钟对列表进行排序。如果对你来说效率太低那么有一些算法能够以最小的CPU和内存成本如前向衰减【25】t-digest【26】或HdrHistogram 【27】来计算百分位数的近似值。请注意平均百分比例如减少时间分辨率或合并来自多台机器的数据在数学上没有意义 - 聚合响应时间数据的正确方法是添加直方图【28】。
> 简单的实现是在时间窗口内保存所有请求的响应时间列表并且每分钟对列表进行排序。如果对你来说效率太低那么有一些算法能够以最小的CPU和内存成本如前向衰减【25】t-digest【26】或HdrHistogram 【27】来计算百分位数的近似值。请注意平均百分比例如减少时间分辨率或合并来自多台机器的数据在数学上没有意义 - 聚合响应时间数据的正确方法是添加直方图【28】。
![](img/fig1-5.png)
@ -289,7 +289,7 @@
* 可演化性evolvability
使工程师在未来能轻松地对系统进行更改,当需求变化时为新应用场景做适配。也称为**可伸缩性extensibility****可修改性modifiability** 或**可塑性plasticity**。
使工程师在未来能轻松地对系统进行更改,当需求变化时为新应用场景做适配。也称为**可伸缩性extensibility****可修改性modifiability** 或**可塑性plasticity**。
和之前提到的可靠性、可伸缩性一样,实现这些目标也没有简单的解决方案。不过我们会试着想象具有可操作性,简单性和可演化性的系统会是什么样子。

View File

@ -468,7 +468,7 @@ MapReduce作业的输出处理遵循同样的原理。通过将输入视为不
#### 存储多样性
数据库要求你根据特定的模型(例如关系或文档)来构造数据,而分布式文件系统中的文件只是字节序列,可以使用任何数据模型和编码来编写。它们可能是数据库记录的集合,但同样可以是文本,图像,视频,传感器读数,稀疏矩阵,特征向量,基因组序列或任何其他类型的数据。
数据库要求你根据特定的模型(例如关系或文档)来构造数据,而分布式文件系统中的文件只是字节序列,可以使用任何数据模型和编码来编写。它们可能是数据库记录的集合,但同样可以是文本、图像、视频、传感器读数、稀疏矩阵、特征向量、基因组序列或任何其他类型的数据。
说白了Hadoop开放了将数据不加区分地转储到HDFS的可能性允许后续再研究如何进一步处理【53】。相比之下在将数据导入数据库专有存储格式之前MPP数据库通常需要对数据和查询模式进行仔细的前期建模。
@ -510,7 +510,7 @@ MapReduce方式更适用于较大的作业要处理如此之多的数据并
这种架构允许非生产(低优先级)计算资源被**过量使用overcommitted**因为系统知道必要时它可以回收资源。与分离生产和非生产任务的系统相比过量使用资源可以更好地利用机器并提高效率。但由于MapReduce作业以低优先级运行它们随时都有被抢占的风险因为优先级较高的进程可能需要其资源。在高优先级进程拿走所需资源后批量作业能有效地“捡面包屑”利用剩下的任何计算资源。
在谷歌运行一个小时的MapReduce任务有大约有5的风险被终止为了给更高优先级的进程挪地方。这一概率比硬件问题、机器重启或其他原因的概率高了一个数量级【59】。按照这种抢占率如果一个作业有100个任务每个任务运行10分钟那么至少有一个任务在完成之前被终止的风险大于50
在谷歌运行一个小时的MapReduce任务有大约有5%的风险被终止为了给更高优先级的进程挪地方。这一概率比硬件问题、机器重启或其他原因的概率高了一个数量级【59】。按照这种抢占率如果一个作业有100个任务每个任务运行10分钟那么至少有一个任务在完成之前被终止的风险大于50%
这就是MapReduce被设计为容忍频繁意外任务终止的原因不是因为硬件很不可靠而是因为任意终止进程的自由有利于提高计算集群中的资源利用率。

View File

@ -32,9 +32,9 @@
例如为了处理任意关键词的搜索查询将OLTP数据库与全文搜索索引集成在一起是很常见的的需求。尽管一些数据库例如PostgreSQL包含了全文索引功能对于简单的应用完全够了【1】但更复杂的搜索能力就需要专业的信息检索工具了。相反的是搜索索引通常不适合作为持久的记录系统因此许多应用需要组合这两种不同的工具以满足所有需求。
我们在“[保持系统同步](ch11.md#保持系统同步)”中接触过集成数据系统的问题。随着数据不同表示形式的增加,集成问题变得越来越困难。除了数据库和搜索索引之外,也许你需要在分析系统(数据仓库,或批处理和流处理系统)中维护数据副本;维护从原始数据中衍生的缓存,或反规范化的数据版本;将数据灌入机器学习,分类,排名,或推荐系统中;或者基于数据变更发送通知。
我们在“[保持系统同步](ch11.md#保持系统同步)”中接触过集成数据系统的问题。随着数据不同表示形式的增加,集成问题变得越来越困难。除了数据库和搜索索引之外,也许你需要在分析系统(数据仓库,或批处理和流处理系统)中维护数据副本;维护从原始数据中衍生的缓存,或反规范化的数据版本;将数据灌入机器学习、分类、排名或推荐系统中;或者基于数据变更发送通知。
令人惊讶的是我经常看到软件工程师做出这样的陈述“根据我的经验99的人只需要X”或者 “......不需要X”对于各种各样的X。我认为这种陈述更像是发言人自己的经验而不是技术实际上的实用性。可能对数据执行的操作其范围极其宽广。某人认为鸡肋而毫无意义的功能可能是别人的核心需求。当你拉高视角并考虑跨越整个组织范围的数据流时数据集成的需求往往就会变得明显起来。
令人惊讶的是我经常看到软件工程师做出这样的陈述“根据我的经验99%的人只需要X”或者 “......不需要X”对于各种各样的X。我认为这种陈述更像是发言人自己的经验而不是技术实际上的实用性。可能对数据执行的操作其范围极其宽广。某人认为鸡肋而毫无意义的功能可能是别人的核心需求。当你拉高视角并考虑跨越整个组织范围的数据流时数据集成的需求往往就会变得明显起来。
#### 理解数据流
@ -507,7 +507,7 @@ COMMIT;
这实在是一个遗憾因为容错机制很难弄好。低层级的可靠机制比如TCP中的那些运行的相当好因而剩下的高层级错误基本很少出现。如果能将这些剩下的高层级容错机制打包成抽象而应用不需要再去操心那该多好呀 —— 但恐怕我们还没有找到这一正确的抽象。
长期以来,事务被认为是一个很好的抽象,我相信它们确实是很有用的。正如[第七章](ch7.md)导言中所讨论的,它们将各种可能的问题(并发写入,违背约束,崩溃,网络中断,磁盘故障)合并为两种可能结果:提交或中止。这是对编程模型而言是一种巨大的简化,但恐怕这还不够。
长期以来,事务被认为是一个很好的抽象,我相信它们确实是很有用的。正如[第七章](ch7.md)导言中所讨论的,它们将各种可能的问题(并发写入、违背约束、崩溃、网络中断、磁盘故障)合并为两种可能结果:提交或中止。这是对编程模型而言是一种巨大的简化,但恐怕这还不够。
事务是代价高昂的,当涉及异构存储技术时尤为甚(请参阅“[实践中的分布式事务](ch9.md#实践中的分布式事务)”)。我们拒绝使用分布式事务是因为它开销太大,结果我们最后不得不在应用代码中重新实现容错机制。正如本书中大量的例子所示,对并发性与部分失败的推理是困难且违反直觉的,所以我怀疑大多数应用级别的机制都不能正确工作,最终结果是数据丢失或损坏。
@ -742,7 +742,7 @@ ACID意义下的一致性请参阅“[一致性](ch7.md#一致性)”)基
当我们开发预测性分析系统时不是仅仅用软件通过一系列IF ELSE规则将人类的决策过程自动化那些规则本身甚至都是从数据中推断出来的。但这些系统学到的模式是个黑盒即使数据中存在一些相关性我们可能也压根不知道为什么。如果算法的输入中存在系统性的偏见则系统很有可能会在输出中学习并放大这种偏见【84】。
在许多国家,反歧视法律禁止按种族,年龄,性别,性取向,残疾,或信仰等受保护的特征区分对待不同的人。其他的个人特征可能是允许用于分析的但是如果这些特征与受保护的特征存在关联又会发生什么例如在种族隔离地区中一个人的邮政编码甚至是他们的IP地址都是很强的种族指示物。这样的话相信一种算法可以以某种方式将有偏见的数据作为输入并产生公平和公正的输出【85】似乎是很荒谬的。然而这种观点似乎常常潜伏在数据驱动型决策的支持者中这种态度被讽刺为“在处理偏差上机器学习与洗钱类似”machine learning is like money laundering for bias【86】。
在许多国家,反歧视法律禁止按种族、年龄、性别、性取向、残疾或信仰等受保护的特征区分对待不同的人。其他的个人特征可能是允许用于分析的但是如果这些特征与受保护的特征存在关联又会发生什么例如在种族隔离地区中一个人的邮政编码甚至是他们的IP地址都是很强的种族指示物。这样的话相信一种算法可以以某种方式将有偏见的数据作为输入并产生公平和公正的输出【85】似乎是很荒谬的。然而这种观点似乎常常潜伏在数据驱动型决策的支持者中这种态度被讽刺为“在处理偏差上机器学习与洗钱类似”machine learning is like money laundering for bias【86】。
预测性分析系统只是基于过去进行推断如果过去是歧视性的它们就会将这种歧视归纳为规律。如果我们希望未来比过去更好那么就需要道德想象力而这是只有人类才能提供的东西【87】。数据与模型应该是我们的工具而不是我们的主人。

14
ch2.md
View File

@ -15,10 +15,10 @@
多数应用使用层层叠加的数据模型构建。对于每层数据模型的关键问题是:它是如何用低一层数据模型来**表示**的?例如:
1. 作为一名应用开发人员,你观察现实世界(里面有人员,组织,货物,行为,资金流向,传感器等并采用对象或数据结构以及操控那些数据结构的API来进行建模。那些结构通常是特定于应用程序的。
2. 当要存储那些数据结构时你可以利用通用数据模型来表示它们如JSON或XML文档,关系数据库中的表、或图模型。
1. 作为一名应用开发人员,你观察现实世界(里面有人员、组织、货物、行为、资金流向、传感器等并采用对象或数据结构以及操控那些数据结构的API来进行建模。那些结构通常是特定于应用程序的。
2. 当要存储那些数据结构时你可以利用通用数据模型来表示它们如JSON或XML文档、关系数据库中的表或图模型。
3. 数据库软件的工程师选定如何以内存、磁盘或网络上的字节来表示JSON/XML/关系/图数据。这类表示形式使数据有可能以各种方式来查询,搜索,操纵和处理。
4. 在更低的层次上,硬件工程师已经想出了使用电流,光脉冲,磁场或者其他东西来表示字节的方法。
4. 在更低的层次上,硬件工程师已经想出了使用电流、光脉冲、磁场或者其他东西来表示字节的方法。
一个复杂的应用程序可能会有更多的中间层次比如基于API的API不过基本思想仍然是一样的每个层都通过提供一个明确的数据模型来隐藏更低层次中的复杂性。这些抽象允许不同的人群有效地协作例如数据库厂商的工程师和使用数据库的应用程序开发人员
@ -156,7 +156,7 @@ JSON表示比[图2-1](img/fig2-1.png)中的多表模式具有更好的**局部
* 组织和学校作为实体
在前面的描述中,`organization`(用户工作的公司)和`school_name`(他们学习的地方)只是字符串。也许他们应该是对实体的引用呢?然后,每个组织学校或大学都可以拥有自己的网页(标识,新闻提要等)。每个简历可以链接到它所提到的组织和学校,并且包括他们的图标和其他信息(请参阅[图2-3](img/fig2-3.png)来自LinkedIn的一个例子
在前面的描述中,`organization`(用户工作的公司)和`school_name`(他们学习的地方)只是字符串。也许他们应该是对实体的引用呢?然后,每个组织学校或大学都可以拥有自己的网页(标识,新闻提要等)。每个简历可以链接到它所提到的组织和学校,并且包括他们的图标和其他信息(请参阅[图2-3](img/fig2-3.png)来自LinkedIn的一个例子
* 推荐
@ -274,7 +274,7 @@ UPDATE users SET first_name = substring_index(name, ' ', 1); -- MySQL
#### 查询的数据局部性
文档通常以单个连续字符串形式进行存储编码为JSONXML或其二进制变体如MongoDB的BSON。如果应用程序经常需要访问整个文档例如将其渲染至网页那么存储局部性会带来性能优势。如果将数据分割到多个表中如[图2-1](img/fig2-1.png)所示),则需要进行多次索引查找才能将其全部检索出来,这可能需要更多的磁盘查找并花费更多的时间。
文档通常以单个连续字符串形式进行存储编码为JSONXML或其二进制变体如MongoDB的BSON。如果应用程序经常需要访问整个文档例如将其渲染至网页那么存储局部性会带来性能优势。如果将数据分割到多个表中如[图2-1](img/fig2-1.png)所示),则需要进行多次索引查找才能将其全部检索出来,这可能需要更多的磁盘查找并花费更多的时间。
局部性仅仅适用于同时需要文档绝大部分内容的情况。数据库通常需要加载整个文档即使只访问其中的一小部分这对于大型文档来说是很浪费的。更新文档时通常需要整个重写。只有不改变文档大小的修改才可以容易地原地执行。因此通常建议保持相对小的文档并避免增加文档大小的写入【9】。这些性能限制大大减少了文档数据库的实用场景。
@ -529,7 +529,7 @@ db.observations.aggregate([
可以将那些众所周知的算法运用到这些图上例如汽车导航系统搜索道路网络中两点之间的最短路径PageRank可以用在网络图上来确定网页的流行程度从而确定该网页在搜索结果中的排名。
在刚刚给出的例子中,图中的所有顶点代表了相同类型的事物(人网页或交叉路口。不过图并不局限于这样的同类数据同样强大地是图提供了一种一致的方式用来在单个数据存储中存储完全不同类型的对象。例如Facebook维护一个包含许多不同类型的顶点和边的单个图顶点表示人地点事件签到和用户的评论边缘表示哪些人是彼此的朋友哪个签到发生在何处谁评论了哪条消息谁参与了哪个事件等等【35】。
在刚刚给出的例子中,图中的所有顶点代表了相同类型的事物(人网页或交叉路口。不过图并不局限于这样的同类数据同样强大地是图提供了一种一致的方式用来在单个数据存储中存储完全不同类型的对象。例如Facebook维护一个包含许多不同类型的顶点和边的单个图顶点表示人地点事件签到和用户的评论边缘表示哪些人是彼此的朋友哪个签到发生在何处谁评论了哪条消息谁参与了哪个事件等等【35】。
在本节中,我们将使用[图2-5](img/fig2-5.png)所示的示例。它可以从社交网络或系谱数据库中获得它显示了两个人来自爱达荷州的Lucy和来自法国Beaune的Alain。他们已婚住在伦敦。
@ -640,7 +640,7 @@ RETURN person.name
答案是肯定的,但有些困难。在关系数据库中,你通常会事先知道在查询中需要哪些连接。在图查询中,你可能需要在找到待查找的顶点之前,遍历可变数量的边。也就是说,连接的数量事先并不确定。
在我们的例子中这发生在Cypher查询中的`() -[:WITHIN*0..]-> ()`规则中。一个人的`LIVES_IN`边可以指向任何类型的位置:街道,城市,地区,地区,国家等。城市可以在一个地区,在一个州内的一个地区,在一个国家内的一个州等等。`LIVES_IN`边可以直接指向正在查找的位置,或者一个在位置层次结构中隔了数层的位置。
在我们的例子中这发生在Cypher查询中的`() -[:WITHIN*0..]-> ()`规则中。一个人的`LIVES_IN`边可以指向任何类型的位置:街道、城市、地区、地区、国家等。城市可以在一个地区,在一个州内的一个地区,在一个国家内的一个州等等。`LIVES_IN`边可以直接指向正在查找的位置,或者一个在位置层次结构中隔了数层的位置。
在Cypher中用`WITHIN * 0`非常简洁地表述了上述事实:“沿着`WITHIN`边,零次或多次”。它很像正则表达式中的`*`运算符。

2
ch3.md
View File

@ -430,7 +430,7 @@ Teradata、Vertica、SAP HANA和ParAccel等数据仓库供应商通常使用昂
**图3-9 用于数据仓库的星型模式的示例**
通常情况下,事实被视为单独的事件,因为这样可以在以后分析中获得最大的灵活性。但是,这意味着事实表可以变得非常大。像苹果沃尔玛或eBay这样的大企业在其数据仓库中可能有几十PB的交易历史其中大部分保存在事实表中【56】。
通常情况下,事实被视为单独的事件,因为这样可以在以后分析中获得最大的灵活性。但是,这意味着事实表可以变得非常大。像苹果沃尔玛或eBay这样的大企业在其数据仓库中可能有几十PB的交易历史其中大部分保存在事实表中【56】。
事实表中的一些列是属性,例如产品销售的价格和从供应商那里购买的成本(可以用来计算利润余额)。事实表中的其他列是对其他表(称为维度表)的外键引用。由于事实表中的每一行都表示一个事件,因此这些维度代表事件发生的对象、内容、地点、时间、方式和原因。

4
ch4.md
View File

@ -290,9 +290,9 @@ Avro的关键思想是Writer模式和Reader模式不必是相同的 - 他们只
#### 代码生成和动态类型的语言
Thrift和Protobuf依赖于代码生成在定义了模式之后可以使用你选择的编程语言生成实现此模式的代码。这在JavaC ++或C等静态类型语言中很有用因为它允许将高效的内存中结构用于解码的数据并且在编写访问数据结构的程序时允许在IDE中进行类型检查和自动完成。
Thrift和Protobuf依赖于代码生成在定义了模式之后可以使用你选择的编程语言生成实现此模式的代码。这在Java、C++或C#等静态类型语言中很有用因为它允许将高效的内存中结构用于解码的数据并且在编写访问数据结构的程序时允许在IDE中进行类型检查和自动完成。
在动态类型编程语言如JavaScriptRuby或Python生成代码没有太多意义因为没有编译时类型检查器来满足。代码生成在这些语言中经常被忽视因为它们避免了显式的编译步骤。而且对于动态生成的模式例如从数据库表生成的Avro模式代码生成对获取数据是一个不必要的障碍。
在动态类型编程语言如JavaScriptRuby或Python生成代码没有太多意义因为没有编译时类型检查器来满足。代码生成在这些语言中经常被忽视因为它们避免了显式的编译步骤。而且对于动态生成的模式例如从数据库表生成的Avro模式代码生成对获取数据是一个不必要的障碍。
Avro为静态类型编程语言提供了可选的代码生成功能但是它也可以在不生成任何代码的情况下使用。如果你有一个对象容器文件它嵌入了Writer模式你可以简单地使用Avro库打开它并以与查看JSON文件相同的方式查看数据。该文件是自描述的因为它包含所有必要的元数据。

2
ch5.md
View File

@ -128,7 +128,7 @@
#### 基于语句的复制
在最简单的情况下,主库记录下它执行的每个写入请求(**语句**即statement并将该语句日志发送给其从库。对于关系数据库来说这意味着每个`INSERT``UPDATE`或`DELETE`语句都被转发给每个从库每个从库解析并执行该SQL语句就像从客户端收到一样。
在最简单的情况下,主库记录下它执行的每个写入请求(**语句**即statement并将该语句日志发送给其从库。对于关系数据库来说这意味着每个`INSERT``UPDATE`或`DELETE`语句都被转发给每个从库每个从库解析并执行该SQL语句就像从客户端收到一样。
虽然听上去很合理,但有很多问题会搞砸这种复制方式:

4
ch6.md
View File

@ -93,7 +93,7 @@
>
> 正如我们将在“[分区再平衡](#分区再平衡)”中所看到的,这种特殊的方法对于数据库实际上并不是很好,所以在实际中很少使用(某些数据库的文档仍然会使用一致性哈希的说法,但是它往往是不准确的)。 因为有可能产生混淆,所以最好避免使用一致性哈希这个术语,而只是把它称为**散列分区hash partitioning**。
不幸的是通过使用键散列进行分区我们失去了键范围分区的一个很好的属性高效执行范围查询的能力。曾经相邻的键现在分散在所有分区中所以它们之间的顺序就丢失了。在MongoDB中如果你使用了基于散列的分区模式则任何范围查询都必须发送到所有分区【4】。Riak 【9】Couchbase 【10】或Voldemort不支持主键上的范围查询。
不幸的是通过使用键散列进行分区我们失去了键范围分区的一个很好的属性高效执行范围查询的能力。曾经相邻的键现在分散在所有分区中所以它们之间的顺序就丢失了。在MongoDB中如果你使用了基于散列的分区模式则任何范围查询都必须发送到所有分区【4】。Riak【9】、Couchbase 【10】或Voldemort不支持主键上的范围查询。
Cassandra采取了折衷的策略【11, 12, 13】。 Cassandra中的表可以使用由多个列组成的复合主键来声明。键中只有第一列会作为散列的依据而其他列则被用作Casssandra的SSTables中排序数据的连接索引。尽管查询无法在复合主键的第一列中按范围扫表但如果第一列已经指定了固定值则可以对该键的其他列执行有效的范围扫描。
@ -190,7 +190,7 @@ Cassandra采取了折衷的策略【11, 12, 13】。 Cassandra中的表可以使
我们在前面说过([图6-3](img/fig6-3.png)),最好将可能的散列分成不同的范围,并将每个范围分配给一个分区(例如,如果$0≤hash(key)<b_0$则将键分配给分区0如果$b_0 hash(key) <b_1$则分配给分区1
也许你想知道为什么我们不使用 ***取模mod***(许多编程语言中的运算符)。例如,`hash(key) mod 10`会返回一个介于0和9之间的数字如果我们将散列写为十进制数散列模10将是最后一个数字。如果我们有10个节点编号为0到9这似乎是将每个键分配给一个节点的简单方法。
也许你想知道为什么我们不使用 ***取模mod***(许多编程语言中的%运算符)。例如,`hash(key) mod 10`会返回一个介于0和9之间的数字如果我们将散列写为十进制数散列模10将是最后一个数字。如果我们有10个节点编号为0到9这似乎是将每个键分配给一个节点的简单方法。
模N$mod N$方法的问题是如果节点数量N发生变化大多数键将需要从一个节点移动到另一个节点。例如假设$hash(key)=123456$。如果最初有10个节点那么这个键一开始放在节点6上因为$123456\ mod\ 10 = 6$。当你增长到11个节点时键需要移动到节点3$123456\ mod\ 11 = 3$当你增长到12个节点时需要移动到节点0$123456\ mod\ 12 = 0$)。这种频繁的举动使得重新平衡过于昂贵。

2
ch7.md
View File

@ -119,7 +119,7 @@ ACID意义上的隔离性意味着**同时执行的事务是相互隔离的**
> * 当电源突然断电时特别是固态硬盘有证据显示有时会违反应有的保证甚至fsync也不能保证正常工作【12】。硬盘固件可能有错误就像任何其他类型的软件一样【13,14】。
> * 存储引擎和文件系统之间的微妙交互可能会导致难以追踪的错误并可能导致磁盘上的文件在崩溃后被损坏【15,16】。
> * 磁盘上的数据可能会在没有检测到的情况下逐渐损坏【17】。如果数据已损坏一段时间副本和最近的备份也可能损坏。这种情况下需要尝试从历史备份中恢复数据。
> * 一项关于固态硬盘的研究发现在运行的前四年中30到80的硬盘会产生至少一个坏块【18】。相比固态硬盘磁盘的坏道率较低但完全失效的概率更高。
> * 一项关于固态硬盘的研究发现在运行的前四年中30%到80%的硬盘会产生至少一个坏块【18】。相比固态硬盘磁盘的坏道率较低但完全失效的概率更高。
> * 如果SSD断电可能会在几周内开始丢失数据具体取决于温度【19】。
>
> 在实践中,没有一种技术可以提供绝对保证。只有各种降低风险的技术,包括写入磁盘,复制到远程机器和备份——它们可以且应该一起使用。与往常一样,最好抱着怀疑的态度接受任何理论上的“保证”。

6
ch8.md
View File

@ -82,7 +82,7 @@
> 你可能想知道这是否有意义——直观地看来系统只能像其最不可靠的组件最薄弱的环节一样可靠。事实并非如此事实上从不太可靠的潜在基础构建更可靠的系统是计算机领域的一个古老思想【11】。例如
>
> * 纠错码允许数字数据在通信信道上准确传输偶尔会出现一些错误例如由于无线网络上的无线电干扰【12】。
> * **互联网协议Internet Protocol, IP** 不可靠:可能丢弃,延迟,重复或重排数据包。 传输控制协议Transmission Control Protocol, TCP在互联网协议IP之上提供了更可靠的传输层它确保丢失的数据包被重新传输消除重复并且数据包被重新组装成它们被发送的顺序。
> * **互联网协议Internet Protocol, IP** 不可靠:可能丢弃、延迟、重复或重排数据包。 传输控制协议Transmission Control Protocol, TCP在互联网协议IP之上提供了更可靠的传输层它确保丢失的数据包被重新传输消除重复并且数据包被重新组装成它们被发送的顺序。
>
> 虽然这个系统可以比它的底层部分更可靠但它的可靠性总是有限的。例如纠错码可以处理少量的单比特错误但是如果你的信号被干扰所淹没那么通过信道可以得到多少数据是有根本性的限制的【13】。 TCP可以隐藏数据包的丢失重复和重新排序但是它不能神奇地消除网络中的延迟。
>
@ -278,7 +278,7 @@
在具有多个CPU插槽的服务器上每个CPU可能有一个单独的计时器但不一定与其他CPU同步。操作系统会补偿所有的差异并尝试向应用线程表现出单调钟的样子即使这些线程被调度到不同的CPU上。当然明智的做法是不要太把这种单调性保证当回事【40】。
如果NTP协议检测到计算机的本地石英钟比NTP服务器要更快或更慢则可以调整单调钟向前走的频率这称为**偏移skewing** 时钟。默认情况下NTP允许时钟速率增加或减慢最高至0.05但NTP不能使单调时钟向前或向后跳转。单调时钟的分辨率通常相当好在大多数系统中它们能在几微秒或更短的时间内测量时间间隔。
如果NTP协议检测到计算机的本地石英钟比NTP服务器要更快或更慢则可以调整单调钟向前走的频率这称为**偏移skewing** 时钟。默认情况下NTP允许时钟速率增加或减慢最高至0.05%但NTP不能使单调时钟向前或向后跳转。单调时钟的分辨率通常相当好在大多数系统中它们能在几微秒或更短的时间内测量时间间隔。
在分布式系统中,使用单调钟测量**经过时间**elapsed time比如超时通常很好因为它不假定不同节点的时钟之间存在任何同步并且对测量的轻微不准确性不敏感。
@ -339,7 +339,7 @@ NTP同步是否能足够准确以至于这种不正确的排序不会发生
你可能能够以微秒或甚至纳秒的精度读取机器的时钟。但即使可以得到如此细致的测量结果这并不意味着这个值对于这样的精度实际上是准确的。实际上大概率是不准确的——如前所述即使你每分钟与本地网络上的NTP服务器进行同步几毫秒的时间漂移也很容易在不精确的石英时钟上发生。使用公共互联网上的NTP服务器最好的准确度可能达到几十毫秒而且当网络拥塞时误差可能会超过100毫秒【57】。
因此将时钟读数视为一个时间点是没有意义的——它更像是一段时间范围例如一个系统可能以95的置信度认为当前时间处于本分钟内的第10.3秒和10.5秒之间它可能没法比这更精确了【58】。如果我们只知道±100毫秒的时间那么时间戳中的微秒数字部分基本上是没有意义的。
因此将时钟读数视为一个时间点是没有意义的——它更像是一段时间范围例如一个系统可能以95%的置信度认为当前时间处于本分钟内的第10.3秒和10.5秒之间它可能没法比这更精确了【58】。如果我们只知道±100毫秒的时间那么时间戳中的微秒数字部分基本上是没有意义的。
不确定性界限可以根据你的时间源来计算。如果你的GPS接收器或原子时钟直接连接到你的计算机上预期的错误范围由制造商告知。如果从服务器获得时间则不确定性取决于自上次与服务器同步以来的石英钟漂移的期望值加上NTP服务器的不确定性再加上到服务器的网络往返时间只是获取粗略近似值并假设服务器是可信的

View File

@ -169,7 +169,7 @@
### 百分位点percentile
通过计算有多少值高于或低于某个阈值来衡量值分布的方法。 例如某个时间段的第95个百分位响应时间是时间t则该时间段中95的请求完成时间小于t5的请求完成时间要比t长。 请参阅“[描述性能](ch1.md#描述性能)”。
通过计算有多少值高于或低于某个阈值来衡量值分布的方法。 例如某个时间段的第95个百分位响应时间是时间t则该时间段中95%的请求完成时间小于t5%的请求完成时间要比t长。 请参阅“[描述性能](ch1.md#描述性能)”。
### 主键primary key

View File

@ -120,7 +120,7 @@
### 人為錯誤
設計並構建了軟體系統的工程師是人類維持系統執行的運維也是人類。即使他們懷有最大的善意人類也是不可靠的。舉個例子一項關於大型網際網路服務的研究發現運維配置錯誤是導致服務中斷的首要原因而硬體故障伺服器或網路僅導致了10-25的服務中斷【13】。
設計並構建了軟體系統的工程師是人類維持系統執行的運維也是人類。即使他們懷有最大的善意人類也是不可靠的。舉個例子一項關於大型網際網路服務的研究發現運維配置錯誤是導致服務中斷的首要原因而硬體故障伺服器或網路僅導致了10-25%的服務中斷【13】。
儘管人類不可靠,但怎麼做才能讓系統變得可靠?最好的系統會組合使用以下幾種辦法:
@ -160,7 +160,7 @@
使用者可以查閱他們關注的人釋出的推文300k請求/秒)。
處理每秒12,000次寫入發推文的速率峰值還是很簡單的。然而推特的伸縮性挑戰並不是主要來自推特量而是來自**扇出fan-out**——每個使用者關注了很多人,也被很多人關注。
處理每秒12,000次寫入發推文的速率峰值還是很簡單的。然而推特的伸縮性挑戰並不是主要來自推特量而是來自**扇出fan-out**[^ii]——每個使用者關注了很多人,也被很多人關注。
[^ii]: 扇出:從電子工程學中借用的術語,它描述了輸入連線到另一個門輸出的邏輯閘數量。 輸出需要提供足夠的電流來驅動所有連線的輸入。 在事務處理系統中,我們使用它來描述為了服務一個傳入請求而需要執行其他服務的請求數量。
@ -222,11 +222,11 @@
通常使用**百分位點percentiles** 會更好。如果將響應時間列表按最快到最慢排序,那麼**中位數median** 就在正中間舉個例子如果你的響應時間中位數是200毫秒這意味著一半請求的返回時間少於200毫秒另一半比這個要長。
如果想知道典型場景下使用者需要等待多長時間那麼中位數是一個好的度量標準一半使用者請求的響應時間少於響應時間的中位數另一半服務時間比中位數長。中位數也被稱為第50百分位點有時縮寫為p50。注意中位數是關於單個請求的如果使用者同時發出幾個請求在一個會話過程中或者由於一個頁面中包含了多個資源則至少一個請求比中位數慢的概率遠大於50
如果想知道典型場景下使用者需要等待多長時間那麼中位數是一個好的度量標準一半使用者請求的響應時間少於響應時間的中位數另一半服務時間比中位數長。中位數也被稱為第50百分位點有時縮寫為p50。注意中位數是關於單個請求的如果使用者同時發出幾個請求在一個會話過程中或者由於一個頁面中包含了多個資源則至少一個請求比中位數慢的概率遠大於50%
為了弄清異常值有多糟糕可以看看更高的百分位點例如第95、99和99.9百分位點縮寫為p95p99和p999。它們意味著9599或99.9的請求響應時間要比該閾值快例如如果第95百分位點響應時間是1.5秒則意味著100個請求中的95個響應時間快於1.5秒而100個請求中的5個響應時間超過1.5秒。如[圖1-4](../img/fig1-4.png)所示。
為了弄清異常值有多糟糕可以看看更高的百分位點例如第95、99和99.9百分位點縮寫為p95p99和p999。它們意味著95%、99%或99.9%的請求響應時間要比該閾值快例如如果第95百分位點響應時間是1.5秒則意味著100個請求中的95個響應時間快於1.5秒而100個請求中的5個響應時間超過1.5秒。如[圖1-4](../img/fig1-4.png)所示。
響應時間的高百分位點(也稱為**尾部延遲**,即**tail latencies**非常重要因為它們直接影響使用者的服務體驗。例如亞馬遜在描述內部服務的響應時間要求時以99.9百分位點為準,即使它隻影響一千個請求中的一個。這是因為請求響應最慢的客戶往往也是資料最多的客戶,也可以說是最有價值的客戶 —— 因為他們掏錢了【19】。保證網站響應迅速對於保持客戶的滿意度非常重要亞馬遜觀察到響應時間增加100毫秒銷售量就減少1【20】而另一些報告說慢 1 秒鐘會讓客戶滿意度指標減少16%【2122】。
響應時間的高百分位點(也稱為**尾部延遲**,即**tail latencies**非常重要因為它們直接影響使用者的服務體驗。例如亞馬遜在描述內部服務的響應時間要求時以99.9百分位點為準,即使它隻影響一千個請求中的一個。這是因為請求響應最慢的客戶往往也是資料最多的客戶,也可以說是最有價值的客戶 —— 因為他們掏錢了【19】。保證網站響應迅速對於保持客戶的滿意度非常重要亞馬遜觀察到響應時間增加100毫秒銷售量就減少1%【20】而另一些報告說慢 1 秒鐘會讓客戶滿意度指標減少16%【2122】。
另一方面最佳化第99.99百分位點(一萬個請求中最慢的一個)被認為太昂貴了,不能為亞馬遜的目標帶來足夠好處。減小高百分位點處的響應時間相當困難,因為它很容易受到隨機事件的影響,這超出了控制範圍,而且效益也很小。
@ -242,7 +242,7 @@
>
> 如果你想將響應時間百分點新增到你的服務的監視儀表板則需要持續有效地計算它們。例如你可能希望在最近10分鐘內保持請求響應時間的滾動視窗。每一分鐘你都會計算出該視窗中的中值和各種百分數並將這些度量值繪製在圖上。
>
> 簡單的實現是在時間視窗內儲存所有請求的響應時間列表並且每分鐘對列表進行排序。如果對你來說效率太低那麼有一些演算法能夠以最小的CPU和記憶體成本如前向衰減【25】t-digest【26】或HdrHistogram 【27】來計算百分位數的近似值。請注意平均百分比例如減少時間解析度或合併來自多臺機器的資料在數學上沒有意義 - 聚合響應時間資料的正確方法是新增直方圖【28】。
> 簡單的實現是在時間視窗內儲存所有請求的響應時間列表並且每分鐘對列表進行排序。如果對你來說效率太低那麼有一些演算法能夠以最小的CPU和記憶體成本如前向衰減【25】t-digest【26】或HdrHistogram 【27】來計算百分位數的近似值。請注意平均百分比例如減少時間解析度或合併來自多臺機器的資料在數學上沒有意義 - 聚合響應時間資料的正確方法是新增直方圖【28】。
![](../img/fig1-5.png)
@ -289,7 +289,7 @@
* 可演化性evolvability
使工程師在未來能輕鬆地對系統進行更改,當需求變化時為新應用場景做適配。也稱為**可伸縮性extensibility****可修改性modifiability** 或**可塑性plasticity**。
使工程師在未來能輕鬆地對系統進行更改,當需求變化時為新應用場景做適配。也稱為**可伸縮性extensibility****可修改性modifiability** 或**可塑性plasticity**。
和之前提到的可靠性、可伸縮性一樣,實現這些目標也沒有簡單的解決方案。不過我們會試著想象具有可操作性,簡單性和可演化性的系統會是什麼樣子。
@ -354,7 +354,7 @@
本章探討了一些關於資料密集型應用的基本思考方式。這些原則將指導我們閱讀本書的其餘部分,那裡將會深入技術細節。
一個應用必須滿足各種需求才稱得上有用。有一些**功能需求**functional requirements即它應該做什麼比如允許以各種方式儲存檢索搜尋和處理資料以及一些**非功能性需求*nonfunctional即通用屬性例如安全性、可靠性、合規性、可伸縮性、相容性和可維護性。在本章詳細討論了可靠性可伸縮性和可維護性。
一個應用必須滿足各種需求才稱得上有用。有一些**功能需求**functional requirements即它應該做什麼比如允許以各種方式儲存檢索搜尋和處理資料以及一些**非功能性需求**nonfunctional即通用屬性例如安全性、可靠性、合規性、可伸縮性、相容性和可維護性。在本章詳細討論了可靠性可伸縮性和可維護性。
**可靠性Reliability** 意味著即使發生故障系統也能正常工作。故障可能發生在硬體通常是隨機的和不相關的、軟體通常是系統性的Bug很難處理和人類不可避免地時不時出錯**容錯技術** 可以對終端使用者隱藏某些型別的故障。

View File

@ -468,7 +468,7 @@ MapReduce作業的輸出處理遵循同樣的原理。透過將輸入視為不
#### 儲存多樣性
資料庫要求你根據特定的模型(例如關係或文件)來構造資料,而分散式檔案系統中的檔案只是位元組序列,可以使用任何資料模型和編碼來編寫。它們可能是資料庫記錄的集合,但同樣可以是文字,影象,影片,感測器讀數,稀疏矩陣,特徵向量,基因組序列或任何其他型別的資料。
資料庫要求你根據特定的模型(例如關係或文件)來構造資料,而分散式檔案系統中的檔案只是位元組序列,可以使用任何資料模型和編碼來編寫。它們可能是資料庫記錄的集合,但同樣可以是文字、影象、影片、感測器讀數、稀疏矩陣、特徵向量、基因組序列或任何其他型別的資料。
說白了Hadoop開放了將資料不加區分地轉儲到HDFS的可能性允許後續再研究如何進一步處理【53】。相比之下在將資料匯入資料庫專有儲存格式之前MPP資料庫通常需要對資料和查詢模式進行仔細的前期建模。
@ -510,7 +510,7 @@ MapReduce方式更適用於較大的作業要處理如此之多的資料並
這種架構允許非生產(低優先順序)計算資源被**過量使用overcommitted**因為系統知道必要時它可以回收資源。與分離生產和非生產任務的系統相比過量使用資源可以更好地利用機器並提高效率。但由於MapReduce作業以低優先順序執行它們隨時都有被搶佔的風險因為優先順序較高的程序可能需要其資源。在高優先順序程序拿走所需資源後批次作業能有效地“撿麵包屑”利用剩下的任何計算資源。
在谷歌執行一個小時的MapReduce任務有大約有5的風險被終止為了給更高優先順序的程序挪地方。這一概率比硬體問題、機器重啟或其他原因的概率高了一個數量級【59】。按照這種搶佔率如果一個作業有100個任務每個任務執行10分鐘那麼至少有一個任務在完成之前被終止的風險大於50
在谷歌執行一個小時的MapReduce任務有大約有5%的風險被終止為了給更高優先順序的程序挪地方。這一概率比硬體問題、機器重啟或其他原因的概率高了一個數量級【59】。按照這種搶佔率如果一個作業有100個任務每個任務執行10分鐘那麼至少有一個任務在完成之前被終止的風險大於50%
這就是MapReduce被設計為容忍頻繁意外任務終止的原因不是因為硬體很不可靠而是因為任意終止程序的自由有利於提高計算叢集中的資源利用率。

View File

@ -32,9 +32,9 @@
例如為了處理任意關鍵詞的搜尋查詢將OLTP資料庫與全文搜尋索引整合在一起是很常見的的需求。儘管一些資料庫例如PostgreSQL包含了全文索引功能對於簡單的應用完全夠了【1】但更復雜的搜尋能力就需要專業的資訊檢索工具了。相反的是搜尋索引通常不適合作為持久的記錄系統因此許多應用需要組合這兩種不同的工具以滿足所有需求。
我們在“[保持系統同步](ch11.md#保持系統同步)”中接觸過整合資料系統的問題。隨著資料不同表示形式的增加,整合問題變得越來越困難。除了資料庫和搜尋索引之外,也許你需要在分析系統(資料倉庫,或批處理和流處理系統)中維護資料副本;維護從原始資料中衍生的快取,或反規範化的資料版本;將資料灌入機器學習,分類,排名,或推薦系統中;或者基於資料變更傳送通知。
我們在“[保持系統同步](ch11.md#保持系統同步)”中接觸過整合資料系統的問題。隨著資料不同表示形式的增加,整合問題變得越來越困難。除了資料庫和搜尋索引之外,也許你需要在分析系統(資料倉庫,或批處理和流處理系統)中維護資料副本;維護從原始資料中衍生的快取,或反規範化的資料版本;將資料灌入機器學習、分類、排名或推薦系統中;或者基於資料變更傳送通知。
令人驚訝的是我經常看到軟體工程師做出這樣的陳述“根據我的經驗99的人只需要X”或者 “......不需要X”對於各種各樣的X。我認為這種陳述更像是發言人自己的經驗而不是技術實際上的實用性。可能對資料執行的操作其範圍極其寬廣。某人認為雞肋而毫無意義的功能可能是別人的核心需求。當你拉高視角並考慮跨越整個組織範圍的資料流時資料整合的需求往往就會變得明顯起來。
令人驚訝的是我經常看到軟體工程師做出這樣的陳述“根據我的經驗99%的人只需要X”或者 “......不需要X”對於各種各樣的X。我認為這種陳述更像是發言人自己的經驗而不是技術實際上的實用性。可能對資料執行的操作其範圍極其寬廣。某人認為雞肋而毫無意義的功能可能是別人的核心需求。當你拉高視角並考慮跨越整個組織範圍的資料流時資料整合的需求往往就會變得明顯起來。
#### 理解資料流
@ -507,7 +507,7 @@ COMMIT;
這實在是一個遺憾因為容錯機制很難弄好。低層級的可靠機制比如TCP中的那些執行的相當好因而剩下的高層級錯誤基本很少出現。如果能將這些剩下的高層級容錯機制打包成抽象而應用不需要再去操心那該多好呀 —— 但恐怕我們還沒有找到這一正確的抽象。
長期以來,事務被認為是一個很好的抽象,我相信它們確實是很有用的。正如[第七章](ch7.md)導言中所討論的,它們將各種可能的問題(併發寫入,違背約束,崩潰,網路中斷,磁碟故障)合併為兩種可能結果:提交或中止。這是對程式設計模型而言是一種巨大的簡化,但恐怕這還不夠。
長期以來,事務被認為是一個很好的抽象,我相信它們確實是很有用的。正如[第七章](ch7.md)導言中所討論的,它們將各種可能的問題(併發寫入、違背約束、崩潰、網路中斷、磁碟故障)合併為兩種可能結果:提交或中止。這是對程式設計模型而言是一種巨大的簡化,但恐怕這還不夠。
事務是代價高昂的,當涉及異構儲存技術時尤為甚(請參閱“[實踐中的分散式事務](ch9.md#實踐中的分散式事務)”)。我們拒絕使用分散式事務是因為它開銷太大,結果我們最後不得不在應用程式碼中重新實現容錯機制。正如本書中大量的例子所示,對併發性與部分失敗的推理是困難且違反直覺的,所以我懷疑大多數應用級別的機制都不能正確工作,最終結果是資料丟失或損壞。
@ -742,7 +742,7 @@ ACID意義下的一致性請參閱“[一致性](ch7.md#一致性)”)基
當我們開發預測性分析系統時不是僅僅用軟體透過一系列IF ELSE規則將人類的決策過程自動化那些規則本身甚至都是從資料中推斷出來的。但這些系統學到的模式是個黑盒即使資料中存在一些相關性我們可能也壓根不知道為什麼。如果演算法的輸入中存在系統性的偏見則系統很有可能會在輸出中學習並放大這種偏見【84】。
在許多國家,反歧視法律禁止按種族,年齡,性別,性取向,殘疾,或信仰等受保護的特徵區分對待不同的人。其他的個人特徵可能是允許用於分析的但是如果這些特徵與受保護的特徵存在關聯又會發生什麼例如在種族隔離地區中一個人的郵政編碼甚至是他們的IP地址都是很強的種族指示物。這樣的話相信一種演算法可以以某種方式將有偏見的資料作為輸入併產生公平和公正的輸出【85】似乎是很荒謬的。然而這種觀點似乎常常潛伏在資料驅動型決策的支持者中這種態度被諷刺為“在處理偏差上機器學習與洗錢類似”machine learning is like money laundering for bias【86】。
在許多國家,反歧視法律禁止按種族、年齡、性別、性取向、殘疾或信仰等受保護的特徵區分對待不同的人。其他的個人特徵可能是允許用於分析的但是如果這些特徵與受保護的特徵存在關聯又會發生什麼例如在種族隔離地區中一個人的郵政編碼甚至是他們的IP地址都是很強的種族指示物。這樣的話相信一種演算法可以以某種方式將有偏見的資料作為輸入併產生公平和公正的輸出【85】似乎是很荒謬的。然而這種觀點似乎常常潛伏在資料驅動型決策的支持者中這種態度被諷刺為“在處理偏差上機器學習與洗錢類似”machine learning is like money laundering for bias【86】。
預測性分析系統只是基於過去進行推斷如果過去是歧視性的它們就會將這種歧視歸納為規律。如果我們希望未來比過去更好那麼就需要道德想象力而這是隻有人類才能提供的東西【87】。資料與模型應該是我們的工具而不是我們的主人。

View File

@ -15,10 +15,10 @@
多數應用使用層層疊加的資料模型構建。對於每層資料模型的關鍵問題是:它是如何用低一層資料模型來**表示**的?例如:
1. 作為一名應用開發人員,你觀察現實世界(裡面有人員,組織,貨物,行為,資金流向,感測器等並採用物件或資料結構以及操控那些資料結構的API來進行建模。那些結構通常是特定於應用程式的。
2. 當要儲存那些資料結構時你可以利用通用資料模型來表示它們如JSON或XML文件,關係資料庫中的表、或圖模型。
1. 作為一名應用開發人員,你觀察現實世界(裡面有人員、組織、貨物、行為、資金流向、感測器等並採用物件或資料結構以及操控那些資料結構的API來進行建模。那些結構通常是特定於應用程式的。
2. 當要儲存那些資料結構時你可以利用通用資料模型來表示它們如JSON或XML文件、關係資料庫中的表或圖模型。
3. 資料庫軟體的工程師選定如何以記憶體、磁碟或網路上的位元組來表示JSON/XML/關係/圖資料。這類表示形式使資料有可能以各種方式來查詢,搜尋,操縱和處理。
4. 在更低的層次上,硬體工程師已經想出了使用電流,光脈衝,磁場或者其他東西來表示位元組的方法。
4. 在更低的層次上,硬體工程師已經想出了使用電流、光脈衝、磁場或者其他東西來表示位元組的方法。
一個複雜的應用程式可能會有更多的中間層次比如基於API的API不過基本思想仍然是一樣的每個層都透過提供一個明確的資料模型來隱藏更低層次中的複雜性。這些抽象允許不同的人群有效地協作例如資料庫廠商的工程師和使用資料庫的應用程式開發人員
@ -156,7 +156,7 @@ JSON表示比[圖2-1](../img/fig2-1.png)中的多表模式具有更好的**區
* 組織和學校作為實體
在前面的描述中,`organization`(使用者工作的公司)和`school_name`(他們學習的地方)只是字串。也許他們應該是對實體的引用呢?然後,每個組織學校或大學都可以擁有自己的網頁(標識,新聞提要等)。每個簡歷可以連結到它所提到的組織和學校,並且包括他們的圖示和其他資訊(請參閱[圖2-3](../img/fig2-3.png)來自LinkedIn的一個例子
在前面的描述中,`organization`(使用者工作的公司)和`school_name`(他們學習的地方)只是字串。也許他們應該是對實體的引用呢?然後,每個組織學校或大學都可以擁有自己的網頁(標識,新聞提要等)。每個簡歷可以連結到它所提到的組織和學校,並且包括他們的圖示和其他資訊(請參閱[圖2-3](../img/fig2-3.png)來自LinkedIn的一個例子
* 推薦
@ -274,7 +274,7 @@ UPDATE users SET first_name = substring_index(name, ' ', 1); -- MySQL
#### 查詢的資料區域性性
文件通常以單個連續字串形式進行儲存編碼為JSONXML或其二進位制變體如MongoDB的BSON。如果應用程式經常需要訪問整個文件例如將其渲染至網頁那麼儲存區域性性會帶來效能優勢。如果將資料分割到多個表中如[圖2-1](../img/fig2-1.png)所示),則需要進行多次索引查詢才能將其全部檢索出來,這可能需要更多的磁碟查詢並花費更多的時間。
文件通常以單個連續字串形式進行儲存編碼為JSONXML或其二進位制變體如MongoDB的BSON。如果應用程式經常需要訪問整個文件例如將其渲染至網頁那麼儲存區域性性會帶來效能優勢。如果將資料分割到多個表中如[圖2-1](../img/fig2-1.png)所示),則需要進行多次索引查詢才能將其全部檢索出來,這可能需要更多的磁碟查詢並花費更多的時間。
區域性性僅僅適用於同時需要文件絕大部分內容的情況。資料庫通常需要載入整個文件即使只訪問其中的一小部分這對於大型文件來說是很浪費的。更新文件時通常需要整個重寫。只有不改變文件大小的修改才可以容易地原地執行。因此通常建議保持相對小的文件並避免增加文件大小的寫入【9】。這些效能限制大大減少了文件資料庫的實用場景。
@ -529,7 +529,7 @@ db.observations.aggregate([
可以將那些眾所周知的演算法運用到這些圖上例如汽車導航系統搜尋道路網路中兩點之間的最短路徑PageRank可以用在網路圖上來確定網頁的流行程度從而確定該網頁在搜尋結果中的排名。
在剛剛給出的例子中,圖中的所有頂點代表了相同型別的事物(人網頁或交叉路口。不過圖並不侷限於這樣的同類資料同樣強大地是圖提供了一種一致的方式用來在單個數據儲存中儲存完全不同型別的物件。例如Facebook維護一個包含許多不同型別的頂點和邊的單個圖頂點表示人地點事件簽到和使用者的評論邊緣表示哪些人是彼此的朋友哪個簽到發生在何處誰評論了哪條訊息誰參與了哪個事件等等【35】。
在剛剛給出的例子中,圖中的所有頂點代表了相同型別的事物(人網頁或交叉路口。不過圖並不侷限於這樣的同類資料同樣強大地是圖提供了一種一致的方式用來在單個數據儲存中儲存完全不同型別的物件。例如Facebook維護一個包含許多不同型別的頂點和邊的單個圖頂點表示人地點事件簽到和使用者的評論邊緣表示哪些人是彼此的朋友哪個簽到發生在何處誰評論了哪條訊息誰參與了哪個事件等等【35】。
在本節中,我們將使用[圖2-5](../img/fig2-5.png)所示的示例。它可以從社交網路或系譜資料庫中獲得它顯示了兩個人來自愛達荷州的Lucy和來自法國Beaune的Alain。他們已婚住在倫敦。
@ -640,7 +640,7 @@ RETURN person.name
答案是肯定的,但有些困難。在關係資料庫中,你通常會事先知道在查詢中需要哪些連線。在圖查詢中,你可能需要在找到待查詢的頂點之前,遍歷可變數量的邊。也就是說,連線的數量事先並不確定。
在我們的例子中這發生在Cypher查詢中的`() -[:WITHIN*0..]-> ()`規則中。一個人的`LIVES_IN`邊可以指向任何型別的位置:街道,城市,地區,地區,國家等。城市可以在一個地區,在一個州內的一個地區,在一個國家內的一個州等等。`LIVES_IN`邊可以直接指向正在查詢的位置,或者一個在位置層次結構中隔了數層的位置。
在我們的例子中這發生在Cypher查詢中的`() -[:WITHIN*0..]-> ()`規則中。一個人的`LIVES_IN`邊可以指向任何型別的位置:街道、城市、地區、地區、國家等。城市可以在一個地區,在一個州內的一個地區,在一個國家內的一個州等等。`LIVES_IN`邊可以直接指向正在查詢的位置,或者一個在位置層次結構中隔了數層的位置。
在Cypher中用`WITHIN * 0`非常簡潔地表述了上述事實:“沿著`WITHIN`邊,零次或多次”。它很像正則表示式中的`*`運算子。

View File

@ -430,7 +430,7 @@ Teradata、Vertica、SAP HANA和ParAccel等資料倉庫供應商通常使用昂
**圖3-9 用於資料倉庫的星型模式的示例**
通常情況下,事實被視為單獨的事件,因為這樣可以在以後分析中獲得最大的靈活性。但是,這意味著事實表可以變得非常大。像蘋果沃爾瑪或eBay這樣的大企業在其資料倉庫中可能有幾十PB的交易歷史其中大部分儲存在事實表中【56】。
通常情況下,事實被視為單獨的事件,因為這樣可以在以後分析中獲得最大的靈活性。但是,這意味著事實表可以變得非常大。像蘋果沃爾瑪或eBay這樣的大企業在其資料倉庫中可能有幾十PB的交易歷史其中大部分儲存在事實表中【56】。
事實表中的一些列是屬性,例如產品銷售的價格和從供應商那裡購買的成本(可以用來計算利潤餘額)。事實表中的其他列是對其他表(稱為維度表)的外來鍵引用。由於事實表中的每一行都表示一個事件,因此這些維度代表事件發生的物件、內容、地點、時間、方式和原因。

View File

@ -290,9 +290,9 @@ Avro的關鍵思想是Writer模式和Reader模式不必是相同的 - 他們只
#### 程式碼生成和動態型別的語言
Thrift和Protobuf依賴於程式碼生成在定義了模式之後可以使用你選擇的程式語言生成實現此模式的程式碼。這在JavaC ++或C等靜態型別語言中很有用因為它允許將高效的記憶體中結構用於解碼的資料並且在編寫訪問資料結構的程式時允許在IDE中進行型別檢查和自動完成。
Thrift和Protobuf依賴於程式碼生成在定義了模式之後可以使用你選擇的程式語言生成實現此模式的程式碼。這在Java、C++或C#等靜態型別語言中很有用因為它允許將高效的記憶體中結構用於解碼的資料並且在編寫訪問資料結構的程式時允許在IDE中進行型別檢查和自動完成。
在動態型別程式語言如JavaScriptRuby或Python生成程式碼沒有太多意義因為沒有編譯時型別檢查器來滿足。程式碼生成在這些語言中經常被忽視因為它們避免了顯式的編譯步驟。而且對於動態生成的模式例如從資料庫表生成的Avro模式程式碼生成對獲取資料是一個不必要的障礙。
在動態型別程式語言如JavaScriptRuby或Python生成程式碼沒有太多意義因為沒有編譯時型別檢查器來滿足。程式碼生成在這些語言中經常被忽視因為它們避免了顯式的編譯步驟。而且對於動態生成的模式例如從資料庫表生成的Avro模式程式碼生成對獲取資料是一個不必要的障礙。
Avro為靜態型別程式語言提供了可選的程式碼生成功能但是它也可以在不生成任何程式碼的情況下使用。如果你有一個物件容器檔案它嵌入了Writer模式你可以簡單地使用Avro庫開啟它並以與檢視JSON檔案相同的方式檢視資料。該檔案是自描述的因為它包含所有必要的元資料。

View File

@ -128,7 +128,7 @@
#### 基於語句的複製
在最簡單的情況下,主庫記錄下它執行的每個寫入請求(**語句**即statement並將該語句日誌傳送給其從庫。對於關係資料庫來說這意味著每個`INSERT``UPDATE`或`DELETE`語句都被轉發給每個從庫每個從庫解析並執行該SQL語句就像從客戶端收到一樣。
在最簡單的情況下,主庫記錄下它執行的每個寫入請求(**語句**即statement並將該語句日誌傳送給其從庫。對於關係資料庫來說這意味著每個`INSERT``UPDATE`或`DELETE`語句都被轉發給每個從庫每個從庫解析並執行該SQL語句就像從客戶端收到一樣。
雖然聽上去很合理,但有很多問題會搞砸這種複製方式:

View File

@ -93,7 +93,7 @@
>
> 正如我們將在“[分割槽再平衡](#分割槽再平衡)”中所看到的,這種特殊的方法對於資料庫實際上並不是很好,所以在實際中很少使用(某些資料庫的文件仍然會使用一致性雜湊的說法,但是它往往是不準確的)。 因為有可能產生混淆,所以最好避免使用一致性雜湊這個術語,而只是把它稱為**雜湊分割槽hash partitioning**。
不幸的是透過使用鍵雜湊進行分割槽我們失去了鍵範圍分割槽的一個很好的屬性高效執行範圍查詢的能力。曾經相鄰的鍵現在分散在所有分割槽中所以它們之間的順序就丟失了。在MongoDB中如果你使用了基於雜湊的分割槽模式則任何範圍查詢都必須傳送到所有分割槽【4】。Riak 【9】Couchbase 【10】或Voldemort不支援主鍵上的範圍查詢。
不幸的是透過使用鍵雜湊進行分割槽我們失去了鍵範圍分割槽的一個很好的屬性高效執行範圍查詢的能力。曾經相鄰的鍵現在分散在所有分割槽中所以它們之間的順序就丟失了。在MongoDB中如果你使用了基於雜湊的分割槽模式則任何範圍查詢都必須傳送到所有分割槽【4】。Riak【9】、Couchbase 【10】或Voldemort不支援主鍵上的範圍查詢。
Cassandra採取了折衷的策略【11, 12, 13】。 Cassandra中的表可以使用由多個列組成的複合主鍵來宣告。鍵中只有第一列會作為雜湊的依據而其他列則被用作Casssandra的SSTables中排序資料的連線索引。儘管查詢無法在複合主鍵的第一列中按範圍掃表但如果第一列已經指定了固定值則可以對該鍵的其他列執行有效的範圍掃描。
@ -190,7 +190,7 @@ Cassandra採取了折衷的策略【11, 12, 13】。 Cassandra中的表可以使
我們在前面說過([圖6-3](../img/fig6-3.png)),最好將可能的雜湊分成不同的範圍,並將每個範圍分配給一個分割槽(例如,如果$0≤hash(key)<b_0$則將鍵分配給分割槽0如果$b_0 hash(key) <b_1$則分配給分割槽1
也許你想知道為什麼我們不使用 ***取模mod***(許多程式語言中的運算子)。例如,`hash(key) mod 10`會返回一個介於0和9之間的數字如果我們將雜湊寫為十進位制數雜湊模10將是最後一個數字。如果我們有10個節點編號為0到9這似乎是將每個鍵分配給一個節點的簡單方法。
也許你想知道為什麼我們不使用 ***取模mod***(許多程式語言中的%運算子)。例如,`hash(key) mod 10`會返回一個介於0和9之間的數字如果我們將雜湊寫為十進位制數雜湊模10將是最後一個數字。如果我們有10個節點編號為0到9這似乎是將每個鍵分配給一個節點的簡單方法。
模N$mod N$方法的問題是如果節點數量N發生變化大多數鍵將需要從一個節點移動到另一個節點。例如假設$hash(key)=123456$。如果最初有10個節點那麼這個鍵一開始放在節點6上因為$123456\ mod\ 10 = 6$。當你增長到11個節點時鍵需要移動到節點3$123456\ mod\ 11 = 3$當你增長到12個節點時需要移動到節點0$123456\ mod\ 12 = 0$)。這種頻繁的舉動使得重新平衡過於昂貴。

View File

@ -77,9 +77,9 @@ ACID原子性的定義特徵是**能夠在錯誤時中止事務,丟棄該
很不幸,這一個詞就至少有四種不同的含義。
ACID一致性的概念是**對資料的一組特定約束必須始終成立**。即**不變invariants**。例如,在會計系統中,所有賬戶整體上必須借貸相抵。如果一個事務開始於一個滿足這些不變的有效資料庫,且在事務處理期間的任何寫入操作都保持這種有效性,那麼可以確定,不變總是滿足的。
ACID一致性的概念是**對資料的一組特定約束必須始終成立**。即**不變invariants**。例如,在會計系統中,所有賬戶整體上必須借貸相抵。如果一個事務開始於一個滿足這些不變的有效資料庫,且在事務處理期間的任何寫入操作都保持這種有效性,那麼可以確定,不變總是滿足的。
但是,一致性的這種概念取決於應用程式對不變的理解,應用程式負責正確定義它的事務,並保持一致性。這並不是資料庫可以保證的事情:如果你寫入違反不變數的髒資料,資料庫也無法阻止你(一些特定型別的不變數可以由資料庫檢查,例如外來鍵約束或唯一約束,但是一般來說,是應用程式來定義什麼樣的資料是有效的,什麼樣是無效的。—— 資料庫只管儲存)。
但是,一致性的這種概念取決於應用程式對不變的理解,應用程式負責正確定義它的事務,並保持一致性。這並不是資料庫可以保證的事情:如果你寫入違反不變式的髒資料,資料庫也無法阻止你(一些特定型別的不變式可以由資料庫檢查,例如外來鍵約束或唯一約束,但是一般來說,是應用程式來定義什麼樣的資料是有效的,什麼樣是無效的。—— 資料庫只管儲存)。
原子性隔離性和永續性是資料庫的屬性而一致性在ACID意義上是應用程式的屬性。應用可能依賴資料庫的原子性和隔離屬性來實現一致性但這並不僅取決於資料庫。因此字母C不屬於ACID[^i]。
@ -119,7 +119,7 @@ ACID意義上的隔離性意味著**同時執行的事務是相互隔離的**
> * 當電源突然斷電時特別是固態硬碟有證據顯示有時會違反應有的保證甚至fsync也不能保證正常工作【12】。硬碟韌體可能有錯誤就像任何其他型別的軟體一樣【13,14】。
> * 儲存引擎和檔案系統之間的微妙互動可能會導致難以追蹤的錯誤並可能導致磁碟上的檔案在崩潰後被損壞【15,16】。
> * 磁碟上的資料可能會在沒有檢測到的情況下逐漸損壞【17】。如果資料已損壞一段時間副本和最近的備份也可能損壞。這種情況下需要嘗試從歷史備份中恢復資料。
> * 一項關於固態硬碟的研究發現在執行的前四年中30到80的硬碟會產生至少一個壞塊【18】。相比固態硬碟磁碟的壞道率較低但完全失效的概率更高。
> * 一項關於固態硬碟的研究發現在執行的前四年中30%到80%的硬碟會產生至少一個壞塊【18】。相比固態硬碟磁碟的壞道率較低但完全失效的概率更高。
> * 如果SSD斷電可能會在幾周內開始丟失資料具體取決於溫度【19】。
>
> 在實踐中,沒有一種技術可以提供絕對保證。只有各種降低風險的技術,包括寫入磁碟,複製到遠端機器和備份——它們可以且應該一起使用。與往常一樣,最好抱著懷疑的態度接受任何理論上的“保證”。

View File

@ -82,7 +82,7 @@
> 你可能想知道這是否有意義——直觀地看來系統只能像其最不可靠的元件最薄弱的環節一樣可靠。事實並非如此事實上從不太可靠的潛在基礎構建更可靠的系統是計算機領域的一個古老思想【11】。例如
>
> * 糾錯碼允許數字資料在通訊通道上準確傳輸偶爾會出現一些錯誤例如由於無線網路上的無線電干擾【12】。
> * **網際網路協議Internet Protocol, IP** 不可靠:可能丟棄,延遲,重複或重排資料包。 傳輸控制協議Transmission Control Protocol, TCP在網際網路協議IP之上提供了更可靠的傳輸層它確保丟失的資料包被重新傳輸消除重複並且資料包被重新組裝成它們被傳送的順序。
> * **網際網路協議Internet Protocol, IP** 不可靠:可能丟棄、延遲、重複或重排資料包。 傳輸控制協議Transmission Control Protocol, TCP在網際網路協議IP之上提供了更可靠的傳輸層它確保丟失的資料包被重新傳輸消除重複並且資料包被重新組裝成它們被傳送的順序。
>
> 雖然這個系統可以比它的底層部分更可靠但它的可靠性總是有限的。例如糾錯碼可以處理少量的單位元錯誤但是如果你的訊號被幹擾所淹沒那麼透過通道可以得到多少資料是有根本性的限制的【13】。 TCP可以隱藏資料包的丟失重複和重新排序但是它不能神奇地消除網路中的延遲。
>
@ -278,7 +278,7 @@
在具有多個CPU插槽的伺服器上每個CPU可能有一個單獨的計時器但不一定與其他CPU同步。作業系統會補償所有的差異並嘗試嚮應用執行緒表現出單調鐘的樣子即使這些執行緒被排程到不同的CPU上。當然明智的做法是不要太把這種單調性保證當回事【40】。
如果NTP協議檢測到計算機的本地石英鐘比NTP伺服器要更快或更慢則可以調整單調鍾向前走的頻率這稱為**偏移skewing** 時鐘。預設情況下NTP允許時鐘速率增加或減慢最高至0.05但NTP不能使單調時鐘向前或向後跳轉。單調時鐘的解析度通常相當好在大多數系統中它們能在幾微秒或更短的時間內測量時間間隔。
如果NTP協議檢測到計算機的本地石英鐘比NTP伺服器要更快或更慢則可以調整單調鍾向前走的頻率這稱為**偏移skewing** 時鐘。預設情況下NTP允許時鐘速率增加或減慢最高至0.05%但NTP不能使單調時鐘向前或向後跳轉。單調時鐘的解析度通常相當好在大多數系統中它們能在幾微秒或更短的時間內測量時間間隔。
在分散式系統中,使用單調鍾測量**經過時間**elapsed time比如超時通常很好因為它不假定不同節點的時鐘之間存在任何同步並且對測量的輕微不準確性不敏感。
@ -339,7 +339,7 @@ NTP同步是否能足夠準確以至於這種不正確的排序不會發生
你可能能夠以微秒或甚至納秒的精度讀取機器的時鐘。但即使可以得到如此細緻的測量結果這並不意味著這個值對於這樣的精度實際上是準確的。實際上大概率是不準確的——如前所述即使你每分鐘與本地網路上的NTP伺服器進行同步幾毫秒的時間漂移也很容易在不精確的石英時鐘上發生。使用公共網際網路上的NTP伺服器最好的準確度可能達到幾十毫秒而且當網路擁塞時誤差可能會超過100毫秒【57】。
因此將時鐘讀數視為一個時間點是沒有意義的——它更像是一段時間範圍例如一個系統可能以95的置信度認為當前時間處於本分鐘內的第10.3秒和10.5秒之間它可能沒法比這更精確了【58】。如果我們只知道±100毫秒的時間那麼時間戳中的微秒數字部分基本上是沒有意義的。
因此將時鐘讀數視為一個時間點是沒有意義的——它更像是一段時間範圍例如一個系統可能以95%的置信度認為當前時間處於本分鐘內的第10.3秒和10.5秒之間它可能沒法比這更精確了【58】。如果我們只知道±100毫秒的時間那麼時間戳中的微秒數字部分基本上是沒有意義的。
不確定性界限可以根據你的時間源來計算。如果你的GPS接收器或原子時鐘直接連線到你的計算機上預期的錯誤範圍由製造商告知。如果從伺服器獲得時間則不確定性取決於自上次與伺服器同步以來的石英鐘漂移的期望值加上NTP伺服器的不確定性再加上到伺服器的網路往返時間只是獲取粗略近似值並假設伺服器是可信的

View File

@ -169,7 +169,7 @@
### 百分位點percentile
透過計算有多少值高於或低於某個閾值來衡量值分佈的方法。 例如某個時間段的第95個百分位響應時間是時間t則該時間段中95的請求完成時間小於t5的請求完成時間要比t長。 請參閱“[描述效能](ch1.md#描述效能)”。
透過計算有多少值高於或低於某個閾值來衡量值分佈的方法。 例如某個時間段的第95個百分位響應時間是時間t則該時間段中95%的請求完成時間小於t5%的請求完成時間要比t長。 請參閱“[描述效能](ch1.md#描述效能)”。
### 主鍵primary key