# 课后彩蛋|完整版思考题答案 你好,我是陈现麟。距离结课已经过去一个多月的时间了,在这段时间里我一直有关注大家的留言,知道你可能还有很多地方存在着疑问,今天这一讲我整理了一下专栏的课后思考题答案,希望能给你带来帮助。 特别说明:部分课程未设置思考题(7讲)以及部分开放性问题无标准答案(7讲)。 ## **[第02讲](https://time.geekbang.org/column/article/481049)** 分布式系统面临故障处理(部分失败)、异步网络、时钟同步和共识协调,这四个新的挑战和 CAP 理论之间是什么关系呢? **答案:**分布式系统中的部分失败会导致出现网络分区,所以需要在 CAP 理论中,分区容错(P)是必须选择的,同时它们也影响可用性(A),共识协调和 CAP 理论中的强一致性在很多情况下都是可以相互转化的问题,如果我们在 CAP 理论中选择强一致性(C),那么可以通过数据的强一致性来解决时钟同步面临的事件排序的问题。 ## **[第03讲](https://time.geekbang.org/column/article/481069)** 在分布式场景下,对于 CAP 理论,我们真的只能三选二吗? **答案:**首先,在分布式场景下,由于分区容错性是我们必须选择的,所以,CAP 理论的三选二就变成了在满足分区容错的前提下,在数据一致性和服务可用性上我们只能二选一。 其次,不论我们在数据一致性和服务可用性之间选择什么,对于被放弃的选择,我们都要尽可能在系统设计时做到最好,比如我们现在的 AP 系统,那么数据一致性级别我们虽然不能达到强一致性,但是可能单调一致性和因果一致性我们是可以做到的。对于 CP 系统也一样,虽然可用性不能达到 100%,但是 99.9999% 也是有可能做到的。 最后,由于系统分区的情况是很少见的,那么在网络不出现分区的时候,我们可以将数据强一致性和 100% 的可用性都选择上,等网络出现分区的时候,系统再选择放弃部分的可用性或者降低数据一致性的级别,通过推迟 CAP 选择来提高系统的可用性和数据一致性。 ## **[第04讲](https://time.geekbang.org/column/article/481085)** 如果将整个互联网看成是一个非常庞大的分布式系统,那么这个分布式系统的服务注册发现系统是怎么实现的?它是一个 AP 系统还是一个 CP 系统? **答案:**如果将整个互联网看成是一个非常庞大的分布式系统,那么这个分布式系统的服务注册发现系统就是 DNS 域名解析系统,通过这个系统,我们用域名地址去获得待访问服务器的 IP 地址。它是一个典型的 AP 系统。 ## **[第05讲](https://time.geekbang.org/column/article/483663)** 我们利用 Hash 策略路由的 MySQL 集群,如果需要对集群进行扩容,我们怎么做才能在最少迁移数据的情况下,依然保证路由的正确性呢? **答案:**首先是垂直扩容,不增加节点的数量,提升节点的性能和容量,这个方案不需要迁移数据,但是成本比较大,并且扩容空间比较小。 其次是水平扩容,由于采用的是 Hash 策略,求模的对象是节点数,增加节点数,会导致路由变化,而需要迁移数据。在这个情况下,一般是采用 2 倍扩容的策略,比如之前是 3 个节点,在扩容的时候就扩容为 6 个节点,这样可以保证有一半的数据不需要迁移。 ## **[第06讲](https://time.geekbang.org/column/article/484820)** 结合“如何处理强一致性的配置”的处理流程中的第二点:为了数据的一致性,放弃了可用性,Prepare 状态的 Proxy 节点相当于被锁住,不能进行读写操作。 请你思考一下,如果允许 Prepare 状态的 Proxy 节点读,会出现什么问题?如果允许 Prepare 状态的 Proxy 节点写,又会出现什么问题? **答案:**如果允许 Prepare 状态的 Proxy 节点读,可能会读到旧的数据。接着课程中的例子讨论,假如 Proxy 1 和 Proxy 2 都处于 Prepare 状态,并且都可以正常读,然后协调者 A 通知 Proxy 实例 Commit 配置信息,由于是通过网络来通知 Commit 的,所以 Proxy 1 和 Proxy 2 接收 Commit 的消息会有先后。 现在假设 Proxy 1 接收到 Commit 消息,并且将读写都切换到存储节点 2,Proxy 2 还没有接收到 Commit 消息,依然从存储节点 1 读取数据,那么这个时候,Proxy 1 新写入或修改的数据,Proxy 2 将不能读到,会出现读到旧数据的情况。 如果允许 Prepare 状态的 Proxy 节点写,可能会出现写操作丢失的情况。假如 Proxy 1 和 Proxy 2 都处于 Prepare 状态,并且都可以正常写,然后协调者 A 通知 Proxy 实例 Commit 配置信息,现在假设 Proxy 1 接收到 Commit 消息,并且将读写都切换到存储节点 2,Proxy 2 还没有接收到 Commit 消息,依然从存储节点 1 写取数据,那么这个时候,Proxy 2 新写入或修改的数据的操作将会丢失。 ## **[第07讲](https://time.geekbang.org/column/article/485006)** 根据本节课讨论的情况,在实现分布式锁服务的时候,你认为应该以什么样的原则来选择我们的存储系统呢? **答案:**如果能容忍一定的错误,那么高性能高可用的 Redis Cluster 是一个很好的选择;如果完全不能容忍失败,那么最好是不要依赖外部的分布式锁服务来保障,而是通过数据库的事务等措施来保障正确性。 ## **[第08讲](https://time.geekbang.org/column/article/485201)** 在 IM 系统中,我们如何实现幂等的消息发送接口? **答案:**客户端在发送消息时,生成唯一 ID,唯一 ID 的生成逻辑可以按以下的方式生成: **唯一 ID = Hash(UID + DID + 时间戳 + 自增计数)** 其中,UID 为用户 ID,DID 为设备 ID,自增计数为同一个时间戳下发送的消息数。然后通过课程中“至少一次消息传递加消息幂等性”的逻辑来处理。 ## **[第09讲](https://time.geekbang.org/column/article/486817)** 如果我们想判断一个服务是否过载,除了请求在队列中的平均等待时间这个指标之外,还有什么其他的好方法吗? **答案:**由于操作系统的可观测性做得非常好,所以机器的性能指标是非常标准和方便测量的,而服务的性能是非常偏业务,是不好标准化和不好测量的,所以可以通过将服务运行在一个单独的机器或者容器上,将服务的性能指标转化为机器的性能指标来测量,比如在 Kubernetes 中就可以通过 Pod 的性能过载来判断 Pod 上运行的服务是否过载。 ## **[第11讲](https://time.geekbang.org/column/article/488519)** 保障分布式系统稳定性的三板斧,熔断、限流和降级都已经讨论完了,欢迎你来分享一下自己对熔断、限流和降级的理解。 **答案:**首先,因为熔断机制是系统稳定性保障的最后一道防线,并且它是自适应的,所以我们应该在系统全局默认启用;其次,限流是用来保障被限流服务稳定性的,所以我们建议,一般在系统的核心链路和核心服务上,默认启用限流机制;最后,降级是通过牺牲被降级的接口或者服务,来保障其他的接口和服务正常运行的,所以我们可以通过降级直接停用非核心服务,然后对于核心接口和服务,在必要的时候,可以提供一个“B计划”。 其实,从整个系统的角度来看,不论是熔断还是限流,一旦触发了规则,都是通过抛弃一些请求来保障系统的稳定性的。所以,如果更广泛地定义降级的话,可以说熔断和限流都是降级的一种特殊情况。 ## **[第12讲](https://time.geekbang.org/column/article/488817)** 在云原生时代,除了按需付费(即扩容、缩容的弹性能力)之外,你觉得还有哪些趋势呢? **答案:**元原生时代,大量的公有云和 SaaS 服务出现,并且由于规模效应,公有云和 SaaS 服务的成本和质量都会越来越高,所以对于企业的 IT 系统来说,应该基于公有云和 SaaS 服务来做架构,让我们的 IT 系统生长在公有云和 SaaS 服务上,研发团队要在公有云和 SaaS 服务之上来做创新。 当然,如果是公司非常核心的业务或者技术,或者第三方的技术不能满足要求,是需要通过自研来解决的。这里主要的思想是“不要重造轮子”,其实和 2000 年之后大量的互联网公司的 IT 系统是基于开源软件来做架构是一样的。 ## **[第13讲](https://time.geekbang.org/column/article/489317)** 你平时在使用监控系统的过程中,碰到最大的痛点是什么呢? **答案:**我觉得主要有两个痛点,一个监控信息缺失,出现故障但是不能发现是非常大的问题,对于这个问题需要多层监控相互覆盖;另一个是监控信息过载,故障出现的时候,有大多的监控信息,不知道看哪里,导致不能快速定位出问题,对于这个问题,需要做监控信息的分层。 ## **[第17讲](https://time.geekbang.org/column/article/492967)** 在极端情况下,如果一个关键词的访问热度非常大,我们有什么办法对这个关键词进行负载均衡呢? **答案:**由于是一个关键词,所以我们不能直接再对它做分片了,不过可以将它复制出多个副本,然后对这些副本再做分片。 这个方案的问题是将写操作变得复杂了,需要将写操作更新到它的多个副本,并且会涉及到多个副本的一致性问题。所以这是一个通过牺牲写来确保读的 trade-off。 ## **[第18讲](https://time.geekbang.org/column/article/493804)** 你在工作中经常接触的数据库系统,是行式存储还是列式存储呢? **答案:**一般来说,OLTP 的大多是行式存储,比如 MySQL、PostgreSQL 等,而 OLAP 则大多是列式存储,比如 ClickHouse 等。 ## **[第19讲](https://time.geekbang.org/column/article/495283)** 请你根据标题思考一下,在主从复制的数据同步模式中,从副本的数据可以读吗? **答案:**在主从复制的数据同步模式中,从副本的数据能不能读可以分二种情况来讨论: 第一,同步复制的情况,如果主从复制选择的是同步复制的方式,那么从副本的数据和主副本的数据是完全相同的。在这种情况下,从副本的数据是可以读的。 第二,异步复制的情况,如果主从复制选择的是异步复制的方式,那么从副本的数据集上可能还没有同步主副本最新的更新。这个时候,如果业务能容忍一定的数据延迟,比如用户 A 的头像修改了,他的好友 B 在非常短的时候内查看到 A 的头像是没有问题的。因为 B 不知道 A 的头像更新了,这样的情况是可以读从副本的。如果业务不能容忍数据的延迟,比如用户 A 充值后,他的余额数目没有立即显示增加,用户肯定不能接受,这样的情况下是不可以读从副本的。 ## **[第20讲](https://time.geekbang.org/column/article/495775)** 本课中我们讨论了通过水平分片的方式避免写入冲突时,会出现一些不能解决的问题,那么请你思考一下,通过垂直分片的方式避免写入冲突时,会出现什么问题呢? **答案:**和课程中的水平分片的例子一样,对于垂直分片来说,如果一个修改操作修改了多个分片数据,就可能会出现冲突无法避免的问题。比如,我们的一个表有 a、b 两个字段,如果我们将表进行垂直分片,a 字段在主副本 1 写入,b 字段在主副本 2 写入。当一个事务需要操作 a 和 b 两列数据时, 如果在主副本 1 上执行,那么同一时间,当 b 字段在主副本 2 上也有修改时,就会出现写入冲突。 ## **[第21讲](https://time.geekbang.org/column/article/496934)** 如果现在有这样的一个业务场景:数据需要有 7 个副本,读写都能容忍一个节点失败,并且读请求远远大于写请求,那么 w 和 r 为多少最合适呢? **答案:**读写都能容忍一个节点失败,说明 w <= 6、r <= 6,读请求远远大于写请求,所以需要读优先,将 w 选择最大值 6,然后根据 w + r > 7,可得 r > 1,根据读优先的原则,r = 2。 ## **[第22讲](https://time.geekbang.org/column/article/497528)** 事务的一致性和数据的一致性是一个概念还是两个概念?如果是两个概念,它们之间有什么联系吗? **答案:**事务的一致性是指一个事务能够正确地将数据从一个一致性的状态,变换到另一个一致性的状态,是指在事务的执行过程中,不能出现任何不一致的问题。事务的一致性其实就是事务的正确性,如果一个事务执行前的数据是正确的,那么执行后的数据也必须是正确的,它关心的是对多个数据对象操作后的一致性。 数据的一致性是指在一个分布式系统中,每一份数据在多个节点有多个副本的时候,如果客户端修改了这一份数据后,这份数据的多个副本是否立即更新的问题。 事务的一致性和数据的一致性是两个不同的概念,不过在分布性系统中,分布性事务一致性需要依赖数据一致性的强一致性级别来实现。 ## **[第23讲](https://time.geekbang.org/column/article/498423)** 在学习 2PC 协议的过程中,我们提到了 3PC 协议,它在 2PC 的两个阶段之间插入一个阶段,从而增加了一个相互协商的过程,并且还引入了超时机制来防止阻塞,你知道这是怎么做的吗? **答案:**相比于 2PC 协议,3PC 有两个改动点: * 引入超时机制,同时在协调者和参与者中都引入超时机制; * 在第一阶段和第二阶段中插入一个准备阶段,保证了在最后提交阶段之前各参与节点的状态是一致的。 简单来说,3PC 引入超时机制和将 2PC 的准备阶段再次一分为二,这样三阶段提交就有CanCommit、PreCommit、DoCommit 三个阶段。 ## **[第24讲](https://time.geekbang.org/column/article/499721)** 你能在银行转账的业务场景下,举一个出现写倾斜的例子吗? **答案:**假设一个人在银行有一个信用余额 a 和一个个人余额 b,银行的要求是 a + b >= 0,当 a = 0、b = 500 时,假设同时有两个事务 T1 和 T2 分别读取了 a = 0、b = 500,T1 从 b 中转出 400,即 b = 100,T1 提交成功;T2 从 a 中转出 400,即 a = -400,T2 提交成功。 这个时候,a = -400, b = 100,a + b = -300,不满足约束 a + b >= 0。 ## **[第25讲](https://time.geekbang.org/column/article/500579)** 通过课程的学习,我们知道了存储设备的特性会影响存储引擎的设计,同样,业务存储的数据特点也会影响存储引擎的设计,请你来思考一下,如果我们的业务需要存储很多非常小的文件(比如平均几十 K),应该怎么来设计存储引擎呢? **答案:**由于业务的特点是有很多非常小的文件,导致文件系统会导致有大量的 iNode 节点,并且这些 iNode 节点不能被缓存,每次读取文件需要从磁盘中加载 iNode 节点,然后再从磁盘读取文件。在这个情况下,由于文件的大小非常小,系统的吞吐量会非常低。 一个比较好的方式是 2010 年 Facebook 发表在 OSDI 的论文《Finding a needle in Haystack: Facebook’s photo storage》中介绍的方法,通过将小文件合并为大文件,减少操作系统的 iNode 接口,然后在存储引擎中缓存小文件在大文件中的位置和大小,来让每一次读文件只需要读取文件的磁盘 IO 操作。 ## **[第26讲](https://time.geekbang.org/column/article/501415)** 通过对一致性模型的学习,你可以通过读写操作序列,分别举出线性一致性、顺序一致性、因果一致性和最终一致性的例子吗? **答案:** ![图片](https://static001.geekbang.org/resource/image/1d/c2/1da11bb2affb6a2efcd5cb10231821c2.png?wh=1404x588) ![图片](https://static001.geekbang.org/resource/image/3d/44/3d4a472c3809178c97d61a4e5576b944.png?wh=1376x586) ![图片](https://static001.geekbang.org/resource/image/1d/bd/1dd87c97dc0c844601eedf68c5f2b3bd.png?wh=1384x580) ![图片](https://static001.geekbang.org/resource/image/29/b5/290ae52fc7861eb309034b88ce86yyb5.png?wh=1378x578) ## **[第27讲](https://time.geekbang.org/column/article/503046)** 本课中,我们明白了一致性和共识的关系,请你继续思考一下,共识和高可用之间有什么关系呢? **答案:**可用性是 CAP 中的一环,而共识和一致性的关系是非常紧密的,并且共识是比线性一致性更高的要求,所以共识与高可用的关系和可用性与一致性的关系类似。 在不考虑其他方面的要求下,如果我们的系统中不要求共识,那么系统只要有一个节点可以使用,系统就能够使用,可用性非常高;如果我们的系统中的共识只关心非故障节点的一致性和整体系统的正常,即一般的共识问题,比如 Paxos 和 Raft 之类的算法,那么系统中大多数节点可用,系统就可用;如果我们的系统中的共识要求无论是非故障节点还是故障节点,都必须达成一致,即 Uniform Consensus,比如分布式事务中的 2PC 算法,那么系统中的节点必须都可用,系统才可用。 ## **[第28讲](https://time.geekbang.org/column/article/503459)** 在课程总结中,有这样一句话,“世界的尽头在哪里我不知道,但是我可以明确地告诉你,分布式的尽头就是共识”,欢迎你来分享一下对这句话的理解。 **答案:**分布式系统简单来说就是通过网络连接的多个节点组成的一个系统,但是这个系统对它的使用者来说要和单进程系统一样。要多个节点组成的系统对外表现的和单进程系统一样,那么分布式系统的多个节点必须要协同,而协同则需要依赖共识。 ## **[春节加餐2](https://time.geekbang.org/column/article/483586)** 计算机历史上的“千年虫”问题你了解过吗?它和时间有关系吗? **答案:** **第一个问题:**“千年虫”问题的根源始于 60 年代,当时计算机存储器的成本很高,如果用四位数字表示年份,就要多占用存储器空间,就会使成本增加,因此为了节省存储空间,计算机系统的编程人员采用两位数字表示年份。 后来,随着计算机技术的迅猛发展,存储器的价格降低了,但在计算机系统中使用两位数字来表示年份的做法却由于思维上的惯性势力而被沿袭下来, 年复一年,直到新世纪即将来临之际,大家才突然意识到用两位数字表示年份将无法正确辨识公元2000年及其以后的年份,比如计算机存储的时间为 92 年,我们不能知道表示的是 2092 年还是 1992 年。 **第二个问题:**“千年虫”问题是由于时间格式存储过短而导致的,和时间有非常直接的关系。 这节课的答疑就到这里,如果你有更多问题,欢迎继续在留言区中告知,我们共同讨论!