Merge branch 'master' into chronicle-error

This commit is contained in:
Feng Ruohang 2021-01-07 18:15:10 +08:00 committed by GitHub
commit 0d22426175
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 19 additions and 18 deletions

View File

@ -1,7 +1,7 @@
# 10. 批处理 # 10. 批处理
![](img/ch10.png) ![](img/ch10.png)
z
> 带有太强个人色彩的系统无法成功。当最初的设计完成并且相对稳定时,不同的人们以自己的方式进行测试,真正的考验才开始。 > 带有太强个人色彩的系统无法成功。当最初的设计完成并且相对稳定时,不同的人们以自己的方式进行测试,真正的考验才开始。
> >
> ——高德纳 > ——高德纳
@ -529,7 +529,8 @@ top5.each{|count, url| puts "#{count} #{url}" } # 5
## MapReduce之后 ## MapReduce之后
虽然MapReduce在2020年代后期变得非常流行并受到大量的炒作但它只是分布式系统的许多可能的编程模型之一。对于不同的数据量数据结构和处理类型其他工具可能更适合表示计算。 虽然MapReduce在2000年代后期变得非常流行并受到大量的炒作但它只是分布式系统的许多可能的编程模型之一。对于不同的数据量数据结构和处理类型其他工具可能更适合表示计算。
不管如何我们在这一章花了大把时间来讨论MapReduce因为它是一种有用的学习工具它是分布式文件系统的一种相当简单明晰的抽象。在这里**简单**意味着我们能理解它在做什么而不是意味着使用它很简单。恰恰相反使用原始的MapReduce API来实现复杂的处理工作实际上是非常困难和费力的 —— 例如任意一种连接算法都需要你从头开始实现【37】。 不管如何我们在这一章花了大把时间来讨论MapReduce因为它是一种有用的学习工具它是分布式文件系统的一种相当简单明晰的抽象。在这里**简单**意味着我们能理解它在做什么而不是意味着使用它很简单。恰恰相反使用原始的MapReduce API来实现复杂的处理工作实际上是非常困难和费力的 —— 例如任意一种连接算法都需要你从头开始实现【37】。

6
ch4.md
View File

@ -90,7 +90,7 @@ JSONXML和CSV属于文本格式因此具有人类可读性尽管它们
JSON比XML简洁但与二进制格式相比还是太占空间。这一事实导致大量二进制编码版本JSON & XML的出现JSONMessagePackBSONBJSONUBJSONBISON和Smile等例如WBXML和Fast Infoset。这些格式已经在各种各样的领域中采用但是没有一个能像文本版JSON和XML那样被广泛采用。 JSON比XML简洁但与二进制格式相比还是太占空间。这一事实导致大量二进制编码版本JSON & XML的出现JSONMessagePackBSONBJSONUBJSONBISON和Smile等例如WBXML和Fast Infoset。这些格式已经在各种各样的领域中采用但是没有一个能像文本版JSON和XML那样被广泛采用。
这些格式中的一些扩展了一组数据类型(例如,区分整数和浮点数,或者增加对二进制字符串的支持),另一方面,它们没有盖面JSON / XML的数据模型。特别是由于它们没有规定模式所以它们需要在编码数据中包含所有的对象字段名称。也就是说在[例4-1]()中的JSON文档的二进制编码中需要在某处包含字符串`userName``favoriteNumber`和`interest`。 这些格式中的一些扩展了一组数据类型(例如,区分整数和浮点数,或者增加对二进制字符串的支持),另一方面,它们没有改变JSON / XML的数据模型。特别是由于它们没有规定模式所以它们需要在编码数据中包含所有的对象字段名称。也就是说在[例4-1]()中的JSON文档的二进制编码中需要在某处包含字符串`userName``favoriteNumber`和`interest`。
**例4-1 本章中用于展示二进制编码的示例记录** **例4-1 本章中用于展示二进制编码的示例记录**
@ -182,7 +182,7 @@ Thrift CompactProtocol编码在语义上等同于BinaryProtocol但是如[图4
#### 数据类型和模式演变 #### 数据类型和模式演变
如何改变字段的数据类型?这可能是可能的——检查文件的细节——但是有一个风险,值将失去精度或被扼杀。例如假设你将一个32位的整数变成一个64位的整数。新代码可以轻松读取旧代码写入的数据因为解析器可以用零填充任何缺失的位。但是如果旧代码读取由新代码写入的数据则旧代码仍使用32位变量来保存该值。如果解码的64位值不适合32位则它将被截断。 如何改变字段的数据类型?这可能是可能的——检查文件的细节——但是有一个风险,值将失去精度或被破坏。例如假设你将一个32位的整数变成一个64位的整数。新代码可以轻松读取旧代码写入的数据因为解析器可以用零填充任何缺失的位。但是如果旧代码读取由新代码写入的数据则旧代码仍使用32位变量来保存该值。如果解码的64位值不适合32位则它将被截断。
Protobuf的一个奇怪的细节是它没有列表或数组数据类型而是有一个字段的重复标记这是第三个选项旁边必要和可选。如[图4-4](img/fig4-4.png)所示,重复字段的编码正如它所说的那样:同一个字段标记只是简单地出现在记录中。这具有很好的效果,可以将可选(单值)字段更改为重复(多值)字段。读取旧数据的新代码会看到一个包含零个或一个元素的列表(取决于该字段是否存在)。读取新数据的旧代码只能看到列表的最后一个元素。 Protobuf的一个奇怪的细节是它没有列表或数组数据类型而是有一个字段的重复标记这是第三个选项旁边必要和可选。如[图4-4](img/fig4-4.png)所示,重复字段的编码正如它所说的那样:同一个字段标记只是简单地出现在记录中。这具有很好的效果,可以将可选(单值)字段更改为重复(多值)字段。读取旧数据的新代码会看到一个包含零个或一个元素的列表(取决于该字段是否存在)。读取新数据的旧代码只能看到列表的最后一个元素。
@ -418,7 +418,7 @@ Web服务仅仅是通过网络进行API请求的一系列技术的最新版本
* 本地函数调用是可预测的,并且成功或失败,这仅取决于受您控制的参数。网络请求是不可预知的:由于网络问题,请求或响应可能会丢失,或者远程计算机可能很慢或不可用,这些问题完全不在您的控制范围之内。网络问题是常见的,所以你必须预测他们,例如通过重试失败的请求。 * 本地函数调用是可预测的,并且成功或失败,这仅取决于受您控制的参数。网络请求是不可预知的:由于网络问题,请求或响应可能会丢失,或者远程计算机可能很慢或不可用,这些问题完全不在您的控制范围之内。网络问题是常见的,所以你必须预测他们,例如通过重试失败的请求。
* 本地函数调用要么返回结果,要么抛出异常,或者永远不返回(因为进入无限循环或进程崩溃)。网络请求有另一个可能的结果:由于超时,它可能会返回没有结果。在这种情况下,你根本不知道发生了什么:如果你没有得到来自远程服务的响应,你无法知道请求是否通过。 (我们将在[第8章](ch8.md)更详细地讨论这个问题。) * 本地函数调用要么返回结果,要么抛出异常,或者永远不返回(因为进入无限循环或进程崩溃)。网络请求有另一个可能的结果:由于超时,它可能会返回没有结果。在这种情况下,你根本不知道发生了什么:如果你没有得到来自远程服务的响应,你无法知道请求是否通过。 (我们将在[第8章](ch8.md)更详细地讨论这个问题。)
* 如果您重试失败的网络请求,可能会发生请求实际上正在通过,只有响应丢失。在这种情况下,重试将导致该操作被执行多次,除非您在协议中引入除重( **幂等idempotence**)机制。本地函数调用没有这个问题。 (在[第十一章](ch11.md)更详细地讨论幂等性) * 如果您重试失败的网络请求,可能会发生请求实际上正在通过,只有响应丢失。在这种情况下,重试将导致该操作被执行多次,除非您在协议中引入除重( **幂等idempotence**)机制。本地函数调用没有这个问题。 (在[第十一章](ch11.md)更详细地讨论幂等性)
* 每次调用本地功能时,通常需要大致相同的时间来执行。网络请求比函数调用要慢得多,而且其延迟也是非常可变的:在不到一毫秒的时间内它可能会完成,但是当网络拥塞或者远程服务超载时,可能需要几秒钟的时间完一样的东西。 * 每次调用本地功能时,通常需要大致相同的时间来执行。网络请求比函数调用要慢得多,而且其延迟也是非常可变的:在不到一毫秒的时间内它可能会完成,但是当网络拥塞或者远程服务超载时,可能需要几秒钟的时间完一样的东西。
* 调用本地函数时,可以高效地将引用(指针)传递给本地内存中的对象。当你发出一个网络请求时,所有这些参数都需要被编码成可以通过网络发送的一系列字节。没关系,如果参数是像数字或字符串这样的基本类型,但是对于较大的对象很快就会变成问题。 * 调用本地函数时,可以高效地将引用(指针)传递给本地内存中的对象。当你发出一个网络请求时,所有这些参数都需要被编码成可以通过网络发送的一系列字节。没关系,如果参数是像数字或字符串这样的基本类型,但是对于较大的对象很快就会变成问题。
客户端和服务可以用不同的编程语言实现所以RPC框架必须将数据类型从一种语言翻译成另一种语言。这可能会捅出大篓子因为不是所有的语言都具有相同的类型 —— 例如回想一下JavaScript的数字大于$2^{53}$的问题(参阅“[JSONXML和二进制变体](#JSONXML和二进制变体)”)。用单一语言编写的单个进程中不存在此问题。 客户端和服务可以用不同的编程语言实现所以RPC框架必须将数据类型从一种语言翻译成另一种语言。这可能会捅出大篓子因为不是所有的语言都具有相同的类型 —— 例如回想一下JavaScript的数字大于$2^{53}$的问题(参阅“[JSONXML和二进制变体](#JSONXML和二进制变体)”)。用单一语言编写的单个进程中不存在此问题。

2
ch6.md
View File

@ -173,7 +173,7 @@
* 数据集大小增加所以您想添加更多的磁盘和RAM来存储它。 * 数据集大小增加所以您想添加更多的磁盘和RAM来存储它。
* 机器出现故障,其他机器需要接管故障机器的责任。 * 机器出现故障,其他机器需要接管故障机器的责任。
所有这些更改都需要数据和请求从一个节点移动到另一个节点。 将负载从集群中的一个节点向另一个节点移动的过程称为**再平衡reblancing**。 所有这些更改都需要数据和请求从一个节点移动到另一个节点。 将负载从集群中的一个节点向另一个节点移动的过程称为**再平衡rebalancing**。
无论使用哪种分区方案,再平衡通常都要满足一些最低要求: 无论使用哪种分区方案,再平衡通常都要满足一些最低要求:

2
ch7.md
View File

@ -324,7 +324,7 @@ SELECT COUNT*FROM emails WHERE recipient_id = 2 AND unread_flag = true
与读取提交的隔离类似,快照隔离的实现通常使用写锁来防止脏写(请参阅“[读已提交](#读已提交)”),这意味着进行写入的事务会阻止另一个事务修改同一个对象。但是读取不需要任何锁定。从性能的角度来看,快照隔离的一个关键原则是:**读不阻塞写,写不阻塞读**。这允许数据库在处理一致性快照上的长时间查询时,可以正常地同时处理写入操作。且两者间没有任何锁定争用。 与读取提交的隔离类似,快照隔离的实现通常使用写锁来防止脏写(请参阅“[读已提交](#读已提交)”),这意味着进行写入的事务会阻止另一个事务修改同一个对象。但是读取不需要任何锁定。从性能的角度来看,快照隔离的一个关键原则是:**读不阻塞写,写不阻塞读**。这允许数据库在处理一致性快照上的长时间查询时,可以正常地同时处理写入操作。且两者间没有任何锁定争用。
为了实现快照隔离,数据库使用了我们看到的用于防止[图7-4]()中的脏读的机制的一般化。数据库必须可能保留一个对象的几个不同的提交版本,因为各种正在进行的事务可能需要看到数据库在不同的时间点的状态。因为它并排维护着多个版本的对象,所以这种技术被称为**多版本并发控制MVCC, multi-version concurrentcy control**。 为了实现快照隔离,数据库使用了我们看到的用于防止[图7-4]()中的脏读的机制的一般化。数据库必须可能保留一个对象的几个不同的提交版本,因为各种正在进行的事务可能需要看到数据库在不同的时间点的状态。因为它并排维护着多个版本的对象,所以这种技术被称为**多版本并发控制MVCC, multi-version concurrency control**。
如果一个数据库只需要提供**读已提交**的隔离级别,而不提供**快照隔离**那么保留一个对象的两个版本就足够了提交的版本和被覆盖但尚未提交的版本。支持快照隔离的存储引擎通常也使用MVCC来实现**读已提交**隔离级别。一种典型的方法是**读已提交**为每个查询使用单独的快照,而**快照隔离**对整个事务使用相同的快照。 如果一个数据库只需要提供**读已提交**的隔离级别,而不提供**快照隔离**那么保留一个对象的两个版本就足够了提交的版本和被覆盖但尚未提交的版本。支持快照隔离的存储引擎通常也使用MVCC来实现**读已提交**隔离级别。一种典型的方法是**读已提交**为每个查询使用单独的快照,而**快照隔离**对整个事务使用相同的快照。

14
ch8.md
View File

@ -216,11 +216,11 @@
为什么数据中心网络和互联网使用分组交换?答案是,它们针对**突发流量bursty traffic**进行了优化。一个电路适用于音频或视频通话,在通话期间需要每秒传送相当数量的比特。另一方面,请求网页,发送电子邮件或传输文件没有任何特定的带宽要求——我们只是希望它尽快完成。 为什么数据中心网络和互联网使用分组交换?答案是,它们针对**突发流量bursty traffic**进行了优化。一个电路适用于音频或视频通话,在通话期间需要每秒传送相当数量的比特。另一方面,请求网页,发送电子邮件或传输文件没有任何特定的带宽要求——我们只是希望它尽快完成。
如果你想通过电路传输文件,你将不得不猜测一个带宽分配。如果您猜的太低传输速度会不必要的太慢导致网络容量闲置。如果你猜的太高电路就无法建立因为如果无法保证其带宽分配网络不能建立电路。因此使用用于突发数据传输的电路浪费网络容量并且使传输不必要地缓慢。相比之下TCP动态调整数据传输速率以适应可用的网络容量。 如果想通过电路传输文件,你得预测一个带宽分配。如果你猜的太低传输速度会不必要的太慢导致网络容量闲置。如果你猜的太高电路就无法建立因为如果无法保证其带宽分配网络不能建立电路。因此使用用于突发数据传输的电路浪费网络容量并且使传输不必要地缓慢。相比之下TCP动态调整数据传输速率以适应可用的网络容量。
已经有一些尝试去建立支持电路交换和分组交换的混合网络比如ATM[^iii] InfiniBand有一些相似之处【35】它在链路层实现了端到端的流量控制从而减少了在网络中排队尽管它仍然可能因链路拥塞而受到延迟【36】。通过仔细使用**服务质量quality of service,**QoS数据包的优先级和调度和**准入控制admission control**(限速发送器),可以仿真分组网络上的电路交换,或提供统计上的**有限延迟**【25,32】。 已经有一些尝试去建立支持电路交换和分组交换的混合网络比如ATM[^iii] InfiniBand有一些相似之处【35】它在链路层实现了端到端的流量控制从而减少了在网络中排队尽管它仍然可能因链路拥塞而受到延迟【36】。通过仔细使用**服务质量quality of service,**QoS数据包的优先级和调度和**准入控制admission control**(限速发送器),可以仿真分组网络上的电路交换,或提供统计上的**有限延迟**【25,32】。
[^iii]: **异步传输模式Asynchronous TransferMode, ATM**在20世纪80年代是以太网的竞争对手【32】但在电话网核心交换机之外并没有得到太多的采用。与自动柜员机也称为自动取款机无关尽管共用一个缩写词。或许在一些平行的世界里互联网是基于像ATM这样的东西互联网视频通话可能比我们的更可靠,因为它们不会遭受丢包和延迟的包裹。 [^iii]: **异步传输模式Asynchronous TransferMode, ATM**在20世纪80年代是以太网的竞争对手【32】但在电话网核心交换机之外并没有得到太多的采用。与自动柜员机也称为自动取款机无关尽管共用一个缩写词。或许在一些平行的世界里互联网是基于像ATM这样的东西此它们的互联网视频通话可能比我们的更可靠,因为它们不会遭受丢包和延迟的包裹。
但是,目前在多租户数据中心和公共云或通过互联网[^iv]进行通信时,此类服务质量尚未启用。当前部署的技术不允许我们对网络的延迟或可靠性作出任何保证:我们必须假设网络拥塞,排队和无限的延迟总是会发生。因此,超时时间没有“正确”的值——它需要通过实验来确定。 但是,目前在多租户数据中心和公共云或通过互联网[^iv]进行通信时,此类服务质量尚未启用。当前部署的技术不允许我们对网络的延迟或可靠性作出任何保证:我们必须假设网络拥塞,排队和无限的延迟总是会发生。因此,超时时间没有“正确”的值——它需要通过实验来确定。
@ -271,7 +271,7 @@
[^v]: 虽然时钟被称为实时时钟但它与实时操作系统无关如第298页上的“[响应时间保证](#响应时间保证)”中所述。 [^v]: 虽然时钟被称为实时时钟但它与实时操作系统无关如第298页上的“[响应时间保证](#响应时间保证)”中所述。
时钟通常与NTP同步这意味着来自一台机器的时间戳理想情况下意味着与另一台机器上的时间戳相同。但是如下节所述时钟也具有各种各样的奇特之处。特别是如果本地时钟在NTP服务器之前太远则它可能会被强制重置看上去好像跳回了先前的时间点。这些跳跃以及他们经常忽略闰秒的事实使时钟不能用于测量经过时间【38】。 时钟通常与NTP同步这意味着来自一台机器的时间戳理想情况下与另一台机器上的时间戳相同。但是如下节所述时钟也具有各种各样的奇特之处。特别是如果本地时钟在NTP服务器之前太远则它可能会被强制重置看上去好像跳回了先前的时间点。这些跳跃以及他们经常忽略闰秒的事实使时钟不能用于测量经过时间【38】。
时钟还具有相当粗略的分辨率例如在较早的Windows系统上以10毫秒为单位前进【39】。在最近的系统中这已经不是一个问题了。 时钟还具有相当粗略的分辨率例如在较早的Windows系统上以10毫秒为单位前进【39】。在最近的系统中这已经不是一个问题了。
@ -385,13 +385,13 @@
while(true){ while(true){
request=getIncomingRequest(); request=getIncomingRequest();
// 确保租约还剩下至少10秒 // 确保租约还剩下至少10秒
if (lease.expiryTimeMillis-System.currentTimeMillis()< 10000){ if (lease.expiryTimeMillis-System.currentTimeMillis() < 10000){
lease = lease.renew(); lease = lease.renew();
} }
if(lease.isValid()){ if(lease.isValid()){
process(request); process(request);
}} }
} }
``` ```
@ -485,7 +485,7 @@ while(true){
如果一个节点继续表现为**天选者**,即使大多数节点已经声明它已经死了,则在考虑不周的系统中可能会导致问题。这样的节点能以自己赋予的权能向其他节点发送消息,如果其他节点相信,整个系统可能会做一些不正确的事情。 如果一个节点继续表现为**天选者**,即使大多数节点已经声明它已经死了,则在考虑不周的系统中可能会导致问题。这样的节点能以自己赋予的权能向其他节点发送消息,如果其他节点相信,整个系统可能会做一些不正确的事情。
例如,[图8-4](img/fig8-4.png)显示了由于不正确的锁实现导致的数据损坏错误。 这个错误不仅仅是理论上的HBase曾经有这个问题【74,75】假设你要确保一个存储服务中的文件一次只能被一个客户访问因为如果多个客户试图对此,该文件将被损坏。您尝试通过在访问文件之前要求客户端从锁定服务获取租约来实现此目的。 例如,[图8-4](img/fig8-4.png)显示了由于不正确的锁实现导致的数据损坏错误。 这个错误不仅仅是理论上的HBase曾经有这个问题【74,75】假设你要确保一个存储服务中的文件一次只能被一个客户访问因为如果多个客户试图对此写入,该文件将被损坏。您尝试通过在访问文件之前要求客户端从锁定服务获取租约来实现此目的。
![](img/fig8-4.png) ![](img/fig8-4.png)
@ -503,7 +503,7 @@ while(true){
我们假设每次锁定服务器授予锁或租约时,它还会返回一个**防护令牌fencing token**,这个数字在每次授予锁定时都会增加(例如,由锁定服务增加)。然后,我们可以要求客户端每次向存储服务发送写入请求时,都必须包含当前的防护令牌。 我们假设每次锁定服务器授予锁或租约时,它还会返回一个**防护令牌fencing token**,这个数字在每次授予锁定时都会增加(例如,由锁定服务增加)。然后,我们可以要求客户端每次向存储服务发送写入请求时,都必须包含当前的防护令牌。
在[图8-5](img/fig8-5.png)中客户端1以33的令牌获得租约但随后进入一个长时间的停顿并且租约到期。客户端2以34的令牌该数字总是增加获取租约然后将其写入请求发送到存储服务包括34的令牌。稍后客户端1恢复生机并将其写入存储服务包括其令牌值33.但是存储服务器会记住它已经处理了一个具有更高令牌编号34的写入因此它会拒绝带有令牌33的请求。 在[图8-5](img/fig8-5.png)中客户端1以33的令牌获得租约但随后进入一个长时间的停顿并且租约到期。客户端2以34的令牌该数字总是增加获取租约然后将其写入请求发送到存储服务包括34的令牌。稍后客户端1恢复生机并将其写入存储服务包括其令牌值33但是存储服务器会记住它已经处理了一个具有更高令牌编号34的写入因此它会拒绝带有令牌33的请求。
如果将ZooKeeper用作锁定服务则可将事务标识`zxid`或节点版本`cversion`用作防护令牌。由于它们保证单调递增因此它们具有所需的属性【74】。 如果将ZooKeeper用作锁定服务则可将事务标识`zxid`或节点版本`cversion`用作防护令牌。由于它们保证单调递增因此它们具有所需的属性【74】。

2
ch9.md
View File

@ -301,7 +301,7 @@
这种行为的原因是每个CPU核都有自己的内存缓存和存储缓冲区。默认情况下内存访问首先走缓存任何变更会异步写入主存。因为缓存访问比主存要快得多【45】所以这个特性对于现代CPU的良好性能表现至关重要。但是现在就有几个数据副本一个在主存中也许还有几个在不同缓存中的其他副本而且这些副本是异步更新的所以就失去了线性一致性。 这种行为的原因是每个CPU核都有自己的内存缓存和存储缓冲区。默认情况下内存访问首先走缓存任何变更会异步写入主存。因为缓存访问比主存要快得多【45】所以这个特性对于现代CPU的良好性能表现至关重要。但是现在就有几个数据副本一个在主存中也许还有几个在不同缓存中的其他副本而且这些副本是异步更新的所以就失去了线性一致性。
为什么要做这个权衡对多核内存一致性模型而言CAP定理是没有意义的在同一台计算机中我们通常假定通 信都是可靠的。并且我们并不指望一个CPU核能在脱离计算机其他部分的条件下继续正常工作。牺牲线性一致性的原因是**性能performance**,而不是容错。 为什么要做这个权衡对多核内存一致性模型而言CAP定理是没有意义的在同一台计算机中我们通常假定通信都是可靠的。并且我们并不指望一个CPU核能在脱离计算机其他部分的条件下继续正常工作。牺牲线性一致性的原因是**性能performance**,而不是容错。
许多分布式数据库也是如此:它们是**为了提高性能**而选择了牺牲线性一致性而不是为了容错【46】。线性一致的速度很慢——这始终是事实而不仅仅是网络故障期间。 许多分布式数据库也是如此:它们是**为了提高性能**而选择了牺牲线性一致性而不是为了容错【46】。线性一致的速度很慢——这始终是事实而不仅仅是网络故障期间。

View File

@ -24,7 +24,7 @@ Alibaba+-Finplus 架构师/全栈工程师 (2015 ~ 2017)
《设计数据密集型应用》封面上的动物是**印度野猪Sus scrofa cristatus**,它是在印度、缅甸、尼泊尔、斯里兰卡和泰国发现的一种野猪的亚种。与欧洲野猪不同,它们有更高的背部鬃毛,没有体表绒毛,以及更大更直的头骨。 《设计数据密集型应用》封面上的动物是**印度野猪Sus scrofa cristatus**,它是在印度、缅甸、尼泊尔、斯里兰卡和泰国发现的一种野猪的亚种。与欧洲野猪不同,它们有更高的背部鬃毛,没有体表绒毛,以及更大更直的头骨。
印度野猪有一头灰色或黑色的头发脊背上有短而硬的毛。雄性有突出的犬齿称为T用来与对手战斗或抵御掠食者。雄性比雌性大这些物种平均肩高33-35英寸体重200-300磅。他们的天敌包括熊、老虎和各种大型猫科动物。 印度野猪有一头灰色或黑色的头发脊背上有短而硬的毛。雄性有突出的犬齿称为T用来与对手战斗或抵御掠食者。雄性比雌性大这些物种平均肩高33-35英寸体重200-300磅。他们的天敌包括熊、老虎和各种大型猫科动物。
这些动物夜行且杂食——它们吃各种各样的东西包括根、昆虫、腐肉、坚果、浆果和小动物。野猪经常因为破坏农作物的根被人们所熟知他们造成大量的破坏并被农民所敌视。他们每天需要摄入4,000 ~ 4,500卡路里的能量。野猪有发达的嗅觉这有助于寻找地下植物和挖掘动物。然而它们的视力很差。 这些动物夜行且杂食——它们吃各种各样的东西包括根、昆虫、腐肉、坚果、浆果和小动物。野猪经常因为破坏农作物的根被人们所熟知他们造成大量的破坏并被农民所敌视。他们每天需要摄入4,000 ~ 4,500卡路里的能量。野猪有发达的嗅觉这有助于寻找地下植物和挖掘动物。然而它们的视力很差。

View File

@ -173,7 +173,7 @@
* 資料集大小增加所以您想新增更多的磁碟和RAM來儲存它。 * 資料集大小增加所以您想新增更多的磁碟和RAM來儲存它。
* 機器出現故障,其他機器需要接管故障機器的責任。 * 機器出現故障,其他機器需要接管故障機器的責任。
所有這些更改都需要資料和請求從一個節點移動到另一個節點。 將負載從叢集中的一個節點向另一個節點移動的過程稱為**再平衡reblancing**。 所有這些更改都需要資料和請求從一個節點移動到另一個節點。 將負載從叢集中的一個節點向另一個節點移動的過程稱為**再平衡rebalancing**。
無論使用哪種分割槽方案,再平衡通常都要滿足一些最低要求: 無論使用哪種分割槽方案,再平衡通常都要滿足一些最低要求:

View File

@ -324,7 +324,7 @@ SELECT COUNT*FROM emails WHERE recipient_id = 2 AND unread_flag = true
與讀取提交的隔離類似,快照隔離的實現通常使用寫鎖來防止髒寫(請參閱“[讀已提交](#讀已提交)”),這意味著進行寫入的事務會阻止另一個事務修改同一個物件。但是讀取不需要任何鎖定。從效能的角度來看,快照隔離的一個關鍵原則是:**讀不阻塞寫,寫不阻塞讀**。這允許資料庫在處理一致性快照上的長時間查詢時,可以正常地同時處理寫入操作。且兩者間沒有任何鎖定爭用。 與讀取提交的隔離類似,快照隔離的實現通常使用寫鎖來防止髒寫(請參閱“[讀已提交](#讀已提交)”),這意味著進行寫入的事務會阻止另一個事務修改同一個物件。但是讀取不需要任何鎖定。從效能的角度來看,快照隔離的一個關鍵原則是:**讀不阻塞寫,寫不阻塞讀**。這允許資料庫在處理一致性快照上的長時間查詢時,可以正常地同時處理寫入操作。且兩者間沒有任何鎖定爭用。
為了實現快照隔離,資料庫使用了我們看到的用於防止[圖7-4]()中的髒讀的機制的一般化。資料庫必須可能保留一個物件的幾個不同的提交版本,因為各種正在進行的事務可能需要看到資料庫在不同的時間點的狀態。因為它並排維護著多個版本的物件,所以這種技術被稱為**多版本併發控制MVCC, multi-version concurrentcy control**。 為了實現快照隔離,資料庫使用了我們看到的用於防止[圖7-4]()中的髒讀的機制的一般化。資料庫必須可能保留一個物件的幾個不同的提交版本,因為各種正在進行的事務可能需要看到資料庫在不同的時間點的狀態。因為它並排維護著多個版本的物件,所以這種技術被稱為**多版本併發控制MVCC, multi-version concurrency control**。
如果一個數據庫只需要提供**讀已提交**的隔離級別,而不提供**快照隔離**那麼保留一個物件的兩個版本就足夠了提交的版本和被覆蓋但尚未提交的版本。支援快照隔離的儲存引擎通常也使用MVCC來實現**讀已提交**隔離級別。一種典型的方法是**讀已提交**為每個查詢使用單獨的快照,而**快照隔離**對整個事務使用相同的快照。 如果一個數據庫只需要提供**讀已提交**的隔離級別,而不提供**快照隔離**那麼保留一個物件的兩個版本就足夠了提交的版本和被覆蓋但尚未提交的版本。支援快照隔離的儲存引擎通常也使用MVCC來實現**讀已提交**隔離級別。一種典型的方法是**讀已提交**為每個查詢使用單獨的快照,而**快照隔離**對整個事務使用相同的快照。