拉巴力的纸皮箱


  • 首页

  • 标签

  • 归档

  • 关于

  • 搜索

《SRE:Google运维解密》-笔记

发表于 2021-07-05

序言

  • 可靠性就像安全性,越早关注越好。
  • 这就意味着一些小型创业公司,在应付日常面临的种种挑战时,也应该抽出一部分精力来面对可靠性这个话题。这与盖房子有些类似,如果一开始将整个地基打好并保持继续修缮,要比盖好房子之后再重新修改设计要容易得多
  • Margaret 曾经说过:“无论对一个软件系统运行原理掌握得多么彻底,也不能阻止人犯意外错误。”
  • 只有靠着对细节的不懈关注,做好充足的灾难预案和准备工作,时刻警惕着,不放过一切机会去避免灾难发生。这就是SRE 最重要的理念!

第1章 介绍

  • 不能将碰运气当成战略。

Google的解决之道:SRE

  • SRE就是让软件工程师来设计一个新型运维团队的结果。

  • 目前来看,UNIX 系统内部细节和1~3层网络知识是Google最看重的两类额外的技术能力。

    • (a)对重复性、手工性的操作有天然的排斥感。
    • (b)有足够的技术能力快速开发出软件系统以替代手工操作。
  • 从本质上来说,SRE 就是在用软件工程的思维和方法论完成以前由系统管理员团队手动完成的任务。这些SRE倾向于通过设计、构建自动化工具来取代人工操作。

  • Google的经验法则是,SRE团队必须将50%的精力花在真实的开发工作上。

  • 由于SRE模型中为了提高可靠性需要采取一些与常规做法违背的做法,所以需要强有力的管理层支持才能推行下去。例如:由于一个季度内的错误预算耗尽而停止发布新功能的决定,可能需要管理层的支持才能让产品研发部门重视起来。

  • 我们可以认为DevOps是SRE核心理念的普适版,可以用于更广范围内的组织结构、管理结构和人员安排。同时,SRE是DevOps模型在Google的具体实践,带有一些特别的扩展。

SRE方法论

  • 一般来说,SRE团队要承担以下几类职责:可用性改进,延迟优化,性能优化,效率优化,变更管理,监控,紧急事务处理以及容量规划与管理。

  • 事后总结应该包括以下内容:事故发生、发现、解决的全过程,事故的根本原因,预防或者优化的解决方案。

  • Google 的一项准则是“对事不对人”,事后总结的目标是尽早发现和堵住漏洞,而不是通过流程去绕过和掩盖它们。

  • 一般来说,任何软件系统都不应该一味地追求100% 可靠。因为对最终用户来说,99.999% 和 100% 的可用性是没有实质区别的

  • 如果100% 不是一个正确的可靠性目标,那么多少才是呢?这其实并不是一个技术问题,而是一个产品问题。

  • 如果一个服务的可靠性目标是99.99%,那么错误预算就是 0.01%。这意味着产品研发部门和SRE部门可以在这个范围内将这个预算用于新功能上线或者产品的创新等任何事情。错误预算可以用于什么范畴呢?研发团队需要用这个预算上线新功能,吸引新用户。理想情况下,我们应该使用错误预算来最大化新功能上线的速度,同时保障服务质量。这个基本模型建立起来之后,许多常见的战术策略,例如灰度发布、1% AB测试等就全说得通了。这些战术性手段都是为了更合理地使用整个服务的错误预算。通过引进“错误预算”的概念,我们解决了研发团队和SRE团队之间的组织架构冲突。SRE团队的目标不再是 “零事故运行”,SRE团队和产品研发团队目标一致,都是在保障业务服务可靠性需求的同时尽可能地加快功能上线速度。这个改动虽小,意义却很大。一次“生产事故”不再是一件坏事,而仅仅是创新流程中一个不可避免的环节,两个团队通过协作共同管理它。

  • 一个需要人工阅读邮件和分析警报来决定目前是否需要采取某种行动的系统从本质上就是错误的。监控系统不应该依赖人来分析警报信息,而是应该由系统自动分析,仅当需要用户执行某种操作时,才需要通知用户。

  • 一个监控系统应该只有三类输出。紧急警报(alert)意味着收到警报的用户需要立即执行某种操作,目标是解决某种已经发生的问题,或者是避免即将发生的问题。工单(ticket)意味着接受工单的用户应该执行某种操作,但是并非立即执行。系统并不能自动解决目前的情况,但是如果一个用户在几天内执行这项操作,系统不会受到任何影响。日志(logging)平时没有人需要关注日志信息,但是日志信息依然被收集起来以备调试和事后分析时使用。正确的做法是平时没人会去主动阅读日志,除非有特殊需要。

  • 任何需要人工操作的事情都只会延长恢复时间。一个可以自动恢复的系统即使有更多的故障发生,也要比事事都需要人工干预的系统可用性更高。当不可避免地需要人工介入时,我们也发现与“船到桥头自然直”的态度相比,通过事先预案并且将最佳方法记录在“运维手册(playbook)”上通常可以使MTTR 降低3倍以上。初期几个万能的工程师的确可以解决生产问题,但是长久看来一个手持“运维宝典”经过多次演习的on-call工程师才是正确之路。

  • 虽然不论多么完备的“运维手册”也无法替代人的创新思维,但是在巨大的时间压力和产品压力下,运维手册中记录的清晰调试步骤和分析方法对处理问题的人是不可或缺的。因此,Google SRE将大部分工作重心放在“运维手册”的维护上,同时通过“Wheel of Misfortune”等项目[2]不断培训团队成员。

  • 变更管理SRE的经验告诉我们,大概 70% 的生产事故由某种部署的变更而触发。变更管理的最佳实践是使用自动化来完成以下几个项目:● 采用渐进式发布机制。● 迅速而准确地检测到问题的发生。● 当出现问题时,安全迅速地回退改动。

  • 容量规划有几个步骤是必需的:● 必须有一个准确的自然增长需求预测模型,需求预测的时间应该超过资源获取的时间。● 规划中必须有准确的非自然增长的需求来源的统计。● 必须有周期性压力测试,以便准确地将系统原始资源信息与业务容量对应起来。

个人总结

  1. 尽早关注安全性;关注细节和充分的准备
  2. 自动化工具替代人工操作
  3. 事后总结的若干要点;事后总结“对事不对人”
  4. 使用错误预算来最大化新功能上线的速度;目标不再是 “零事故运行”
  5. 自动恢复的系统;事先预案和“运维手册;自动化减少人为事故
  6. 容量规划;周期性压力测试和调整

第3章 拥抱风险

  • 你可能认为Google会试图构建一个百分之百可靠的服务。事实证明,超过一定值后,再提高可靠性对于一项服务(和它的用户)来说,结果可能会更差而不是更好!极端的可靠性会带来成本的大幅提升:过分追求稳定性限制了新功能的开发速度和将产品交付给用户的速度,并且很大程度地增加了成本,这反过来又减少了一个团队可以提供的新功能的数量。

  • 此外,用户通常不会注意到一项服务在高可靠性和极端可靠性之间的差异,因为用户体验主要是受较不可靠的组件主导,例如手机移动网络或者他们正在使用的设备。简单地说,用户在一个有着99%可靠性的智能手机上是不能分辨出99.99%和99.999%的服务可靠性的区别的!基于这一点,SRE旨在寻求快速创新和高效的服务运营业务之间的风险的平衡,而不是简单地将服务在线时间最大化。这样一来,我们可以优化用户的整体幸福感,平衡系统的功能、服务和性能。

管理风险

  • 不可靠的系统会很快侵蚀用户的信心,所以我们想要减少系统出故障的几率。然而,经验表明,在构建系统的过程中,可靠性进一步提升的成本并不是线性增加的—可靠性的下一个改进可能比之前的改进成本增加100倍。

度量服务的风险

  • Google标准做法是通过一个客观的指标来体现一个待优化的系统属性。

  • 可用性=系统正常运行时间/(系统正常运行时间+停机时间)

  • 在Google内部,基于时间的可用性通常是毫无意义的。
    我们通过请求成功率来定义服务可用性。
    可用性=成功请求数/总的请求数

  • 使用请求成功率指标量化计划外停机时间使得这种指标更适合在不直接服务终端用户的系统中使用。

  • 通常,我们会为一项服务设定季度性的可用性目标,每周甚至每天对性能进行跟踪。我们通过寻找、跟踪和调整重要的、不可避免的偏差来使服务达到一个高层次的可用性目标。

服务的风险容忍度

  • 为了辨别服务的风险容忍度,SRE必须与产品负责人一起努力,将一组商业目标转化为明确的可以实现的工程目标。

  • 尽管当时YouTube已经有了一个很出色的产品,但它仍然在不断变化和快速发展着。因此,我们为YouTube设定了一个相比我们企业的产品更低的可用性目标,因为快速发展更加重要。

  • 对于一项给定的服务的故障预期是另一个需要重点考虑的因素。我们的业务对于服务的停机时间的容忍程度有多高?持续的低故障率或者偶尔发生的全网中断哪一个会更糟糕?这两种类型的故障可能会导致绝对数量上完全相同的错误被返回,但可能对于业务的影响相差很大。下面这个例子说明了一个提供私人信息的系统中自然发生的完全和部分服务中断的区别。假设有一个联系人管理应用程序,一种情况是导致用户头像显示失败的间歇性故障,另一种情况是将A用户的私人联系列表显示给B用户的故障。第一种情况显然是一个糟糕的用户体验,SRE会努力去快速地解决这个问题。然而,在第二种情况下,暴露私人数据的风险可能会破坏基本的用户信任。因此,在第二种情况下,在进行调试和事后的数据清理时,完全停止该服务更加恰当。

  • 对于Google提供的其他服务,有时候,我们可以接受计划内的常规的服务中断。几年前,Ads前端曾经就是这样的一种服务。它是广告商和网站创建者用来建立、配置、运行和监控他们的广告活动的服务。因为这项工作大部分发生在正常工作时间内,我们认为维修窗口中发生的偶然的、正常的、计划之中的故障是可以接受的,并且我们把这些故障看作计划内停机时间,而不是计划外停机时间。

使用错误预算的目的

  • 产品研发的绩效是如何很大程度通过产品研发速度体现的,这会激励员工尽可能快地创建新的代码。同时, SRE 的绩效表现取决于该服务的可靠性,这意味着SRE 会对高频率的更改提出抗议。两个团队之间的信息不对称进一步加剧了这种内在的紧张局势。产品开发者更了解编写和发布他们的代码所需的时间,而SRE则更关注服务可靠性程度(以及生产环境中的其他相关事项)。

  • 软件对故障的容忍度对意外事件的容忍程度有多高?做得太少,我们就只能设计出一个脆弱无用的产品。做得太多,我们的产品可能没有人会使用(但运行非常稳定)。

  • 测试新发布的代码的最好做法就是在一个典型工作负载的服务子集中进行测试,这种做法通常被称为金丝雀测试。

  • 一项决策越是基于数据做出的,常常就越好。

  • 错误预算提供了一个明确的、客观的指标来决定服务在一个单独的季度中能接受多少不可靠性。这个指标在SRE与产品研发部门的谈判中将政治因素排除。

  • 我们的实际做法如下:● 产品管理层定义一个SLO,确定一项服务在每个季度预计的正常运行时间。● 实际在线时间是通过一个中立的第三方来测算的:我们的监控系统。● 这两个数字的差值就是这个季度中剩余的不可靠性预算。● 只要测算出的正常在线时间高于SLO,也就是说,只要仍然有剩余的错误预算,就可以发布新的版本。

  • 错误预算的主要好处就是它能够激励产品研发和SRE一起找出创新和可靠性之间合理的平衡点。

  • 只要系统符合SLO,就可以继续发行新版本。如果频繁地违反SLO 导致错误预算被耗尽,那么发布就会暂停,同时需要在系统测试和开发环节投入更多资源使得系统更有弹性,以使性能得到提升。

  • 有比这种简单的开/关技术更巧妙和有效的方法:[2]例如,当SLO 违规导致错误预算接近耗尽时,将发布的速度减慢,或者回退到上一版本。

  • 例如,如果产品研发人员想要在测试上节约时间或者想要提高发布速度并且SRE 表示反对时,那么就可以通过错误预算指导决策。当预算剩余很多时,产品研发人员就可以承担更多的风险。如果预算接近耗尽,产品研发人员自身将会推动更多的测试或者放慢发布的速度,因为他们不想冒着用尽预算的风险和拖延他们的程序上线。实际上,产品开发团队这样就开始进行自我监管。他们知道预算还剩多少,并且可以控制自己的风险。(当然,这要求SRE在SLO达不到的时候有权停止程序的发布。)如果网络中断或者数据中心发生故障影响了SLO,怎么办?这样的事件也会给错误预算带来不良的影响,会使本季度剩余部分的发布将会减少。整个团队会支持这种发布频率的降低,因为每个人都有义务保障服务正常运行。

  • 利用错误预算可以同时找到制定得过高的可用性目标,显示出它们所导致的灵活性和创新速度方面的问题。如果团队无法发布新的功能,他们可以选择降低SLO(从而增加错误预算)来提高创新速度。

  • 管理服务的可靠性主要在于管理风险,而且管理风险的成本可能很高。

  • ● 100%可能永远都不是一个正确的可靠性目标:不仅是不可能实现的,而且它通常比一项服务的用户期望的可靠性大得多。我们要将服务风险和愿意承担的业务风险相匹配。● 错误预算在SRE和产品研发团队之间调整激励,同时强调共同责任。错误预算使得讨论发布速率更容易,同时可有效地减少任何关于事故的讨论。这样,多个团队可以毫无怨言地对生产环境风险度达成一致。


第4章 服务质量目标

  • 如果不详细了解服务中各种行为的重要程度,并且不去度量这些行为的正确性的话,就无法正确运维这个系统,更不要说可靠地运维了

  • 我们需要利用一些主观判断结合过去的经验以及对服务的理解来定义一些服务质量指标(SLI)、服务质量目标(SLO),以及服务质量协议(SLA)。

服务质量术语

  • SLO的选择和公布可以帮助设立用户对服务质量的预期。

  • 该策略可以应对那些没有根据的抱怨—“服务太慢了”。如果没有一个明确的SLO,用户经常会按照自己的理解设置一个服务性能的预期,即使这可能跟运维人员或者设计者所想的完全不同。这种问题可能会导致对某个服务的过度依赖—用户错误地认为这个服务会比实际情况更可靠

  • 由于真正的全球Chubby服务故障出现的频率太低,以至于其他服务负责人开始认为全球Chubby服务永远不会出故障,从而不停地将更多的服务依赖于此。
    Chubby全球服务的高可靠性实际上提供了一种安全假象,因为这些服务实际上在Chubby全球服务不可用的时候不能正常工作,不管这种情况是多么罕见。

  • 每个季度,如果真实故障没有将可用性指标降低到SLO之下,SRE会有意安排一次可控的故障,将服务停机。利用这种方法,我们可以很快找出那些对Chubby全球服务的不合理依赖,强迫服务的负责人尽早面对这类分布式系统的天生缺陷。

  • 不管某个服务是否具有SLA,定义SLI与SLO,并且用它们来管理服务质量都是很有价值的。

指标在实践中的应用

  • 只有理解用户对系统的真实需求才能真正决定哪些指标是否有用。指标过多会影响对那些真正重要的指标的关注,而选择指标过少则会导致某些重要的系统行为被忽略。一般来说,四五个具有代表性的指标对系统健康程度的评估和关注就足够了。

  • 常见的服务,根据它们的相关SLI通常会归类为以下几个大类。● 用户可见的服务系统,例如莎士比亚搜索服务的前端服务器通常关心可用性、延迟,以及吞吐量。换句话说:是否能正常处理请求?每个请求花费的时间是多少?多少请求可以被处理?● 存储系统通常强调:延迟、可用性和数据持久性。换句话说:读写数据需要多少时间?我们是否可以随时访问数据?数据是否一段时间内还能被读取?扩展讨论参见第26章。● 大数据系统,例如数据处理流水线系统,一般来说关心吞吐量和端到端延迟。换句话说:处理了多少数据?数据从输入到产出需要多少时间?(某些流水线任务还会关注某个单独处理阶段的延迟。)● 所有的系统都应该关注:正确性。是否返回了正确的回复,是否读取了正确的数据,或者进行了正确的数据分析操作。正确性是系统健康程度的一个重要指标,但是它更关注系统内部的数据,而不是系统本身,所以这通常不是SRE直接负责的。指标的收集利用某种监控系统,大部分指标数据都在服务器端被收集,例如Borgmon(具体参见第10章)或者Prometheus。或者利用某种日志分析系统,例如分析日志中HTTP 500回复所占的比例。然而,某些系统可以加入对客户端数据的收集,否则可能会错失一些不影响服务器端指标,但是对用户产生影响的问题。例如,只关注莎士比亚服务器搜索后端的延迟可能会错失由页面JavaScript脚本导致的用户可见的延迟问题。在这个例子中,度量页面在浏览器中可用的延迟是度量用户体验的一个更好的指标。

  • 平均请求延迟可能看起来很简单,但是却掩盖了一个重要的细节;很可能大部分请求都是很快的,但是长尾请求速度却很慢。

  • 大部分指标都应该以“分布”,而不是平均值来定义。

  • 响应时间的分布越分散,意味着普通用户受到长尾请求延迟的影响就越明显,这可能预示了负载过高情况下出现的排队问题。

  • 一般来说,SRE更倾向于分析一组数据的百分比分布,而非其算术平均值。长尾效应比算术平均值更有特点,使用百分比分布能够更清晰地进行分析。因为计算机系统的本身特质决定,数据是具有特定分布特点的 ——例如,请求延迟必须大于0,同时如果超时设置为1000ms,则不可能有成功请求超过这个时间。因此,我们不能假设算术平均值和中位数是相等的——它们甚至可能相差甚远!

  • 汇总间隔:每1分钟汇总一次● 汇总范围:集群中的全部任务● 度量频率:每10秒一次● 包含哪些请求:从黑盒监控任务发来的HTTP GET请求● 数据如何获取:通过监控系统获取服务器端信息得到● 数据访问延迟:从收到请求到最后一个字节被发出

目标在实践中的应用

  • 我们应该从思考(或者调研)用户最关心的方面入手,而非从现在能度量什么入手。

  • 不要仅以目前的状态为基础选择目标

  • 了解系统的各项指标和限制非常重要,但是仅仅按照当前系统的标准制定目标,而不从全局出发,可能会导致团队被迫长期运维一个过时的系统,没有时间去推动架构重构等任务。

  • 避免绝对值虽然要求系统可以在没有任何延迟增长的情况下无限扩张,或者“永远”可用是很诱人的,但是这样的要求是不切实际的。就算有一个系统能够做到这一点,它也需要花很长时间来设计和构建,同时运维也很复杂—最关键的是,这可能比用户可以接受的(甚至是很开心地接受的)标准要高太多。

  • 不要追求完美我们可以随着时间流逝了解系统行为之后优化SLO的定义。刚开始可以以一个松散的目标开始,逐渐收紧。这比一开始制定一个困难的目标,在出现问题时放松要好得多。

  • 如果服务一切正常,可能力量应该花在其他的优先级上,例如消除技术债务、增加新功能,或者引入其他产品等。

个人总结

  1. 没有根据的抱怨—“服务太慢了”;指标制定
  2. 大部分指标都应该以“分布”,而不是平均值来定义。
  3. 如果服务一切正常,可能力量应该花在其他的优先级上,例如消除技术债务、增加新功能,或者引入其他产品等。

第5章 减少琐事

  • 如果系统正常运转中需要人工干预,应该将此视为一种Bug。

  • “正常”的定义会随系统的进步而不断改变。要把更多的时间花费在长期项目研发上而非日常运维中。因为术语日常运维可能会被误解,我们在这里使用一个专门的词语——琐事(toil)。

琐事的定义

  • 琐事不仅仅代表“我不喜欢做的工作”。

  • 到底什么是琐事?琐事就是运维服务中手动性的,重复性的,可以被自动化的,战术性,没有持久价值的工作。而且,琐事与服务呈线性关系的增长。并不是每件琐事都有以上全部特性,但是,每件琐事都满足下列一个或多个属性:手动性

  • 例如手动运行脚本以便自动执行一些任务。运行一个脚本可能比手动执行脚本中的每一步要快,但具体运行脚本所花费的手动的时间(而非脚本所需要的运行时间)应该被认为是琐事。重复性的如果某件事是第一次做,甚至第二次做,都不应该算作琐事。琐事就是不停反复做的工作。如果你正在解决一个新出现的问题或者寻求一种新的解决办法,不算作琐事。

  • 可以被自动化的如果计算机可以和人类一样能够很好地完成某个任务,或者通过某种设计变更来彻底消除对某项任务的需求,这项任务就是琐事。如果主观判断是必需的,那么很大程度上这项任务不属于琐事。[8]

  • 战术性的琐事是突然出现的、应对式的工作,而非策略驱动和主动安排的。处理紧急警报是琐事。我们可能永远无法完全消除这种类型的工作,但我们必须继续努力减少它。没有持久价值如果在你完成某项任务之后,服务状态没有改变,这项任务就很可能是琐事。如果这项任务会给服务带来永久性的改进,它就不是琐事。一些繁重的工作—比如挖掘遗留代码和配置并且将它们清理出去也不是琐事。与服务同步线性增长如果在工作中所涉及的任务与服务的大小、流量或用户数量呈线性增长关系,那这项任务可能属于琐事。一个良好管理和设计的服务应该至少可以应对一个数量级的增长,而不需要某些一次性工作(例如增加资源)之外的额外工作。

为什么琐事越少越好

  • SRE的一个公开目标是保持每个SRE的工作时间中运维工作(即琐事)的比例低于50%。SRE至少花50%的时间在工程项目上,

  • 以减少未来的琐事或增加服务功能。增加服务功能包括提高可靠性、性能,或利用率,同时也会进一步消除琐事。

什么算作工程工作

  • 工程工作通常是有创新性和创造性的,着重通过设计来解决问题,解决方案越通用越好。

琐事繁多是不是一定不好

  • 琐事不会总是让每个人都不开心,特别是不太多的时候。已知的和重复性的工作有一种让人平静的功效。完成这些事可以带来一种满足感和快速胜利感。琐事可能是低风险低压力的活动,有些员工甚至喜欢做这种类型的工作。

  • 琐事的存在并不总是坏事,但是每个人都必须清楚,在SRE所扮演的角色中,一定数量的琐事是不可避免的,这其实是任何工程类工作都具有的特点。少量的琐事存在不是什么大问题。但是一旦琐事的数量变多,就会有害了。如果琐事特别繁重,那就应该非常担忧,大声抱怨。在许多琐事有害的原因中,有如下因素需要考虑:职业停滞如果花在工程项目上的时间太少,你的职业发展会变慢,甚至停滞。Google确实会奖励做那些脏活累活的人,但是仅仅是该工作是不可避免,并有巨大的正面影响的时候才会这样做。没有人可以通过不停地做脏活累活满足自己的职业发展。

  • 士气低落每个人对自己可以承担的琐事限度有所不同,但是一定有个限度。过多的琐事会导致过度劳累、厌倦和不满。另外,牺牲工程实践而做琐事会对SRE组织的整体发展造成损害。

  • 原因如下:造成误解。我们努力确保每个SRE以及每个与SRE一起工作的人都理解SRE是一个工程组织。如果个人或者团队过度参与琐事,会破坏这种角色,造成误解。进展缓慢琐事过多会导致团队生产力下降。如果SRE团队忙于为手工操作和导出数据救火,新功能的发布就会变慢。开创先例如果SRE过于愿意承担琐事,研发同事就更倾向于加入更多的琐事,有时候甚至将本来应该由研发团队承担的运维工作转给SRE来承担。其他团队也会开始指望SRE接受这样的工作,这显然是不好的。促进摩擦产生即使你个人对琐事没有怨言,你现在的或未来的队友可能会很不开心。如果团队中引入了太多的琐事,其实就是在鼓励团队里最好的工程师开始寻找其他地方提供的更有价值的工作。违反承诺那些为了项目工程工作而新入职的员工,以及转入SRE的老员工会有被欺骗的感觉,这非常不利于公司的士气。

小结

  • 如果我们都致力于每一周通过工程工作消除一点琐事,就可以持续性地整顿服务。我们就可以将更多的力量投入到扩大服务规模的工程工作上去,或者是进行下一代的服务的架构设计,又或者是建立一套跨SRE使用的工具链。让我们多创新,少干琐事吧!

个人总结

  • 琐事应该尽量自动化,把时间创造性的事情上!

为什么要监控

  • 紧急警报的处理会占用员工的宝贵时间。如果该员工正在工作时间段,该警报的处理会打断他原本的工作流程。如果该员工正在家,紧急警报的处理则会影响他的个人生活,甚至是把他从睡眠中叫醒。当紧急警报出现得太频繁时,员工会进入“狼来了”效应,怀疑警报的有效性甚至忽略该警报,有的时候在警告过多的时候甚至会忽略掉真实发生的故障。由于无效信息太多,分析和修复可能会变慢,故障时间也会相应延长。高效的警报系统应该提供足够的信息,并且误报率非常低。

对监控系统设置合理预期

  • 但是监控系统中最重要的一点就是整个“生产故障,人工处理紧急警报,简单定位和深入调试”过程必须要保持非常简单,必须能被团队中任何一个人所理解。

现象与原因

  • 监控系统应该解决两个问题:什么东西出故障了,以及为什么出故障。

  • “现象”和“原因”的区分是构建信噪比高的监控系统时最重要的概念

黑盒监控与白盒监控

  • 黑盒监控是面向现象的,代表了目前正在发生的—而非预测会发生的—问题,即“系统现在有故障”。白盒监控则大量依赖对系统内部信息的检测,如系统日志、抓取提供指标信息的HTTP节点等。白盒监控系统因此可以检测到即将发生的问题及那些重试所掩盖的问题等。

  • 这里应该注意,在一个多层系统中,某一个服务的现象是另外一个服务的原因。

  • 白盒监控有时是面向现象的,有时是面向原因的,这取决于白盒系统所提供的信息。

  • 黑盒监控可以保证系统只在某个问题目前正在发生,并且造成了某个现象时才会发出紧急警报。

4个黄金指标

  • 监控系统的4个黄金指标分别是延迟、流量、错误和饱和度(saturation)。

  • 延迟增加是饱和度的前导现象。99% 的请求延迟(在某一个小的时间范围内,例如一分钟)可以作为一个饱和度早期预警的指标。

  • 如果我们度量所有这4个黄金指标,同时在某个指标出现故障时发出警报(或者对于饱和度来说,快要发生故障时),能做到这些,服务的监控就基本差不多了。

关于长尾问题

  • 区分平均值的“慢”和长尾值的“慢”的一个最简单办法是将请求按延迟分组计数(可以用来制作直方图):延迟为0~10ms之间的请求数量有多少,30~100ms之间,100~300ms之间等。

度量指标时采用合适的精度

  • 如果我们的监控目标需要高精度数据,但是却不需要极低的延迟,可以通过一些内部采样机制外部汇总的方式降低成本。

  • 这种方式使我们可以观测到短暂的CPU热点,但是又不需要为此付出高额成本进行收集和保留高精度数据

简化,直到不能再简化

  • 那些不常用的数据收集、汇总,以及警报配置应该定时删除(某些SRE团队的标准是一个季度没有用到一次即将其删除)。

将上述理念整合起来

  • ● 每当收到紧急警报时,应该立即需要我进行某种操作。每天只能进入紧急状态几次,太多就会导致“狼来了”效应。● 每个紧急警报都应该是可以具体操作的。● 每个紧急警报的回复都应该需要某种智力分析过程。如果某个紧急警报只是需要一个固定的机械动作,那么它就不应该成为紧急警报。● 每个紧急警报都应该是关于某个新问题的,不应该彼此重叠。

小结

  • E-mail警报的价值通常极为有限,很容易变成噪声。我们应该倾向于构建一个良好的监控台页面,直接显示所有的非紧急的异常情况。

个人总结

  1. 避免太多“紧急警报”造成“狼来了”效应
  2. 监控系统应该解决两个问题:什么东西出故障了,以及为什么出故障。
  3. 监控系统的4个黄金指标分别是延迟、流量、错误和饱和度(saturation)。

自动化的价值

  • 任何一个人或者一群人执行数百次动作时,不可能保证每次都用同样的方式进行:没有几个人能像机器一样永远保持一致。这种不可避免的不一致性会导致错误、疏漏、数据质量的问题和可靠性问题。在这个范畴内—一致性地执行范围明确、步骤已知的程序—是自动化的首要价值。

自动化的应用案例

  • 广泛使用的工具有Puppet、Chef、cfengine,甚至 Perl都提供了自动化完成特定任务的方法,主要区别在于对帮助进行自动化的组件的抽象层次不同。

可靠性是最基本的功能

  • 民航[20]或工业应用中—经常会指出高效的自动化的缺点[21]:随着时间的推移,操作员与系统的有用的、直接接触会逐渐减少,因为自动化会覆盖越来越多的日常活动。不可避免的,当自动化系统出现问题时,操作员将无法成功地操作该系统。

小结

  • 团队应该在开发流程开始时就留出一定资源进行发布工程工作。尽早采用最佳实践和最佳流程可以降低成本,以免未来重新改动这些系统。

个人总结

  1. 自动化很重要,但也要避免自动化工具出问题时,人工无法处理

第9章 简单化

  • 可靠性只有靠对最大程度的简化不断追求而得到。

  • 软件系统本质上是动态的和不稳定的。[25]只有真空中的软件系统才是永远稳定的。如果我们不再修改代码,就不会引入新的Bug。如果底层硬件或类库永远不变,这些组件也就不会引入Bug。如果冻结当前用户群,我们将永远不必扩展系统。事实上,一个对SRE管理系统的方法不错的总结是:“我们的工作最终是在系统的灵活性和稳定性上维持平衡。”

乏味是一种美德

  • 必要复杂度是一个给定的情况所固有的复杂度,不能从该问题的定义中移除,而意外复杂度则是不固定的,可以通过工程上的努力来解决。
  • 例如,编写一个Web服务器需要处理快速提供Web页面的必要复杂度。但是,如果我们用Java编写该服务器,试图减少GC的影响就可能会引入意外复杂度。

我绝对不放弃我的代码

  • 那些由于功能开关没有启用而没有被执行的代码,就像一个定时炸弹一样等待爆炸,正如Knight Capital的痛苦经历
  • 审查代码以确保它确实符合商业目标,定期删除无用代码,并且在各级测试中增加代码膨胀检测。

“负代码行”作为一个指标

  • 我曾经做过的一些最令人满意的编码工作就是删除了数千行已经没用的代码。

最小 API

  • 法国诗人 Antoine de Saint Exupery 曾写道,“不是在不能添加更多的时候,而是没有什么可以去掉的时候,才能达到完美。”
    (参见文献[Sai39])这个原则同样适用于软件的设计和构建。API是这个规则应该遵循的一个清晰的例子。

  • 书写一个明确的、最小的API 是管理软件系统管理简单性必要的部分。我们向API消费者提供的方法和参数越少,这些API就越容易理解,我们就能用更多的精力去尽可能地完善这些方法。同时,一个反复出现的主题是:有意识地不解决某些问题可以让我们能够更专注核心问题,使得我们已有的解决方案更好。在软件工程上,少就是多!一个很小的,很简单的API通常也是一个对问题深刻理解的标志。

个人总结

  1. 我们的工作最终是在系统的灵活性和稳定性上维持平衡
  2. 必要复杂度是业务固有的,而意外复杂度则是不固定的,可以通过工程上的努力来解决
  3. “不是在不能添加更多的时候,而是没有什么可以去掉的时候,才能达到完美。”

第Ⅲ部分 具体实践

  • 正确的解决方案不一定是当场把问题一次性修复好,而可以靠降低系统准确度、关闭一些不重要的功能,或者将用户流量导向其他没有问题的任务实例等手段暂时缓解问题。解决方案的细节肯定是和每个服务和团队相关的。但是如何有效地应对紧急问题的方法论是每个团队都适用的。

第10章 基于时间序列数据进行有效报警

  • 一个大型系统不应该要求运维人员持续关注其中使用的无数个小组件,而是应该自动汇总所有的信息,自动抛弃其中的异常情况。监控系统应该主要从高级服务质量目标层面进行报警,但是也应该保持足够的粒度,可以追踪到某个具体组件。

Borgmon的起源

  • Prometheus 都是开源软件中与Borgmon 基于时间序列数据报警理念类似的系统。
  • Prometheus[3]与Borgmon十分类似,尤其是当你对比其中的规则计算语法时。变量收集和规则计算的理念在所有这些项目中都很类似。希望借助这些工具,读者可以自行试验、部署本章内描述的一些想法。

黑盒监控

  • 白盒监控只能看到已经接收到的请求,并不能看到由于DNS故障导致没有发送成功的请求,或者是由于软件服务器崩溃而没有返回的错误
  • 探针程序使用应用级别的自动请求探测目标是否成功返回。

配置文件的维护

  • Borgmon提供一种类似于宏的模板机制,允许用户创造出一些可重用的规则库,进一步减小了配置文件中的重复程度,降低配置文件出错的可能性。

个人总结

  1. Prometheus
  2. 黑盒监控(类似APM)
  3. 保证监控系统的维护成本与服务的部署规模呈非线性相关增长是非常关键的。

  • SRE团队和纯运维团队十分不一样的地方在于,SRE团队非常强调用工程化手段来应对运维问题。而这些运维问题,当达到一定规模时,也确实只有采用软件工程化手段才能解决。

  • 我们为SRE花在纯运维事务上的时间设立了50%的上限。SRE至少要花50%的时间进行工程项目研发,以便能够研发出更好的自动化和服务优化手段来更好地服务整个业务。

安全感

  • 现代理论研究指出,在面临挑战时,一个人会主动或非主动(潜意识)地选择下列两种处理方法之一(参见文献[Kah11]):● 依赖直觉,自动化、快速行动。● 理性、专注、有意识地进行认知类活动。

  • 当处理复杂系统问题时,第二种行事方式是更好的,可能会产生更好的处理结果,以及计划更周全的执行过程。为了确保on-call工程师可以保持在第二种处理方式范围内,我们必须要减轻on-call所带来的压力感。

  • 在应急事故处理过程中,凭直觉操作和快速反应(例如服务出现问题就先重启服务器)看起来都是很有用的方法,但是这些方法都有自己的缺点。直觉很可能是错误的,而且直觉一般都不是基于明确的数据支持的。因此,在处理问题的过程中,on-call工程师很有可能由于凭直觉去解释问题产生的原因而浪费宝贵的时间。快速反应主要是由习惯而产生的,习惯性的快速反应的动作后果一般都没有经过详细考虑,这可能会将灾难扩大。在应急事件处理过程中,最理想的方法论是这样的:在有足够数据支撑的时候按步骤解决问题,同时不停地审视和验证目前所有的假设。

  • 让on-call SRE知道他们可以寻求外部帮助,对减轻on-call压力也很有帮助。最重要的资源有:● 清晰的问题升级路线。● 清晰定义的应急事件处理步骤。● 无指责,对事不对人的文化氛围(参见文献[Loo10]和[All12])

  • 最后,在应急事件处理结束时,仔细评估哪些地方有问题,哪些地方做得好是非常关键的。而且应该采取措施避免再次发生同样的问题。SRE团队必须在大型应急事件发生之后书写事后报告,详细记录所有事件发生的时间线。这些事后报告都是对事不对人的,为日后系统性地分析问题产生的原因提供了宝贵数据。犯错误是不可避免的,软件系统应该提供足够的自动化工具和检查来减少人为犯错误的可能性(参见文献[Loo10])。

避免运维压力过大

  • 控制on-call工程师收到的针对同一起事故的报警总数也很重要。有的时候,一个异常情况可能会触发多条报警,所以合理地分组汇总报警信息是很重要的。

  • 奸诈的敌人—运维压力不够虽然给一个非常安静的系统on-call值班是很幸福的事情,但是当一个系统太稳定,或者SRE on-call的周期太长会发生什么呢?SRE团队运维压力不够也是一个不良现象。长时间不操作生产环境会导致自信心问题,包括自信心太强以及自信心不够。这些现象只有在下一次发生问题时,才会显现出来。为了避免这种问题,应该控制SRE团队的大小,保证每个工程师每个季度至少参与oncall一次,最好两次。这样可以保证团队成员有足够的生产环境操作经验。“命运之轮”(见第28章)也是一种有助提高技能和共享知识的团队活动。同时,Google每年举办一次持续数天的全公司灾难恢复演习(DiRT),针对理论性和实际性的灾难进行演练。

个人总结

  1. 理性,逐步验证解决
  2. 清晰定义的应急事件处理步骤和升级流程
  3. 定期演练避免一旦突发时生疏

第12章 有效的故障排查手段

  • 值得警惕的是,理解一个系统应该如何工作并不能使人成为专家。只能靠调查系统为何不能正常工作才行。

  • 新手们常常不能有效地进行故障排查,是因为这个过程理想情况下同时需要两个条件。

    • 1.对通用的故障排查过程的理解(不依靠任何特定系统)。2.对发生故障的系统的足够了解。

理论

  • 我们不断提出一个造成系统问题的假设,进而针对这些假设进行测试和排除。

实践

  • 有效的故障报告应该写清预期是什么,实际的结果是什么,以及如何重现

  • 合理判定一个问题的严重程度需要良好的工程师判断力,同时,也需要一定程度的冷静。

  • 在大型问题中,你的第一反应可能是立即开始故障排查过程,试图尽快找到问题根源。这是错误的!不要这样做。正确的做法应该是:尽最大可能让系统恢复服务。

  • 这可能需要一些应急措施,比如,将用户流量从问题集群导向其他还在正常工作的集群,或者将流量彻底抛弃以避免连锁过载问题,或者关闭系统的某些功能以降低负载。缓解系统问题应该是你的第一要务。在寻找问题根源的时候,不能使用系统的用户并没有得到任何帮助。当然,快速定位问题时仍应该及时保存问题现场,比如服务日志等,以便后续进行问题根源分析时使用。

  • 这种方法也同样适用于计算机系统:如果一个Bug有可能导致不可恢复的数据损坏,停止整个系统要比让系统继续运行更好。对新SRE来说,这个想法是反直觉,令人不安的。对以前曾经有过产品研发背景的人来说更有点难以接受

  • 在日志中支持多级记录是很重要的,尤其是可以在线动态调整日志级别。

最后一个修改

  • 计算机系统有惯性存在:我们发现,一个正常工作的计算机系统会维持工作,直到某种外力因素出现,例如一个配置文件的修改,用户流量的改变等。检查最近对系统的修改可能对查找问题根源很有帮助。

  • 某项测试可能产生有误导性的结果。例如:防火墙规则可能只允许某些特定IP访问,所以在你的工作机上ping 数据库可能会失败,而实际从应用服务器上ping数据库可能是成功的。

  • 将你的想法明确地记录下来,包括你执行了哪些测试,以及结果是什么。

  • 尤其是当你处理更加复杂的问题时,良好的文档可以让你记住曾经发生过什么,可避免重复执行。

神奇的负面结果

  • 我们在设计实验的时候应该将可能的负面结果考虑在内,因为一个可靠的、应用广泛的负面结果对其他人更有帮助。

  • 公布负面结果有助于提升整个行业的数据驱动风气。

  • 公布结果。如果你对一项测试的结果感兴趣,那么很有可能其他人也感兴趣。当你公布结果的时候,其他人不需要再重新设计和运行一套类似试验。

  • 更多的试验压根没有公布数据,因为很多人错误地认为负面结果意味着没有价值。

使故障排查更简单

  • 使故障排查更简单有很多方法可以简化和加速故障排查过程。可能最基本的是:● 增加可观察性。在实现之初就给每个组件增加白盒监控指标和结构化日志。● 利用成熟的、观察性好的组件接口设计系统。

个人总结

  1. 理解一个系统应该如何工作并不能使人成为专家。只能靠调查系统为何不能正常工作才行。
  2. 有效的故障报告应该写清预期是什么,实际的结果是什么,以及如何重现
  3. 先让系统恢复服务,再排查问题,尽量保存现场相关数据或日志等。
  4. 发生问题时,留意“最后一个修改”
  5. 排查问题后文档记录,避免以后重复执行
  6. 实验的负面结果也有价值

第13章 紧急事件响应

  • 东西早晚要坏的,这就是生活。
  • 将紧急事件的处理过程变成一个学习机会。

当系统出现问题时怎么办

  • 当系统出现问题时怎么办首先,别惊慌失措!这不是世界末日,你也并不是一个人在战斗!作为一个专业人士,你已经接受过如何正确处理这样的情况的训练。通常来说,我们处理的问题一般不涉及真实的物理危险,只是计算机系统出现了问题。最差的情况下,也只是半个互联网都停止运转了(Google网络规模已经是全球排名前三)。所以请深吸一口气,慢慢来。

测试导致的紧急事故

  • 这次测试带来的严重后果促使开发者对代码类库中的问题进行了一次彻底的修复,同时制定了一个周期性测试机制来保证这类严重问题不再重现。

  • 由于我们没有在测试环境中测试回滚机制,没有发现这些机制其实是无效的,导致了事故总时长被延长了。我们现在要求在大型测试中一定先测试回滚机制。

变更部署带来的紧急事故

  • 在这些带外通信系统之外,Google还有命令行工具和其他的访问方式确保我们能够在其他条件无法访问的时候进行更新和变更回滚。这些工具和访问方式在这次事故中起到了重大作用,但是工程师应该更加频繁地测试,以便更为熟悉它们。

  • 具有讽刺意味的是,我们本来计划在下个季度提高部署测试流程和自动化的优先级。这次事故的发生直接将他们的优先级提高了,同时强调不管风险看起来有多小,也要经过严格完整的部署测试。

流程导致的严重事故

  • 我们在机器管理自动化上投入了大量的时间和精力,以使我们能很轻松地在整个集群里运行、停止和重新配置大量任务。但是当意外来临时,自动化的效率有时可能是很可怕的。

所有的问题都有解决方案

  • 时间和经验一再证明,系统不但一定会出问题,而且会以没有人能够想到的方式出问题。Google学到的最关键的一课是,所有的问题都有对应的解决方案,虽然对一个面对着疯狂报警的工程师来说,它可能不是那么显而易见。如果你想不到解决办法,那么就在更大的范围内寻求帮助。找到更多团队成员,寻求更多的帮助,做你需要做的一切事情,但是要快。最高的优先级永远是将手头问题迅速解决。很多时候,触发这个事故的人对事故了解得最清楚,一定要充分利用这一点。

  • 非常重要的是,一旦紧急事件过去之后,别忘了留出一些时间书写事后报告。

向过去学习,而不是重复它

  • 向过去学习,而不是重复它为事故保留记录没有什么比过去的事故记录是更好的学习资料了。历史就是学习其他人曾经犯的错误。在记录中,请一定要诚实,一定要事无巨细。尤其重要的是,提出关键的问题。时刻寻找如何能在战术及战略上避免这项事故的发生。公布和维护事后报告,确保全公司的每个人都能从中学到你所学到的知识。在事故结束后,确保自己和其他人切实完成事故中总结的待办事项。这样能够避免未来再次发生以同样的因素触发的同样的事故。一旦开始仔细学习过去的事故,我们就能更好地避免未来的事故。

  • 鼓励主动测试面对失败,理论和实践是两个完全不同的领域。直到你的系统真的失败的那一刻,你并不真的了解它,以及依赖它的系统,或者它的用户会如何应对。不要预设任何假设,也不要依赖任何没有经过测试的假设。你是希望这个系统在星期六凌晨两点钟,公司大部分同事都还在参加黑森林中的团建时出现故障,还是希望和最可靠和最聪明的同事在一起仔细监控着它们上周详细评审过的测试时出现故障呢?

一次流程管理良好的事故

  • Josephine上线之后,发现Robin也自愿加入进来。Sabrina 提醒Robin和Josephine,他们应该优先处理任何Mary交给他们的工作,同时他们必须告知Mary他们进行的任何操作。Robin和Josephine 通过阅读实时事故状况文档,很快熟悉了目前的情况。到下午5点时,Sabrina开始寻找接下来负责处理事故的替代人,因为她和她的同事们快要到下班时间了。她更新了事故状况文档。在5点45分时,她召开了一个简短的电话会议,让所有人都清楚目前的情况。在6点时,他们与他们的姐妹团队(另外一个办公室同团队的人)进行了职责交接。Mary第二天早上回到公司时,发现她身处大西洋另一端的同事已经定位了具体问题,缓解了问题,同时将事故做了了结,已经开始写事后总结了。问题解决了!她冲了点咖啡,开始规划一些结构性改变,使得这类问题在未来不会再重现。

什么时候对外宣布事故

  • 先宣布事故发生,随后找到一个简单解决方案,然后宣布事故结束,要比在问题已经持续几个小时之后才想起流程管理更好。应当针对事故设立一个明确的宣布条件。Google团队依靠下面几个宽松的标准——如果下面任何一条满足条件,这次事故应该被及时宣布。● 是否需要引入第二个团队来帮助处理问题?● 这次事故是否正在影响最终用户?● 在集中分析一小时后,这个问题是否依然没有得到解决?

  • 如果平时不经常使用,事故流程管理的可靠性萎缩得很快。所以怎么使工程师不忘记他们的流程管理技能呢?难道一定要制造更多事故吗?幸运的是,事故流程管理框架常常也适用于其他的跨时区、或者跨团队的常规运维变更实施。如果我们经常使用流程管理框架处理生产变更请求,那么在事故来临时,就可以很好地利用流程管理框架管理它。如果你的组织经常进行灾难恢复演习(你应该这样做!参见文献[Kir12]),事故流程管理应该包含在其中。Google经常针对之前发生的灾难进行角色扮演式演习,比如演习另外一个地区的团队处理过的问题,以更好地熟悉事故流程管理体系。

小结 !!!

  • 我们发现,通过事前准备一个事故流程管理策略,并确保平稳实施,以及经常测试,我们能够降低事故的平均恢复时间(MTTR),同时减轻处理紧急事故的人的工作压力。任何对服务可靠性关注的组织团队都会从类似策略上获得帮助。事故流程管理最佳实践划分优先级:控制影响范围,恢复服务,同时为根源调查保存现场。事前准备:事先和所有事故处理参与者一起准备一套流程。信任:充分相信每个事故处理参与者,分配职责后让他们自主行动。反思:在事故处理过程中注意自己的情绪和精神状态。如果发现自己开始惊慌失措或者感到压力难以承受,应该寻求更多的帮助。考虑替代方案:周期性地重新审视目前的情况,重新评估目前的工作是否应该继续执行,还是需要执行其他更重要或者更紧急的事情。练习:平时不断地使用这项流程,直到习惯成自然。换位思考:上次你是事故总控负责人吗?下次可以换一个职责试试。鼓励每个团队成员熟悉流程中的其他角色。

个人总结

  1. 东西早晚要坏的,这就是生活。
  2. 将紧急事件的处理过程变成一个学习机会。
  3. 在大型测试中一定先测试回滚机制。
  4. 时间和经验一再证明,系统不但一定会出问题,而且会以没有人能够想到的方式出问题。所有的问题都有对应的解决方案。
  5. 向过去学习,而不是重复它(事后报告)
  6. 事前准备一个事故流程管理策略

第15章 事后总结:从失败中学习

  • 一篇事后总结是一次事故的书面记录,包括该事故造成的影响,为缓解该事故采取的措施,事故的根本原因,以及防止未来问题重现的后续任务。

Google的事后总结哲学

  • 书写事后总结的主要目的是为了保证该事故被记录下来,理清所有的根源性问题,同时最关键的是,确保实施有效的措施使得未来重现的几率和影响得到降低,甚至避免重现。

  • 书写事后总结不是一种惩罚措施,而是整个公司的一次学习机会。但是书写事后总结的过程确实需要消耗团队的一定时间和精力,所以我们在选择上很严格。每个团队都有一些内部灵活性,但是基本的事后总结条件为:● 用户可见的宕机时间或者服务质量降级程度达到一定标准。● 任何类型的数据丢失。● on-call 工程师需要人工介入的事故(包括回滚、切换用户流量等)。● 问题解决耗时超过一定限制。● 监控问题(预示着问题是由人工发现的,而非报警系统)。

  • 在SRE的文化中,最重要的就是事后总结“对事不对人”。一篇事后总结必须重点关注如何定位造成这次事件的根本问题,而不是指责某个人或某团队的错误或者不恰当的举动。一篇对事不对人的事后总结假设所有参与事件处理的人都是善意的,他们在自己当时拥有的信息下做了正确的举动。如果因为某些“错误的”举动就公开指责或者羞辱某个人或团队,那么人们就会自然地逃避事后总结。

  • 我们不能“修好”某个人,但是可以通过改善系统和流程从而更好地协助他在设计和维护大型复杂系统时,做出更多“正确”的判断。

  • 当一次事故发生时,我们不能把事后总结当成例行公事。我们的工程师将事后总结看作一个修复问题,一个使Google变得更可靠的机会。一篇“对事不对人”的事后总结不应该简单地指责或者抱怨某个团队,而应该确实提出服务如何能够获得进步。

  • 下面是两个例子。指责“我们需要重写整个复杂后端系统。在过去三个季度中,它每周都在出问题。我们对一点一点修复它的问题已经烦透了!真的,如果我再收到一个报警,那我就自己重写了。”对事不对人“通过重写整个后端系统可能可以避免这些烦人的报警信息继续发生,目前版本的维护手册非常冗长,学习成本很高。相信通过重写,可以减少报警信息,未来的oncall工程师会感谢我们的。”

  • 最佳实践:避免指责,提供建设性意见。

  • 对事不对人的事后总结有的时候比较难写,因为事后总结的格式清晰地表明了触发事故的原因。从事后总结中排除指责的因素可以使人们在向上级汇报问题的时候更有自信。同时我们也不应该因为某个团队和个人经常写事后总结而对他们产生怀疑。一个充满相互指责风气的环境很容易让人将事故和问题掩盖起来,从而对整个组织酿成更大的灾难。

协作和知识共享

  • 最佳实践:所有的事后总结都需要评审

  • 一旦所有的事故参与者都对文档和其中的代办事项表示了肯定,这篇事后总结会被添加到该团队或者整个组织的文档汇总中。

  • 透明化的共享机制保证了每个人都可以很容易地找到和学习以前的事故。

建立事后总结文化

  • 最佳实践:公开奖励做正确事的人

  • 最佳实践:收集关于事后总结有效性的反馈

个人总结

  • 最佳实践:避免指责,提供建设性意见。
  • 一篇“对事不对人”的事后总结不应该简单地指责或者抱怨某个团队,而应该确实提出服务如何能够获得进步。
  • 一个充满相互指责风气的环境很容易让人将事故和问题掩盖起来,从而对整个组织酿成更大的灾难。

第16章 跟踪故障

  • 提高可靠性的唯一可靠的方法论是建立一个基线(baseline),同时不断跟踪改变。

  • 通过找到基础设施中造成故障最多的一部分,可以更好地知道如果提高该部分的稳定性或性能会带来多大帮助。

  • 甚至有的时候,我们需要人为制造一些宕机时间,以免给内部用户造成某种假象(通常指某些服务设计的架构已经决定发生故障会耗时很久才能解决,但是经常由于运气因素而造成非常稳定的假象。某些团队选择定期制造人为宕机时间,以避免用户过于依赖该服务)。

个人总结

  1. 定时宕机,避免把依赖当作理所当然。

第17章 测试可靠性

  • 如果你还没有亲自试过某件东西,那么就假设它是坏的。

  • 测试的数量直接取决于所服务系统的可靠性要求。随着代码的测试覆盖度上升,每次改动的不确定性和降低系统可靠度的可能性都降低了。足够的代码测试覆盖度可以让我们对系统做出更多的改动,而不会使系统可靠度下降到可接受水平之下

  • 测试和平均修复时间的关系系统通过某项测试或者一系列测试并不一定能证明系统是稳定的,但是失败的测试通常证明了系统不可靠。

软件测试的类型

  • 软件测试基本分为两大类:传统测试和生产测试。传统测试在软件开发过程中很常见,主要用来在开发过程中离线评估软件的正确性。生产测试在生产Web服务器上进行,用来评估一个已经部署的软件系统工作是否正常。

  • 单元测试单元测试(unit test)是最小、最简单的软件测试形式。这些测试用来评估某一个独立的软件单元,比如一个类,或者一个函数的正确性。这些测试不考虑包含该软件单元的整体系统的正确性。单元测试同时也是一种规范,用来保证某个函数或者模块完全符合系统对其的行为要求。单元测试经常被用来引入测试驱动开发的概念。集成测试通过独立的单元测试的软件组件被组装成大的系统组件。工程师通过在这个组件中运行一个集成测试(integration test)来检验该组件的功能的正确性。依赖注入(dependency injection),利用类似Dagger[53]这样的工具,我们可以创建出复杂依赖的mock(测试中替代真实逻辑的伪组件),用以方便地测试某个系统组件。一个常见的例子是通过依赖注入来用轻便的mock替换一个有状态的数据库,同时保持一模一样的行为特征。

  • 系统测试系统测试(system test)是一个在未部署的系统上运行的大型测试。某个组件的所有模块(Module)都会被装载到系统中(例如通过集成测试的软件服务器)。接下来工程师可以端到端地测试系统功能。系统测试包括以下几种类型。冒烟测试(smoke test)工程师在冒烟测试中可检测非常简单但是非常重要的系统行为。这是最简单的一种系统测试形式。冒烟测试有时也被称为理性测试,如果该测试不通过,那么其他更昂贵的测试可以不用运行了。性能测试(performance test)一旦冒烟测试通过,系统基本的正确性已经得到了保障。下一步通常是通过某个系统测试的变形来保证整个系统的性能自始至终保持在可接受范围内。因为系统的响应时间和资源要求可能在开发过程中大量改变,该系统必须接受某些测试以确保它不会在没人知道的情况下逐渐变慢(在发布到最终用户之前)。例如,一个程序可能随着改变开始需要32GB内存,而以前只需要8GB。或者该程序的响应时间由10ms变成了50ms,随后变成了100ms。性能测试可以保证随着时间推移系统性能不会下降,或者资源要求不会升高。回归测试(regression test)另外一种系统测试可保证之前的Bug不会重现。回归测试可以被理解为曾经发生过的,导致系统故障或产生错误信息的Bug列表。通过将这些Bug记录为系统测试或者集成测试,重构代码的工程师可以保证他们不会偶然间将他们曾经辛苦调查和修复的Bug又带回来。很重要的是,每个测试都有成本,时间成本和计算资源成本。在一个极限上,单元测试非常便宜,通常可以在毫秒级和很少的资源上(例如一个笔记本电脑上)完成。而在另一个极限上,将一个完整的软件服务器设立起来,同时包括它所有的依赖系统(或者是mock),然后运行相关的测试可能会需要很长时间—几分钟到几小时—一般还需要专属的运算资源。时刻关注这些测试的成本,是软件开发效率提升的重要因素,同时也鼓励程序员更有效地利用我们的测试资源。生产测试生产测试和一个已经部署在生产环境中的业务系统直接交互,而不是运行在密闭的测试环境中。这些测试和黑盒监控在很多地方十分类似(参见第6章),有的时候也被称为黑盒测试。生产测试对运行一个可靠的生产环境来说是必要的。

小结

  • 测试是工程师提高可靠性投入回报比最高的一种手段。

个人总结

  • 测试分类查资料总结

基于意图的容量规划

  • 向大型团队推广内部软件工具需要以下几点:● 持续的和完整的推广方案。● 用户的拥护。● 资深工程师和管理层的赞助,因为他们看到了项目的实用潜力。

使用DNS进行负载均衡

  • DNS负载均衡。最简单的方案是在DNS回复中提供多个A记录或者AAAA记录,由客户端任意选择一个IP地址使用

负载均衡:虚拟IP

  • 虚拟IP地址(VIP)不是绑定在某一个特定的网络接口上的,它是由很多设备共享的。

识别异常任务:流速控制和跛脚鸭任务

  • 从一个客户端的视角来看,某个后端任务可能处于下列任一种状态中:健康后端任务初始化成功,正在处理请求。拒绝连接后端任务处于无响应状态。这可能是因为任务正在启动或者停止,或者是因为后端正处于一种异常状态(虽然很少有后端任务在非停止状态下停止监听端口)。

  • 跛脚鸭状态后端任务正在监听端口,并且可以服务请求,但是已经明确要求客户端停止发送请求。

  • 当某个请求进入跛脚鸭状态时,它会将这个状态广播给所有已经连接的客户端。但是那些没有建立连接的客户端呢?

  • 在Google的RPC框架实现中,不活跃的客户端(没有建立TCP连接的客户端)也会定期发送UDP健康检查包。这就使跛脚鸭状态可以相对较快地传递给所有的客户端—通常在一到两个RTT周期内—无论它们处于什么状态下。

  • 允许任务处于这种半正常的跛脚鸭状态的好处就是让无缝停止任务变得更容易,处于停止过程中的任务不会给正在处理的请求返回一个错误值。能够无影响地停止一个活跃的后端任务可以让处理代码推送、设备维护活动,和机器故障问题导致的任务重启变得对用户透明。这个停止过程通常按照以下步骤进行:1.任务编排系统发送一个SIGTERM信号给该任务。2.后端任务进入跛脚鸭状态,同时请求它的所有客户端发送请求给其他后端任务。这通过SIGTERM信号处理程序中调用RPC实现中的API完成。3.任何在后端进入跛脚鸭状态时正在进行的请求(或者在进入状态之后,但是其他客户端收到通知之前)仍会继续进行。4.随着请求回复被发送回客户端,该后端任务的活跃请求逐渐降低为0。5.在配置的时间过后,该后端程序要么自己干净地退出,要么任务编排系统主动杀掉它。该时间应该被设置为一个足够大的值,以便一般的请求可以有足够的时间完成。每个服务的该数值都不同,一般来说取决于客户端的复杂程度,10s到150s是一个不错的选择。这个策略使客户端可以在后端程序进行耗时较长的初始化过程中(这时后端程序还不能服务请求)就建立连接。如果后端程序等到服务可以接受请求的时候才建立链接,就增加了一些不必要的延迟。一旦后端程序可以提供服务了,它就会主动通知所有客户端。

利用划分子集限制连接池大小

  • 利用划分子集限制连接池大小在健康管理之外,负载均衡另外要考虑的一个因素就是子集划分:限制某个客户端任务需要连接的后端任务数量。我们的RPC系统中的每个客户端都会针对后端程序维持一个长连接发送请求。这些连接通常在客户端启动的时候就建立完成,并且保持活跃状态,不停地有请求通过它们,直到客户端终止。另外一个方案是针对每个请求建立和销毁后端连接,这样会带来极大的资源成本和造成延迟问题。在极端情况下,如果某个连接闲置时间非常长,我们的RPC实现可以自动将该连接转为“不活跃”状态,转为UDP模式连接,而非TCP模式。

QPS陷阱

  • Google在多年的经验积累中得出:按照QPS来规划服务容量,或者是按照某种静态属性(认为其能指代处理所消耗的资源:例如某个请求所需要读取的键值数量)一般是错误的选择。就算这个指标在某一个时间段内看起来工作还算良好,早晚也会发生变化。有些变动是逐渐发生的,有些则是非常突然的(例如某个软件的新版本突然使得某些请求消耗的资源大幅减少)。这种不断变动的目标,使得设计和实现良好的负载均衡策略使用起来非常困难。更好的解决方案是直接以可用资源来衡量可用容量。

客户端侧的节流机制

  • 拒绝一个执行简单内存查询的请求可能跟实际执行该请求消耗内存差不多(因为这里主要的消耗是在应用层协议解析中,结果的产生部分很简单)。就算在某些情况下,拒绝请求可以节省大量资源,发送这些拒绝回复仍然会消耗一定数量的资源。

  • 当某个客户端检测到最近的请求错误中的一大部分都是由于“配额不足”错误导致时,该客户端开始自行限制请求速度,限制它自己生成请求的数量。超过这个请求数量限制的请求直接在本地回复失败,而不会真正发到网络层。

  • 我们使用一种称为自适应节流的技术来实现客户端节流。具体地说,每个客户端记录过去两分钟内的以下信息:请求数量(requests)应用层代码发出的所有请求的数量总计(指运行于自适应节流系统之上的应用代码)。请求接受数量(accepts)后端任务接受的请求数量。

重要性

  • 重要性重要性(criticality)是另外一个在全局配额和限制机制中比较有用的信息。某个发往后端的请求都会被标记为以下4类中的一种,这说明了请求的重要性。最重要 CRITICAL_PLUS为最重要的请求预留的类型,拒绝这些请求会造成非常严重的用户可见的问题。重要 CRITICAL生产任务发出的默认请求类型。拒绝这些请求也会造成用户可见的问题,但是可能没有CRITICAL_PLUS那么严重。我们要求服务必须为所有的CRITICAL和CRTICAL_PLUS流量配置相应的资源。可丢弃的SHEDDABLE_PLUS这些流量可以容忍某种程度的不可用性。这是批量任务发出的请求的默认值。这些请求通常可以过几分钟,或者几小时之后重试。可丢弃的SHEDDABLE这些流量可能会经常遇到部分不可用情况,偶尔会完全不可用。

  • 我们同时增强了RPC系统,可以自动传递重要性信息。如果后端接收到请求A,在处理过程中发出了请求B 和C给其他后端,请求B和C会使用与A相同的重要性属性。

  • 在过去一段时间内,Google内部的许多系统都逐渐产生了一种与重要性类似的属性,但是通常不能跨服务兼容。通过标准化和在RPC系统中自动传递,我们现在可以在某些特定节点处统一设置重要性属性。这意味着,我们相信依赖的服务在过载情况下可以按正确的优先级来拒绝请求,不论它们处于整个处理栈中多深的位置。于是我们一般在离浏览器或者客户端最近的地方设置优先级—通常在HTTP前端服务器上。同时,我们可以在特殊情况下在处理栈的某处覆盖优先级设置。

资源利用率信号

  • 资源利用率信号我们的任务过载保护是基于资源利用率(utilization)实现的。

  • 在多数情况下,资源利用率仅仅是指目前CPU的消耗程度(目前CPU使用量除以全部预留CPU数量)。但是在某些情况下,同时也会考虑内存的使用率。随着资源利用率的上升,我们开始根据请求的重要性来拒绝一些请求(高重要性的请求对应高阈值)。

处理过载错误

  • 决定何时重试

连接造成的负载

  • 连接造成的负载连接造成的负载是最后一个值得一提的因素。有时候我们仅仅考虑后端处理接收的请求所造成的负载(这也是用QPS来建模负载的一个问题),然而却忽略了其他因素,比如维护一个大型连接池的CPU和内存成本,或者是连接快速变动的成本。这样的问题在小型系统中可以忽略不计,但是在大型RPC系统中很快就会造成问题。

  • 我们的RPC协议需要不活跃的客户端定期执行健康检查。当某个连接空闲一段可配置的时间后,客户端放弃TCP连接,转为UDP健康检查。不幸的是,这种行为会对大量请求率很低的客户端造成问题:健康检查需要比实际处理请求更多的资源。

  • 通过仔细调节连接参数(如,大幅降低健康检查频率)或者动态创建和销毁连接可以优化这些场景。

小结

  • 利用不同的技术手段(确定性算法、加权轮询、客户端侧的节流、用户配额等)更平均地将负载分散到数据中心中。然而这些手段都依赖于在分布式下的状态传递机制。虽然大部分情况下都表现良好,但是在真实情况下,某些应用遇到了一些困难。所以,我们认为保护某个具体任务,防止过载是非常重要的。简单地说:一个后端任务被配置为服务一定程度的流量,不管多少额外流量被指向这个任务,它都应该保证服务质量。

  • 一个常见的错误是认为过载后端应该拒绝和停止接受所有请求。然而,这个假设实际上是与可靠的负载均衡目标相违背的。我们实际上希望客户端可以尽可能地继续接受请求,然后在有可用资源时才处理。某个设计良好的后端程序,基于可靠的负载均衡策略的支持,应该仅仅接受它能处理的请求,而优雅地拒绝其他请求。

个人总结

  • RPC框架设计的重要参考 TODO
  • 如何优雅地重试:https://mp.weixin.qq.com/s/6IkTnUbBlHjM3GM_bT35tA 这个实现类似
    策略 说明
    重试熔断 请求失败 / 成功 > 0.1 时停止重试
    链路上传错误标志 下层重试失败后上传错误标志,上层不再重试
    链路下传重试标志 重试请求特殊标记,下层对重试请求不会重试
    DDL 当剩余时间不够时不再发起重试请求
    框架熔断 微服务框架本身熔断、过载保护等机制也会影响重试效果


第22章 处理连锁故障

  • 如果请求没有成功,以指数型延迟重试。

  • 连锁故障是由于正反馈循环(positive feedback)导致的不断扩大规模的故障。[72]连锁故障可能由于整个系统的一小部分出现故障而引发,进而导致系统其他部分也出现故障。例如,某个服务的一个实例由于过载出现故障,导致其他实例负载升高,从而导致这些实例像多米诺骨牌一样一个一个全部出现故障。

连锁故障产生的原因和如何从设计上避免

  • RPC超时服务器过载时,对客户端RPC的回复会变慢,最终会超过客户端所设置的超时时间。这会导致服务器对请求实际进行的处理都被浪费了,而客户端可能会重试RPC,造成更严重的过载。

  • CPU缓存效率下降CPU使用得越多,任务被分配到多个CPU核心上的几率越大,从而导致CPU核心本地缓存的失效,进而降低CPU处理的效率。

  • 缓存命中率下降可用内存的减少可能会导致应用层缓存的命中率降低,导致向后端发送更多的RPC,可能会导致后端任务过载。

  • 线程线程不足可能会导致错误或者导致健康检查失败。如果服务器为此增加更多线程,这些线程可能会占用更多内存。在极端情况下,线程不足可能会导致进程ID数不足(Linux的进程ID数是有限的)。文件描述符文件描述符(file descriptor)不足可能会导致无法建立网络连接,进而导致健康检查失败。

  • 假设如下场景:1.某Java前端服务器GC参数没有被调优。2.在高负载(但是在期待范围内)情况下,前端由于GC问题导致CPU不足。3.CPU不足导致请求处理变慢。4.同时处理的请求增多导致内存使用上升。5.内存压力上升,同时由于固定内存分配比例的原因,用于缓存的内存数量减少。6.缓存数量降低意味着缓存中键值数量下降,从而导致命中率下降。7.缓存命中率下降导致更多的请求被发往后端进行处理。8.后端服务器CPU或者线程不足。9.CPU不足导致健康检查失败,从而触发了连锁故障。在上述这个复杂情景下,发生故障时可能没有时间仔细分析因果关系。尤其是在前端和后端由不同团队运维时,判断后端崩溃是由于前端缓存命中率下降可能非常困难。

  • 会自动避免产生错误的软件服务器的负载均衡策略会将这个问题加剧—某几个后端任务产生了错误,会导致负载均衡器不再向它们发送请求,进而使得其余软件服务器的负载上升,从而再次触发滚雪球效应。

防止软件服务器过载

  • 防止软件服务器过载下面描述了避免过载的几种策略,大致以优先级排序。使用负载压力测试得出服务器的极限,同时测试过载情况下的失败模式

  • 提供降级结果给用户返回低质量的,但是更容易计算的结果。

  • 在过载情况下主动拒绝请求软件服务器应该保护自己不进入过载崩溃状态。

上层系统应该主动拒绝请求

  • 在反向代理层,通过针对请求的某种特性进行数量限制(如IP地址),来缓解和避免拒绝服务攻击,避免攻击性客户端的影响。● 在负载均衡器层,在服务进入全局过载时主动丢弃请求。

  • 进行容量规划好的容量规划可以降低连锁反应发生的可能性。容量规划应该伴随着性能测试进行,以确定可能导致服务失败的负载程度。

  • 进行容量规划只能减少触发连锁反应的可能性,但是并不能完全避免。当一个计划内或者计划外的事件导致大部分集群容量同时下线时,连锁反应是不可避免的。负载均衡问题、网络分区事件,或者突发性流量增长,都会创造意料之外的负载问题。有些系统可以根据需要动态增加容量,这可能防止过载发生,但是适当地进行容量规划还是必要的。

  • 在这个理想化的情景下,只有在请求速率超过单个请求的处理速率时,请求才会进入队列,这种情况会导致线程池和队列的同时饱和。

  • 对一个流量基本稳定的服务来说,队列长度比线程池大小更小会更好(如 50% 或更小)。当服务处理速度无法跟上请求到达速率时,尽早拒绝请求会更好。

  • 流量抛弃和优雅降级流量抛弃(load shedding)是指在软件服务器临近过载时,主动抛弃一定量的负载。

  • 一种简单的流量抛弃实现方式是根据CPU使用量、内存使用量及队列长度等进行节流。

  • 简化了一些细节,[75]但是这里很好地展现了重试是如何摧毁一个系统的。注意临时性的过载升高,或者使用量的缓慢增加都有可能造成这种情况。

  • 一定要使用随机化的、指数型递增的重试周期。

  • 如果重试不是随机分布在重试窗口里的,那么系统出现的一个小故障(某个网络问题)就可能导致重试请求同时出现,这些请求可能会逐渐放大

  • 限制每个请求的重试次数。不要将请求无限重试。● 考虑使用一个全局重试预算。例如,每个进程每分钟只允许重试60次,如果重试预算耗尽,那么直接将这个请求标记为失败,而不真正发送它。这个策略可以在全局范围内限制住重试造成的影响,容量规划失败可能只是会造成某些请求被丢弃,而不会造成全球性的连锁故障。

  • 从多个视角重新审视该服务,决定是否需要在某个级别上进行重试。这里尤其要避免同时在多个级别上重试导致的放大效应:高层的一个请求可能会造成各层重试次数的乘积数量的请求。如果服务器由于过载不能提供服务,后端、前端、JavaScript层各发送3次重试(总计4次请求),那么一个用户的动作可能会造成对数据库的64 次请求(43)。在数据库由于过载返回错误时,这种重试只会加重问题。

  • 使用明确的返回代码,同时详细考虑每个错误模式应该如何处理。例如,将可重试错误和不可重试错误分开。

  • 设置一个截止时间通常是明智的。不设置截止时间,或者设置一个非常长的截止时间通常会导致某些短暂的、已经消失的问题继续消耗服务器资源,直到重启。

  • 截止时间设置得太长可能会导致框架中的高层级部分由于低层级的问题而持续消耗资源。截止时间设置得太短可能会导致某些比较重型的请求持续失败。恰当的截止时间设置,需要在多个限制条件中选择一个平衡点。

  • 截止时间传递与其在发送RPC给后端服务器时自拟一个截止时间,不如让软件服务器采用截止时间传递和取消传递的策略。

  • 可使用截止时间传递机制,截止时间在整个服务栈的高层设置(如,前端服务器)。由初始请求触发的整个RPC树会设置同样的绝对截止时间。

  • 同时,我们可能还会将传递出去的截止时间减少一点(如几百毫秒),以便将网络传输时间和客户端收到回复之后的处理时间考虑在内。

  • RPC取消的传递可以避免某些泄露情况,如果某个初始RPC设置了一个很长的截止时间,但是底层之间的RPC只有短暂的截止时间,超时失败了。使用简单的截止时间传递可能会导致初始RPC虽然无法继续处理,却继续消耗服务器资源直到超时。

  • 请求延迟的双峰分布(Bimodal)

  • 使用100s的截止时间,5%的请求会消耗5000个线程(50QPS * 100 seconds),但是前端服务器并没有这么多可用线程。忽略副作用,前端也仅能够处理19.6%的请求(1000可用线程 /(5000+95)线程工作),这会造成 80.4%的错误率。因此,不仅 5% 的请求受到了影响(那些由于后端问题不可能成功的请求),实际上大部分的请求都受到了影响。

  • 如果无法完成的请求能够尽早返回一个错误而不是等完整个截止时间,我们就可以避免这个问题。例如,如果一个后端服务器不可用,经常立刻返回一个错误值是最好的,而不是等待这个后端服务器变得可用。如果RPC层支持快速失败的选项,一定要启用它。

  • 假设你的后端要处理来自不同客户端的性能和特征各异的请求,我们可以考虑限制一个客户端只能占用25% 的线程总数,以便在某个异常客户端大量产生负载的情况下提供一些公平性。

慢启动和冷缓存

  • 慢启动和冷缓存进程在刚刚启动之后通常要比稳定状态下处理请求的速度慢一点。慢的原因可能是由下列一个或多个原因导致:必需的初始化过程在接收到第一个请求后,需要跟后端服务器建立连接。运行时性能优化,尤其是JavaJIT 编译过程,热点优化,以及类延迟加载机制。同样的,有些服务器会在缓存没有充满之前效率很低。

  • 过量配备(overprovision)该服务。区分延迟类缓存和容量类缓存是很重要的:当使用延迟类缓存时,服务器可以在空缓存的情况下仍然处理预期的请求负载,但是使用容量类缓存时,该服务将不能够在空缓存下处理请求负载。

保持调用栈永远向下

  • 假设一个用户有一个主后端和一个预先选择好的另外集群中的一个热备后端,主后端在底层出现错误或者延迟上升的情况下将请求代理给热备后端。如果整个系统都处于过载状态,那么从主到副的这种代理可能会增多,会给系统带来更多的负载,因为请求通常要被解析两次,还需要主后端消耗资源等待副后端任务。

  • 在用户的请求路径中最好能够避免使用同层通信—也就是避免通信路径中出现环。

  • 应该由客户端来进行这种通信。例如,如果一个前端需要和后端通信,但是猜错了后端任务,后端不会代理请求给正确的后端,而是通过返回错误使得前端在正确的后端任务上重试它的请求。

连锁故障的触发条件

  • 根据请求数量和可用容量来动态调节任务的同时更新数量可能是个好办法。

  • 在发生连锁故障时,检查最近的改变以及回滚通常是明智的,尤其在这些改变会影响容量或者更改请求特点的情况下。

  • 自然增长在很多情况下,连锁故障不是由于某个特定的服务改变导致的,而是由于使用量的天然上升,却没有进行对应的容量调整导致的。

连锁故障的测试

  • 设计良好的组件应该可以拒绝一小部分请求而继续存活。

解决连锁故障的立即步骤

  • 莎士比亚搜索服务的连锁故障某个关于莎士比亚作品的纪录片在日本上映了,同时特别指明了莎士比亚搜索服务是进行进一步研究的最佳工具。随着这次广播,亚洲数据中心的流量激增,超过了服务容量。服务容量的问题伴随着当时正在进行的大型更新而变得更严重了。幸运的是,一些安全防护措施帮助缓解了可能的故障。生产环境准备评审(production readiness review)流程指出了一些问题,开发团队已经解决。例如,开发者为服务加入了优雅降级功能。当容量不够时,服务不再返回照片,或者不再返回解释故事发生位置的小地图。取决于RPC的目的,超时的RPC要么不再重试(例如,之前提到的图片),要么采用随机指数型延迟进行重试。即使有这些保护措施的存在,任务还是一个接一个地失败了,然后被Borg系统重启,这导致正常工作的任务数量持续减少。由于这个原因,服务监控页面上的某些图表变成了红色,并且SRE收到了紧急警报。为了解决这个问题,SRE临时向亚洲数据中心增加了一些服务容量,调整了莎士比亚搜索任务的任务数量。通过这种操作,成功恢复了亚洲数据中心的莎士比亚搜索服务。接下来,SRE书写了一篇事后总结,详细说明了触发问题的事件,哪些做得好,哪些可以做得更好,和一系列待办事项来避免这个情景重现。例如,在服务过载的情况下,GSLB负载均衡器可以将一些流量导入邻近的数据中心。同时,SRE团队启用了自动伸缩机制,于是任务的数量可以自动跟着流量增长,这样他们就不用再操心这类问题了。

小结

  • 小结当一个系统过载时,某些东西总是要被牺牲掉。一旦一个服务越过了临界点,服务一些用户可见错误,或者低质量结果要比尝试继续服务所有请求要好。理解这些临界点所在,以及超过临界点系统的行为模式,是所有想避免连锁故障的运维人员所必需的。如果不加小心,某些原本为了降低服务背景错误率或者优化稳定状态的改变反而会让服务更容易出现事故。在请求失败的时候重试、负载自动转移、自动杀掉不健康的服务器、增加缓存以提高性能或者降低延迟:这些手段原本都是为了优化正常情况下的服务性能,但是也可能会提高大规模的服务故障的几率。一定要小心评估这些改变,否则灾难就会接踵而至。

第22章 处理连锁故障

  • 如果请求没有成功,以指数型延迟重试。

  • 连锁故障是由于正反馈循环(positive feedback)导致的不断扩大规模的故障。[72]连锁故障可能由于整个系统的一小部分出现故障而引发,进而导致系统其他部分也出现故障。例如,某个服务的一个实例由于过载出现故障,导致其他实例负载升高,从而导致这些实例像多米诺骨牌一样一个一个全部出现故障。

连锁故障产生的原因和如何从设计上避免

  • RPC超时服务器过载时,对客户端RPC的回复会变慢,最终会超过客户端所设置的超时时间。这会导致服务器对请求实际进行的处理都被浪费了,而客户端可能会重试RPC,造成更严重的过载。

  • CPU缓存效率下降CPU使用得越多,任务被分配到多个CPU核心上的几率越大,从而导致CPU核心本地缓存的失效,进而降低CPU处理的效率。

  • 缓存命中率下降可用内存的减少可能会导致应用层缓存的命中率降低,导致向后端发送更多的RPC,可能会导致后端任务过载。

  • 线程线程不足可能会导致错误或者导致健康检查失败。如果服务器为此增加更多线程,这些线程可能会占用更多内存。在极端情况下,线程不足可能会导致进程ID数不足(Linux的进程ID数是有限的)。文件描述符文件描述符(file descriptor)不足可能会导致无法建立网络连接,进而导致健康检查失败。

  • 假设如下场景:1.某Java前端服务器GC参数没有被调优。2.在高负载(但是在期待范围内)情况下,前端由于GC问题导致CPU不足。3.CPU不足导致请求处理变慢。4.同时处理的请求增多导致内存使用上升。5.内存压力上升,同时由于固定内存分配比例的原因,用于缓存的内存数量减少。6.缓存数量降低意味着缓存中键值数量下降,从而导致命中率下降。7.缓存命中率下降导致更多的请求被发往后端进行处理。8.后端服务器CPU或者线程不足。9.CPU不足导致健康检查失败,从而触发了连锁故障。在上述这个复杂情景下,发生故障时可能没有时间仔细分析因果关系。尤其是在前端和后端由不同团队运维时,判断后端崩溃是由于前端缓存命中率下降可能非常困难。

  • 会自动避免产生错误的软件服务器的负载均衡策略会将这个问题加剧—某几个后端任务产生了错误,会导致负载均衡器不再向它们发送请求,进而使得其余软件服务器的负载上升,从而再次触发滚雪球效应。

防止软件服务器过载

  • 防止软件服务器过载下面描述了避免过载的几种策略,大致以优先级排序。使用负载压力测试得出服务器的极限,同时测试过载情况下的失败模式。

  • 提供降级结果给用户返回低质量的,但是更容易计算的结果。

  • 在过载情况下主动拒绝请求软件服务器应该保护自己不进入过载崩溃状态。

上层系统应该主动拒绝请求

  • 在反向代理层,通过针对请求的某种特性进行数量限制(如IP地址),来缓解和避免拒绝服务攻击,避免攻击性客户端的影响。● 在负载均衡器层,在服务进入全局过载时主动丢弃请求。

  • 进行容量规划好的容量规划可以降低连锁反应发生的可能性。容量规划应该伴随着性能测试进行,以确定可能导致服务失败的负载程度。

  • 进行容量规划只能减少触发连锁反应的可能性,但是并不能完全避免。当一个计划内或者计划外的事件导致大部分集群容量同时下线时,连锁反应是不可避免的。负载均衡问题、网络分区事件,或者突发性流量增长,都会创造意料之外的负载问题。有些系统可以根据需要动态增加容量,这可能防止过载发生,但是适当地进行容量规划还是必要的。

  • 在这个理想化的情景下,只有在请求速率超过单个请求的处理速率时,请求才会进入队列,这种情况会导致线程池和队列的同时饱和。

  • 对一个流量基本稳定的服务来说,队列长度比线程池大小更小会更好(如 50% 或更小)。当服务处理速度无法跟上请求到达速率时,尽早拒绝请求会更好。

  • 流量抛弃和优雅降级流量抛弃(load shedding)是指在软件服务器临近过载时,主动抛弃一定量的负载。

  • 一种简单的流量抛弃实现方式是根据CPU使用量、内存使用量及队列长度等进行节流。

  • 简化了一些细节,[75]但是这里很好地展现了重试是如何摧毁一个系统的。注意临时性的过载升高,或者使用量的缓慢增加都有可能造成这种情况。

  • 一定要使用随机化的、指数型递增的重试周期。

  • 如果重试不是随机分布在重试窗口里的,那么系统出现的一个小故障(某个网络问题)就可能导致重试请求同时出现,这些请求可能会逐渐放大

  • 限制每个请求的重试次数。不要将请求无限重试。● 考虑使用一个全局重试预算。例如,每个进程每分钟只允许重试60次,如果重试预算耗尽,那么直接将这个请求标记为失败,而不真正发送它。这个策略可以在全局范围内限制住重试造成的影响,容量规划失败可能只是会造成某些请求被丢弃,而不会造成全球性的连锁故障。

  • 从多个视角重新审视该服务,决定是否需要在某个级别上进行重试。这里尤其要避免同时在多个级别上重试导致的放大效应:高层的一个请求可能会造成各层重试次数的乘积数量的请求。如果服务器由于过载不能提供服务,后端、前端、JavaScript层各发送3次重试(总计4次请求),那么一个用户的动作可能会造成对数据库的64 次请求(43)。在数据库由于过载返回错误时,这种重试只会加重问题。

  • 使用明确的返回代码,同时详细考虑每个错误模式应该如何处理。例如,将可重试错误和不可重试错误分开。

  • 设置一个截止时间通常是明智的。不设置截止时间,或者设置一个非常长的截止时间通常会导致某些短暂的、已经消失的问题继续消耗服务器资源,直到重启。

  • 截止时间设置得太长可能会导致框架中的高层级部分由于低层级的问题而持续消耗资源。截止时间设置得太短可能会导致某些比较重型的请求持续失败。恰当的截止时间设置,需要在多个限制条件中选择一个平衡点。

  • 截止时间传递与其在发送RPC给后端服务器时自拟一个截止时间,不如让软件服务器采用截止时间传递和取消传递的策略。

  • 可使用截止时间传递机制,截止时间在整个服务栈的高层设置(如,前端服务器)。由初始请求触发的整个RPC树会设置同样的绝对截止时间。

  • 同时,我们可能还会将传递出去的截止时间减少一点(如几百毫秒),以便将网络传输时间和客户端收到回复之后的处理时间考虑在内。

  • RPC取消的传递可以避免某些泄露情况,如果某个初始RPC设置了一个很长的截止时间,但是底层之间的RPC只有短暂的截止时间,超时失败了。使用简单的截止时间传递可能会导致初始RPC虽然无法继续处理,却继续消耗服务器资源直到超时。

  • 请求延迟的双峰分布(Bimodal)

  • 使用100s的截止时间,5%的请求会消耗5000个线程(50QPS * 100 seconds),但是前端服务器并没有这么多可用线程。忽略副作用,前端也仅能够处理19.6%的请求(1000可用线程 /(5000+95)线程工作),这会造成 80.4%的错误率。因此,不仅 5% 的请求受到了影响(那些由于后端问题不可能成功的请求),实际上大部分的请求都受到了影响。

  • 如果无法完成的请求能够尽早返回一个错误而不是等完整个截止时间,我们就可以避免这个问题。例如,如果一个后端服务器不可用,经常立刻返回一个错误值是最好的,而不是等待这个后端服务器变得可用。如果RPC层支持快速失败的选项,一定要启用它。

  • 假设你的后端要处理来自不同客户端的性能和特征各异的请求,我们可以考虑限制一个客户端只能占用25% 的线程总数,以便在某个异常客户端大量产生负载的情况下提供一些公平性。

慢启动和冷缓存

  • 慢启动和冷缓存进程在刚刚启动之后通常要比稳定状态下处理请求的速度慢一点。慢的原因可能是由下列一个或多个原因导致:必需的初始化过程在接收到第一个请求后,需要跟后端服务器建立连接。运行时性能优化,尤其是JavaJIT 编译过程,热点优化,以及类延迟加载机制。同样的,有些服务器会在缓存没有充满之前效率很低。

  • 过量配备(overprovision)该服务。区分延迟类缓存和容量类缓存是很重要的:当使用延迟类缓存时,服务器可以在空缓存的情况下仍然处理预期的请求负载,但是使用容量类缓存时,该服务将不能够在空缓存下处理请求负载。

保持调用栈永远向下

  • 假设一个用户有一个主后端和一个预先选择好的另外集群中的一个热备后端,主后端在底层出现错误或者延迟上升的情况下将请求代理给热备后端。如果整个系统都处于过载状态,那么从主到副的这种代理可能会增多,会给系统带来更多的负载,因为请求通常要被解析两次,还需要主后端消耗资源等待副后端任务。

  • 在用户的请求路径中最好能够避免使用同层通信—也就是避免通信路径中出现环。

  • 应该由客户端来进行这种通信。例如,如果一个前端需要和后端通信,但是猜错了后端任务,后端不会代理请求给正确的后端,而是通过返回错误使得前端在正确的后端任务上重试它的请求。

连锁故障的触发条件

  • 根据请求数量和可用容量来动态调节任务的同时更新数量可能是个好办法。

  • 在发生连锁故障时,检查最近的改变以及回滚通常是明智的,尤其在这些改变会影响容量或者更改请求特点的情况下。

  • 自然增长在很多情况下,连锁故障不是由于某个特定的服务改变导致的,而是由于使用量的天然上升,却没有进行对应的容量调整导致的。

连锁故障的测试

  • 设计良好的组件应该可以拒绝一小部分请求而继续存活。

小结

  • 小结当一个系统过载时,某些东西总是要被牺牲掉。一旦一个服务越过了临界点,服务一些用户可见错误,或者低质量结果要比尝试继续服务所有请求要好。理解这些临界点所在,以及超过临界点系统的行为模式,是所有想避免连锁故障的运维人员所必需的。如果不加小心,某些原本为了降低服务背景错误率或者优化稳定状态的改变反而会让服务更容易出现事故。在请求失败的时候重试、负载自动转移、自动杀掉不健康的服务器、增加缓存以提高性能或者降低延迟:这些手段原本都是为了优化正常情况下的服务性能,但是也可能会提高大规模的服务故障的几率。一定要小心评估这些改变,否则灾难就会接踵而至。

个人总结

  1. 使用负载压力测试得出服务器的极限以及过载降级策略
  2. 一定要使用随机化的、指数型递增的重试周期。
  3. 使用明确的返回代码,同时详细考虑每个错误模式应该如何处理。例如,将可重试错误和不可重试错误分开。
  4. 设置一个截止时间通常是明智的。RPC之间传递。
  5. 限制一个客户端对线程总数占有比例,以便在某个异常客户端大量产生负载的情况下提供一些公平性。
  6. 慢启动和冷缓存;区分延迟类缓存和容量类缓存。
  7. 保持调用栈永远向下(减少转发次数和后端资源消耗)。
  8. 设计良好的组件应该可以拒绝一小部分请求而继续存活。

第23章 管理关键状态:利用分布式共识来提高可靠性

  • 在构建可靠的、高可用的系统过程时,我们发现分布式共识系统适合用来维护某个强一致的系统状态

  • 当你需要实现领头人选举(leader election)、关键性共享状态或分布式锁等时,我们建议采用某种正式证明过的、详尽测试过的分布式共识系统来实现。如果不严肃对待这个问题很有可能会导致事故,在更糟的情况下,将造成某种非常难以修复的数据一致性问题,这可能会大大加长系统事故的处理时间。

  • 网络分区问题迟早会发生(光缆被切断,数据包由于拥塞造成丢失或延迟升高,硬件故障,网络组件配置错误等),理解如何构建分布式共识实际上就是理解某个服务应用如何实现强一致性和高可用。由于商业的压力,很多服务都需要很高的可用性,这些应用程序通常都需要对数据访问的强一致性。

  • 最终一致可能会带来意想不到的问题(参见文献[Lu15]),尤其是当时钟漂移(在分布式系统中,这是不可避免的),或者网络分区(参见文献[Kin15])发生的时候。

  • Jeff Shute(参见文献[Shu13])曾经说过,“我们发现开发者通常花费了大量的时间构建一个极为复杂和容易出错的机制来应对最终一致性下可能过时的数据。我们认为对开发者来说这是一种无法接受的负担,数据一致性的问题应该在数据库层解决。”

使用共识系统的动力:分布式系统协调失败

  • 在网络分区情况下—造成问题不一定是由完全分区导致的,而是:● 网络非常慢。● 某些消息可以通过,但是某些消息被丢弃了。● 单方面的节流措施。

  • 如果网络质量真的很差,分布式共识系统也无法正确选举出主实例时,作为工程师可能也没有什么好办法来做出正确决策。

分布式共识是如何工作的

  • 拜占庭式问题指的是当某个进程由于程序Bug或者恶意行为发送不正确的消息的问题,这种问题相对来说处理成本更高,同时也更少见。

  • 严格来讲,在有限时间内解决异步式分布式共识问题是不可能的。正如Dijkstra 的获奖文章—FLP impossibility result(参见文献[Fis85])写的那样,在不稳定的网络条件下,没有任何一种异步式分布式共识算法可以保证一定能够达成共识。在实际操作中,我们通过保证给系统提供足够的健康的副本,以及良好的网络连接状态来保障分布式共识算法在大多数情况下是可以在有限时间内达成共识的。同时整个系统还需要加入随机指数型延迟。

  • 这样,我们可以保障重试不会造成连锁反应,以及本章后面会提到的角斗士(dueling proposers)问题。

  • 这个协议在一个有利环境下能够保障安全性,同时提供足够的冗余度。

  • 最初的分布式共识问题的解决方案是Lamport的Paxos 协议(参见文献[Lam98]),但是也有其他的协议能够解决这个问题,包括 Raft(参见文献[Ong14])、Zab(参见文献[Jun11]),以及 Mencius(参见文献[Mao08])。Paxos本身也有很多变种尝试提升性能(参见文献[Zoo14])。这些变种常常只是在某一个小细节上不同,例如给某个进程一个特殊的领头人角色以简化协议实现等。

分布式共识的系统架构模式

  • 可靠的复制状态机一个复制状态机(replicated state machine,RSM)是一个能在多个进程中用同样顺序执行同样的一组操作的系统。RSM 是一个有用的分布式系统基础组件,可以用来构建数据和配置存储,执行锁机制和领头人选举(接下来会详细描述)。

  • 在RSM系统上进行的操作是通过共识算法来全局排序的。

  • 时间戳在分布式系统中问题非常大,因为在多个物理机器上保证时间同步是不可能的。Spanner(参见文献[Cor12])通过针对最差情况下的不确定性建模,同时在必要时减慢处理速度来解决这个问题。

  • 分布式系统的领头人选举是跟分布式共识等价的问题。复制多份服务并使用一个唯一的领头人(leader)来进行某种类型的工作是很常见的设计。唯一的领头人是一种保证粗粒度互斥性的方法。

分布式共识系统的性能问题

  • 世界上并没有某个性能“最优”的分布式共识和状态机复制算法,因为算法性能通常取决于与系统负载有关的多个因子,以及系统性能的目标和系统的部署情况。

  • 复合式Paxos:消息流过程详解复合式Paxos(Multi-Paxos)协议采用了一个强势领头人(strong leader)进程的概念:除非目前没有选举出任何的领头人进程,或者发生了某种错误,在正常情况下,提议者只需要对满足法定人数的进程发送一次消息就可以保障系统达成共识。这种强势领头人在很多共识协议中都适用,在系统需要传递大量消息的时候非常合适。

  • 很多共识系统使用TCP/IP作为通信协议。TCP/IP是面向连接的,同时针对消息的先进先出(FIFO)顺序提供了强保障。但是在发送任何数据之前,都需要先建立新的TCP/IP连接,也就是一次网络往返需要进行三次握手协议。TCP/IP慢启动机制也限制了连接的初始带宽。常见的TCP/IP窗口大小在4~15KB之间。

  • TCP/IP的慢启动问题对共识组中的进程来说可能不是问题:因为这些进程会互相建立一个连接而且保持这些连接,因为通信将会很频繁。但是,对拥有非常多的客户端的系统来说,可能不能做到让每个客户端都和共识组所有进程都保持一个活动连接。因为TCP/IP需要消耗一定资源,包括文件描述符,以及对应的KeepAlive数据流量。对高度分片、拥有几千个副本的数据存储系统,以及更大规模的客户端来说,这种成本是无法接受的。一个解决方案是使用地域性的代理池,如图23-9所示。该代理池的进程与共识组建立持久的TCP/IP进程,以降低客户端的开销。同时代理池也是包装分片逻辑和负载均衡逻辑,以及共识系统的服务发现逻辑的好地方。

  • 快速Paxos协议:性能优化快速Paxos协议(Fast Paxos,参见文献[Lam06])是Paxos协议的一个变种,意在优化Paxos算法在广域网络中的性能。使用该协议的每个客户端可以直接向组内的接收者们发送Propose消息,而不需要像经典Paxos和复合Paxos那样通过一个领头人进程发送。

  • 稳定的领头人机制上文已经描述过,复合Paxos是如何通过选举一个稳定的领头人进程来提高性能的。Zab(参见文献[Jun11])和Raft(参见文献[Ong14])协议是其他两个例子,它们也通过选举稳定的领头人进程来提高性能。这种方案可以进一步进行读操作优化,因为领头人始终拥有最新信息,但是也存在以下几个问题:● 所有的改变状态的操作都必须经由该领头人,这个要求使得距离领头人进程较远的客户端必须要增加额外的网络延迟。● 领头人进程的外发网络带宽是系统性能的瓶颈(参见文献[Mao08]),因为该领头人的Accept消息包含了每个提议的所有数据,而其他进程发送的消息仅仅包含了交易数字,而没有真正的数据。● 如果领头人进程恰好处于一台有性能问题的机器上,整个系统的吞吐量都会受到影响。几乎在所有关注性能的分布式共识系统设计中,都采用了单个稳定领头人进程机制,或者是某种领头人轮换机制—预先分配给每个副本一个独立的分布式算法编号(通常是对交易编号取模)。使用轮换机制的算法包括Mencius(参见文献[Mao08])和Egalitarian Paxos(参见文献[Mor12a0])。

  • 复合Paxos的信息流的过程在本章前面“复合式Paxos:消息流过程详解”一节中描述过了,但是并没有展示在什么情况下协议要求必须在磁盘中记录状态改变。在进程做任何一个承诺之前都必须进行磁盘写操作。在复合Paxos性能的关键点—协议的第二部分中,这些操作点处于接收者针对某个提议发送Accepted消息之前,以及提议者发送Accept消息之前—因为这条Accept消息其实是一个隐含的Accepted消息

  • 这就意味着在单个共识操作的延迟中,有以下几个操作:● 提议者的一次硬盘写入操作。● 并行消息发送给接收者。● 每个接收者的磁盘写操作(并行)。● 回复消息的传递。

  • 分布式共识算法经常用来实现一个复制状态机。RSM需要保留一份交易日志,以便用于灾难恢复(正如其他数据存储那样)。共识算法的日志可以和RSM的交易日志合为一个。将这两个日志合并可以避免不停地向磁盘上的两个不同位置交替写入(参见文献[Bol11]),也就降低了磁盘寻址操作消耗的时间。这些磁盘于是可以每秒处理更多操作,该系统也可以每秒处理更多操作。

  • 在数据存储系统中,磁盘在维护日志之外还有其他用处:数据通常也是存放于磁盘中的。日志的写操作必须直接刷写到硬盘,但是数据的改变通常可以写入内存缓存中,稍后再写入磁盘,可以进行重新排序以便更有效地写入(参见文献[Bol11])。

分布式共识系统的部署

  • 系统设计者部署共识系统时,最重要的决策在于选择副本的数量和对应的部署位置。

  • 副本的数量一般来说,共识系统都要采用“大多数”的法定过程。也就是说,一组2f+1副本组成的共识组可以同时承受f个副本失败而继续运行(如果需要容忍拜占庭式失败,也就是要能够承受副本返回错误结果的情况,则需要3f+1个副本来承受f个副本失败(参看文献[Cas99]))。针对非拜占庭式失败的情况,最小的副本数量是3—如果仅仅部署两个副本,则不能承受任何一个副本失败)。3个副本可以承受1个副本失败。大部分系统的不可用时间都是由计划内维护造成的

  • 3个副本使该系统可以在1个副本维护时继续工作(这里假设其余两个副本可以承受系统负载)。如果某个非计划内的失败情况在常规维护时发生了,那么共识系统就会不可用。而共识系统的不可用通常是不可接受的,于是在这种情况下,我们需要5个副本同时运行,于是可以允许系统在两个副本不可用的情况下继续运行。如果5个副本中有4个能正常运行,则不需要进行任何人工干预操作,但是如果仅仅只有3个能正常运行,那么我们需要立刻增加一个或者两个额外的副本。

  • 如果共识系统中大部分的副本已经无法访问,以至于无法完成一次法定进程,那么该系统理论上来说已经进入了一个无法恢复的状态。因为至少有一个副本的持久化日志无法访问。在这个状态下,有可能某个操作仅仅只在无法访问的副本上执行了。这时,系统管理员可以强行改变小组成员列表,将新的副本添加到小组中,使用其他可用成员的信息来填充。但是数据丢失的可能性永远存在—这种情况应该全力避免。在灾难处理过程中,系统管理员需要决定是否需要进行这种强制性的重新配置,或者仅仅是等待那些机器恢复可用。当决策做出后,对系统日志的处理(以及对应的监控)就变得非常重要。

  • 针对任何系统的副本数量的考虑都是基于以下几个因素中的一个进行妥协:● 对可靠性的要求● 计划内维护操作的频率● 危险性● 性能● 成本最后的决策每个系统都会不同:每个系统都有不同的可用性服务水平目标;某些组织会更频繁地进行维护性操作;而某些组织使用的硬件成本、质量和可靠性都有所不同。

  • 一个故障域(failure domain)是指系统中可能由于一个故障而同时不可用的一组组件。常见的故障域包括:● 物理机器。● 数据中心中用同一个供电设施的一个机柜。● 数据中心中的数个机柜,使用同一个网络设备连接。● 受单个光纤影响的一个数据中心。● 处于同一个地理区域的一组数据中心,可能被同一个自然灾害所影响。

  • 一般来说,随着副本之间的距离增加,副本之间的网络往返时间也会增加,但是系统能够承受的失败程度也会增加。对多数共识系统来说,网络往返时间的增加会导致操作延迟的增加。

  • 对延迟的敏感性和对灾难的承受程度,每个系统很不一样。在某些共识系统架构下并不需要特别高的吞吐量,或者特别低的延迟:例如,某个共识系统如果只是为了提供成员信息和领头人选举,服务一般不会负载很高,如果共识操作时间仅仅是领头人租约的一小部分时,性能并不是关键点。批处理居多的系统也很少会被延迟所影响:可以通过批处理数量的增加来提高吞吐量。

  • 当处理关键数据时,即使已经有了可靠的共识系统部署在不同的故障域范围内,我们也必须经常将数据备份在其他地方。因为有两个故障域是我们永远无法逃避的:软件本身和系统管理员的人为错误。软件系统中的Bug可能会在罕见情况下浮现造成数据丢失,而错误的系统配置也可能会造成类似情况。而人工操作可能会执行错误的命令,从而造成数据损坏。

  • 当决定副本位置的时候,记得最关键的因素是客户端可见性能:理想情况下,客户端和共识系统之间的RTT应该是最小化的。在广域网络中,无领头人的协议,例如 Mencius和 Egalitarian Paxos可能有一定的性能优势,尤其是当应用程序设计为可以对任意副本进行读操作而不需要进行共识操作时。

  • 向一个采取“大多数”法定仲裁过程系统中增加新的副本可能会降低系统的可用性,如图23-10所示。对ZooKeeper和Chubby来说,一个常见的部署使用5个副本,一个法定仲裁过程需要3个副本参与。整个系统可以在两个副本,也就是40% 不可用的情况下仍然正常工作。当使用6个副本时,仲裁过程需要4个副本:也就是超过33% 的副本不可用就会导致系统无法工作。

  • 为了将系统流量分布得更为均匀,系统设计者可以考虑采用5个副本,将其中2个副本放置于美国中心地带,1个放置于东海岸,另外两个放置于欧洲。这样分布可基本保证共识过程在北美洲的副本上完成,而不需要等待欧洲的回复。而从欧洲起始的共识过程可以仅仅跟美国东海岸的副本完成。东海岸的副本就像两个可能的仲裁组的关键轴,将两个组连接在了一起。

个人总结

  1. 复合式Paxos、快速Paxos协议
  2. 一个常见的部署使用5个副本,一个法定仲裁过程需要3个副本参与。
  3. 分布式共识协议学习参考 TODO

数据完整性的强需求

  • 数据完整性意味着用户可以维持对云服务的访问,用户对数据的访问能力是最重要的,所以这种访问能力的完整性非常重要。

  • 大多数云计算应用都是优化以下5项的某种组合:在线时间、延迟、规模、创新速度和隐私。

  • 为了避免应用程序的数据出现问题,影响到最终用户,一个带外系统、专门负责检查和平衡各种数据存储的系统就很有必要了。

  • 一般来说,公司会采用某种备份策略来“预防”数据丢失。然而,真正应该被关注的重点其实是数据恢复,这是区分备份与存档的重要区别。就像一句流行语说的那样:没有人真的想要备份数据,他们只想恢复数据。

  • 备份与存档最重要的区别就是,备份是可以直接被应用程序重新加载的。因此备份和存档的使用场景非常不同。

  • 存档的目的是将数据长时间安全保存,以满足审核、取证和合规要求。在这些情况下的数据恢复通常不需要满足服务的在线率要求。例如,我们可能需要将财务交易数据保存七年时间。为了达到这个目标,我们可以每个月将累积的审核日志迁移到离线的、异地的长期存档存储系统上。读取和恢复这样的信息可能需要一周时间,在长达一个月的财务审核过程中,这是可以接受的。相对存档,当灾难来临的时候,数据必须从真实的备份中快速恢复,最好能维持服务在线率的要求。否则的话,受影响的用户将由于数据问题无法使用应用程序,直到数据恢复过程完成。

  • 尤其考虑到大多数最新产生的数据直到安全备份结束之前都存在丢失的风险,这就意味着备份(而不是存档)应该至少每天进行一次,或者每小时,甚至更短时间内进行一次。备份应该同时使用完整备份、增量备份,或者甚至是流式持续备份手段。因此,当选择备份策略时,一定要考虑针对某个问题需要的恢复时间,以及可以丢失多少最新数据。

保障数据完整性和可用性:Google SRE的目标

  • 数据完整性是手段,数据可用性是目标数据完整性指的是在其生命周期中,数据的准确性和一致性。从数据被记录的那一刻开始,一直到数据被访问的时候,数据应该保持正确,不会以某种未预知的方式改变。

  • 从用户的角度来看,仅仅保障数据完整性,而没有保障数据的可用性是没有意义的。

  • 交付一个恢复系统,而非备份系统针对系统进行备份是一个长期被忽视、被拖延的系统管理任务。任何人都不会将备份作为一个高优先级任务进行—备份需要长期消耗时间和资源,却不能带来任何现在可见的好处。由于这个原因,在备份策略具体实施中如果出现了某些被忽视的细节问题,大家一般都能理解。甚至有的人说,就像其他针对低可能性的危险的保护措施那样,这样的态度是必然发生的。这里的核心问题其实是,我们没有意识到备份措施防护的问题虽然是不太可能发生的,但却是会造成严重问题的。当该服务的数据不可用时,备份系统对整个服务、整个产品,甚至整个公司来说,都是生死攸关的。

  • 相对于关注没人愿意做的备份任务来说,我们应该通过关注更重要的(但并不是更简单的)恢复任务来鼓励用户进行备份。备份其实就像纳税一样,是一个服务需要持久付出的代价,来保障其数据的可用性。我们不应该强调应该纳多少税,而是应该强调这些税会用来提供什么服务:数据可用性的保障。因此,我们不会强迫团队进行“备份”,而是要求:● 各团队需要为不同的失败场景定义一系列数据可用性SLO。● 各团队需要定期进行演练,以确保他们有能力满足这些SLO。

造成数据丢失的事故类型

  • 根源问题某种无法恢复的用户数据丢失是由以下几个因素造成的:用户行为、管理员的错误、应用程序的Bug、基础设施中的问题、硬件故障和部署区的大型事故。

  • Google根据一次针对19次数据恢复过程的研究得出,最常见的用户可见数据丢失场景是由于数据删除和软件Bug造成的引用完整性问题。

  • 在设计数据完整性保障机制时,必须要认识到复制机制和冗余并不意味着可恢复性。

扩展性问题:全量、增量以及相互竞争的备份和恢复机制

  • 当别人问你“是否有备份”的时候,一个经典的错误回答是“我们有比备份更好的机制—复制机制!”复制机制很重要,包括提高数据的本地性(locality),保护某个部署点单点事故等。但是有很多数据丢失场景是复制机制无法保护的。一个自动同步多个副本的数据存储多半会在你发现问题之前,将损坏的数据记录以及错误的删除动作更新到多个副本上。

Google SRE保障数据完整性的手段

  • 24种数据完整性的事故组合

  • 由于数据丢失类型很多(如上文所述),没有任何一种银弹可以同时保护所有事故类型,我们需要分级进行。分级防护会引入多个层级,随着层级增加,所保护的数据丢失场景也更为罕见。

  • 软删除机制可以大幅度减少支持人员的压力。软删除意味着被删除的数据立刻被标记为已删除,这样除了管理后台代码之外其他代码不可见。

  • 软删除还意味着一旦数据被标记为已删除,在某段时间以后会真的被删除。这个时间的长度,尤其是在有很多短期数据的情况下,取决于某个组织的政策和相关的法律条文、可用的存储空间和存储成本,以及产品的价格和市场定位等。常见的软删除时间是15、30、45或者60天。在Google的经验中,大部分账号劫持和数据完整性问题都会在60天内汇报或者检测到。因此,为软删除保存超过60天的需求可能没有那么强烈。

  • 应用中实现一个回收站机制,作为用户错误的主要防护手段。

  • 就算不考虑成本问题,频繁地进行全量备份也是非常昂贵的。最主要的是,这会给线上服务用户的数据存储造成很大的计算压力,以至于影响服务的扩展性和性能。为了缓解这种压力,我们需要在非峰值时间段进行全量备份,同时在繁忙时间段进行增量备份。

  • 没有任何办法可以100% 确定数据是正确的:虽然我们相信存储系统的实现,但是还是要校验!使这个问题变得更复杂的是,为了从低级数据损坏或者删除场景中恢复数据,我们必须从不同恢复点的不同备份中恢复不同子集的数据。同时,我们还要考虑到在一个不断变化的环境中,代码和数据结构的变化对老的备份数据的影响。

  • 安排一些开发者来开发一套数据校验流水线可能会在短期内降低业务功能开发的速度。然而,在数据校验方面投入的工程资源可以在更长时间内保障其他业务开发可以进行得更快。因为工程师们可以放心数据损坏的Bug没那么容易流入生产环境。和在项目早期引入单元测试效果类似,数据校验流水线可以在整个软件开发过程中起到加速作用。

  • 举一个具体的例子:Gmail拥有一系列数据校验器,每一个校验器都曾经在生产环境中检测到真实的数据完整性问题。Gmail开发者由于知道每个新引入的数据一致性问题都可以在24小时之内被检测到而感到非常安心。这些数据校验器的存在,结合单元测试及回归测试的文化使得Gmail开发者有足够的勇气每周多次修改Gmail生产存储系统的实现。

  • 带外数据校验比较难以正确实现。当校验规则太严格的时候,一个简单的合理的修改就会触发校验逻辑而失败。这样一来,工程师就会抛弃数据校验逻辑。如果规则不够严格,那么就可能漏过一些用户可见的数据问题。为了在两者之间取得恰当的平衡,我们应该仅仅校验那些对用户来说具有毁灭性的数据问题。

  • 大规模部署带外检测器可能成本较高。Gmail计算资源的很大一部分都被用来支持每日数据检测的运行。使这个问题变得更严重的是,校验器本身可能会造成软件服务器缓存命中率的下降,这就会造成用户可见响应速度的下降。为了避免这种问题,Gmail提供了一系列针对校验器的限速机制,同时定期重构这些校验器,以降低磁盘压力。

  • 我们可以保证每天运行的校验器持续寻找毁灭性的问题,而其他更严格的校验器可以以更低频率运行,以便满足延迟和成本的要求。

  • 大部分高速进行的小型开发团队都负担不起设计、构建和维护这样的系统所需的资源。就算强迫他们进行,最后的结果也经常是不可靠、不全面的,以及浪费性、一次性的方案,很快就会被抛弃。因此,最好的办法是单独组织一个中央基础设施组为多个产品和工作组提供一套数据校验框架。该基础设施组负责维护带外数据校验框架,而产品开发组负责维护对应的业务逻辑,以便和他们不断发展的产品保持一致。

  • 数据恢复流程中任何一个环节都有可能出问题,只有建立一个完善的端到端测试才能让人晚上放心睡觉。即使最近刚刚成功进行过一次数据恢复,下次执行时某些步骤仍然可能出问题。

  • 只有真正进行恢复操作之后,才能确定到底能否恢复最新数据。

  • 失败是不可避免的,不去主动寻找这些数据恢复失败的场景等于玩火自焚。只有通过进行测试来主动寻找这些弱点,才能够提前修复,关键时刻才不至于追悔莫及!

SRE的基本理念在数据完整性上的应用

  • 信任但要验证我们依赖的任何API都不可能一直完美工作。不管测试有多么复杂,工程质量有多么高,API都一定会存在某些问题。应该通过使用带外数据检测器来检查数据最关键、最核心的部分,哪怕API语义中说明了这些问题不存在。要记住,完美的理论不代表实现也是完美的。

个人总结

  • 备份与存档最重要的区别就是,备份是可以直接被应用程序重新加载的。
  • 通过使用带外数据检测器来检查数据最关键、最核心的部分。

不完美的机器

  • 不完美的机器从某种意义上讲,人类可以被称为不完美的机器。人会感觉无聊,人的处理器(指思维方式)工作原理不清楚,用户界面(指沟通方式)也不太友好,效率也不高。在工作安排中,能够认识到人的这些缺陷,取长补短是最好的。

  • 分心指数工程师能够被分心的方法太多了。例如,某个名字是Fred的SRE 星期一正常上班,他今天不是on-call,也不负责处理中断性事务。很明显,他希望能够进行他自己的项目工作。倒一杯咖啡,带上“不要打扰”的耳机,开始干活,一切正常,对吗?但是,下列任何一件事情都可能随时发生:● Fred的团队使用随机工单分配系统,某个需要今天处理的工单被分配给了他。● Fred的同事目前负责on-call,收到了一个紧急警报。发出警报的组件Fred最熟悉,所以这个同事过来咨询意见。● 某个用户将某个工单的优先级提高了,这个工单是上周Fred轮值on-call的时候就在处理的。● 某个已经持续了三四周的发布到Fred的时候突然出现了问题,Fred需要停下手中的事情检查发布的状态,回滚这个发布等。● 某个用户过来咨询一个问题,因为Fred为人和善。● 等等。最后结果是

  • 虽然Fred有一整天的时间分配给项目工作,他还是很容易被分心。他可以通过关闭E-mail、关掉IM,或者其他手段来减少一定的干扰。但是某些干扰是政策造成的,以及一些无法避免的责任造成的。我们可以说某种程度的干扰是不可避免的,也是有意为之的。这是正确的:每个人都有甩不掉的Bug,同事之间也会有关系的产生与职责的分配。然而,作为团队来说,是有一些管理方式能使更多的员工不受干扰的。

  • 极化时间为了限制干扰数量,我们应该减少上下文切换(指工作类型、环境等的改变)。某些中断性任务是无法避免的。然而,将工程师当成是可以随时中断、上下文切换没有成本是不正确的。给每次上下文切换加上成本的考虑。在项目工作中,一次20分钟的中断性任务需要进行两次上下文切换,而这种切换会造成数个小时的生产力的丧失。为了避免这种经常性的生产力丧失,我们应该延长每种工作模式的时间,一天甚至半天都可以。这种策略与“挤时间”(参见文献[Gra09])策略工作得很好。

  • 极化时间意味着当每个人来上班时,他们应该清晰地知道自己今天是否只是做项目工作,还是只是做中断性工作。这意味着他们可以更长时间地专注于手上的工作,不会不停地被那些他们本不应该负责的事情所打扰。

  • 一般性建议不论哪种中断性任务,如果中断性任务的量对一个人来说太高,那么应该增加一个人负责。这个概念很显然适用于工单,但是也适用于紧急警报。on-call工程师可以将紧急警报降级为工单,也可以找副on-call来共同处理。

  • 主on-call工程师应该专注于on-call工作。如果目前紧急警报较少,那么一些可以随时放下的工单,或者其他中断性事务应该由on-call人员处理。当某个工程师on-call一周时,这一周他应该完全排除在项目进度之外。如果某个项目非常重要,不能等待一周,那么这个人就不应该参与on-call。管理层应该介入,安排其他人替代on-call。管理层不应该期望员工在on-call的同时还能在项目上有所进展(或者其他高上下文切换成本的活动)。

  • 另外:总有一些清理工作要做,工单数量可能是0,但是总会有需要更新的文档,需要整理的配置文件等。未来的on-call工程师可以从这些活动中受益。他们就不会再来打扰你了!

  • 工单如果目前你是随机分配工单给团队成员,请立刻停止。这样做对团队的时间非常不尊重,和让成员尽可能不被打扰的目标背道而驰。工单处理应该由全职人员负责,同时保证占用合理的时间。如果团队目前的工单主oncall和副on-call都处理不完,那么需要重新架构整个工单的处理流程,保障任何时间都有两个全职员工处理工单。不要将复杂分散到整个团队中去。人不是机器,这样做只会干扰员工,降低他们的工作效率。

  • 持续性的运维工作尽可能地让整个团队共同负责这件事。例如变更发布、配置文件修改等,如果这项工作有非常清晰的流程定义,那么就没有任何理由让任何一个人专门负责这件事情。定义一个发布管理者的角色来由on-call和其他中断性任务处理者来承担。同时应该进行正式化交接过程—这有一定成本,但是却保障了接下来的人不受打扰。负责中断性事务,或者不负责有的时候,某个中断性任务只有某个目前不在值班的成员能够妥善处理。虽然理想情况下,这种情况不应该发生,但是还是偶尔会发生,我们应该尽一切努力避免这种情况。

个人总结

  1. 极化时间,减少干扰;整理必要的文档。
  2. 不要将复杂分散到整个团队中去。人不是机器,这样做只会干扰员工,降低他们的工作效率。

第30章 通过嵌入SRE的方式帮助团队从运维过载中恢复

  • Google的SRE团队的标准政策要求团队在项目研发和被动式工作之间均匀分配时间。在实际工作中,这种平衡可能会被每天工单数量的不断增加而被打乱,甚至持续数月之久。长时间承担非常繁重的Ops类型的工作是非常危险的,团队成员可能会劳累过度,不能在项目工作上取得任何进展。当一个团队被迫花费过多时间解决工单的时候,他们就会减少花在改进服务上的时间,服务的可扩展性和可靠性肯定会随之变差。解决这个问题的一种方式是给处于过载状态的团队临时调入一个SRE。调入该团队后,该SRE应该关注于改善这个团队的行事方式,而不是简单地帮助团队清理积压的工单。通过观察团队的日常工作,提出改善性意见,该SRE可以帮助团队用全新的视角来审视自己的日常工作。这往往是团队本身的成员做不到的。注意,当采用这个方法的时候,只需要调入一名SRE就够了。同时调入两名SRE不一定会产生更好的结果。对那些防御性比较强的团队来说,可能会激发问题的发生。

  • 当你开始构建自己的第一个SRE团队时,采用本章中介绍的方法可以帮助避免团队走入歧途,退化成那种只专注于工单处理的传统运维团队。

  • 在决定采用这种嵌入SRE的方式之前,你应该花一些时间来回顾一下Ben Treynor Sloss的介绍中提到的SRE理念和实践经验,同时应该看一下本书的第6章。

第一阶段:了解服务,了解上下文

  • 第一阶段:了解服务,了解上下文在嵌入团队的过程中,你的主要工作是清楚地表达团队目前的流程和工作习惯对于该服务的可扩展性有利或者有弊的原因。你应该提醒该团队,日益增加的工单不应需要更多的SRE来处理。SRE模型的目标是仅仅在系统复杂度上升的时候才增加新人。你应该尝试引导团队建立健康的工作习惯,这样能够减少花费在工单上的时间。这与指出该服务目前还可以自动化或者进一步简化同样重要。

  • SRE团队陷入Ops模式的原因是过分关注如何快速解决紧急事件而不是如何减少紧急事件的数量。

第二阶段:分享背景知识

  • 第二阶段:分享背景知识在了解团队内部动态和找到痛点之后,你应该通过建立一些最佳实践来为改善状况做准备。例如,建立书写事后总结的机制,辨别琐事的来源,确定每个琐事的最佳解决方案等。书写一个好的事后总结作为示范

  • 你应该将团队遇到的紧急事件分为琐事的和非琐事的。最后,将这个列表交给团队并且清晰地解释哪些紧急事件应该被自动化,而其他的紧急事件则是运维服务必须承担的工作。

第三阶段:主导改变

  • 第三阶段:主导改变保持团队健康是一个持续的过程。正因为此,这不是你可以通过个人英雄主义来解决的问题。为了确保团队在未来可以进行自我调节,我们需要帮助他们建立一个良好的SRE心理模型。

  • 对某项决策进行充分解释的例子:●“我反对最新版本的原因不是因为测试结果有问题,而是因为我们对发布所制定的错误预算已经耗尽了。”● “发布需要能够安全回退,因为我们的SLO非常高。要想达到SLO的要求,平均恢复时间必须非常短,这样在回退之前进行深入的现场调查是不现实的。”对于某项决策不充分的解释的例子:●“我不认为每一个服务器自己生成自身的路由配置是安全的,因为我不信。”这一决策可能是正确的,但是分析论证上是很薄弱的(也没有详细解释)。团队无法知道你心中所想,所以他们很可能模拟你这种不良行为。相反,尝试“[……]不安全,因为在该代码中的一个错误会导致其他服务同时受到影响,同时额外的代码也可能带来减慢回滚的速度的问题。”●“自动化机制在遇到部署不一致的情况时应该放弃进行。”

  • 和之前的例子一样,这一决策可能是正确的,但是是不充分的。相反,尝试“[……]因为我们这里的一个简化假设是全部的改变都通过自动化进行,这种情况的发生说明了有什么东西违反了这一规则。如果这种情况经常发生,我们应该找到和消除造成这种变化的根源。”

  • 提出引导性问题应该提出引导性问题,而非指责性的问题。当你和SRE团队交流时,试着用一种可以鼓励别人思考基本理念的方式来提出问题。

  • 引导性问题的例子:● “我看到任务失败的警报经常发生,但是on-call工程师通常什么都不做。这样会对SLO有什么影响?”● “这个上线过程看起来非常复杂。你知道为什么创建一个新的服务实例需要这么多的配置文件的更新吗?”引导性问题的反例:●“这些旧的、停滞的发布是什么情况?”●“为什么某个组件要做这么多的事情?”

小结

  • 你的最后一个任务是书写一份报告。报告中应该重申你的观点、例子和逻辑推理过程。同时,该报告应该向团队提供一些待办事项,来保证他们会实践你所传授的东西。你应该将报告组织成一份检查报告[12],解释成功路上的每一个重要的决策。大部分的工作现在已经完成了。虽然你的嵌入式工作正式结束,但仍然应该参与设计评审和代码评审。在未来几个月中持续关注这个团队,确保他们正在慢慢提高自己的容量规划能力、紧急事件处理能力和部署发布过程。

第31章 SRE与其他团队的沟通与协作

沟通:生产会议

  • 在两个SRE团队视频会议时,如果一个团队比另外一个大很多,我们建议从较小的团队一边选择主席。更大的一方会自然而然地安静下来,一些不平衡的团队规模所造成的不良影响(这些会由于视频会议的延迟而变得更糟)将得到改善。[14]我们不知道这是否有任何科学依据,但它确实有效。

SRE的内部协作

  • 不管这些角色定义得是否清晰,基本来说,技术负责人是负责团队技术方向的,可以以多种方式进行领导。他可以通过仔细评审每个人的代码,组织进行季度工作汇报,或者是在团队中推进共识的建立来引领团队方向等方式来领导团队。

  • 在Google内部,技术负责人和SRE经理的工作几乎一样,因为我们的SRE经理也具有高度的技术能力。但SRE经理在这之外还有两个特殊责任:绩效管理,以及其他一切其他人不能处理的事情。好的技术负责人、SRE经理以及项目经理组成了一个完整的管理团队,可以很好地组织项目进行讨论设计文档,必要的时候甚至亲自书写代码。

  • 建议只有在不得已的情况下才应该跨地域开发项目,但是这同时也会带来一定的好处。跨地域工作需要更多的沟通,工作完成得也更慢;但是好处是—如果你能协调好的话—则会有更高的产能。单地项目其实也可能会导致其他人不知道你正在做什么,所以这两种做法其实都有一定成本。


第33章 其他行业的实践经验

  • 在本章中,我们会讨论到许多SRE的核心指导思想。为了简化与其他行业最佳实践的比较,我们将这些理念分为4大类:● 灾难预案与演习● 书写事后总结的文化● 自动化与降低日常运维负载● 结构化的、理智的决策

附录A 系统可用性

附录B 生产环境运维过程中的最佳实践

附录C 事故状态文档示范

附录D 事后总结示范

附录E 发布协调检查列表


其他


看见有人不赞同,其实这些问题你们没有遇见过,产品着急上线,出现了一个极低概率的case,修复成本很高。上线后的回报很大,而这个case带来的损失很小,这个时候肯定带着bug上线啊。很多时候产品上线发现商业模式跑不通,这个bug就不用修了,或者迭代过程中才用了另外的实现方式避免了这个问题。错误预算都不一定能用上。
——评论

阿里的要求:可灰度、可监控、可回滚,大厂的思路基本都是一致的。
——评论

产品在不同的发展阶段,可用性目标应该是动态变化的,快速发展期的可用性往往低于成熟稳定期,以保证新功能的发布、试错等顺利进行。——评论

打天下和守天下,需要不同的节奏,优先级不同,方案和指标也不同。——评论

量化风险成本,建立一致性目标,风险共担,减少非必要的沟通扯皮成本,比较好的双赢模式。
对照着看传统运维和开发的独立承担指标就属于零和博弈了,只有政治斗争能化解。——评论

  • 一文帮你理解 Google SRE 体系
  • 我对 SRE 的理解
  • 云原生背景运维转型之 SRE 实践
  • SRE的主要职责是什么?

开发阶段checklist

发表于 2020-12-23
  • 方案补全
  • 索引复查; 主库从库查询检查;事务检查;写语句死锁风险检查
    (更新语句要注意index_merge问题,避免死锁)
  • 缓存策略
  • 降级检查
  • 异常捕捉处理
  • 使用代码扫描工具
  • 错误码整理
  • 灰度策略;是否可回滚;是否可监控
  • 日志补全
  • 考虑异步操作
  • 监控数据状态,比如某些任务表,监控失败率及时发现异常
  • git仓库包含多模块项目,要注意是否都发版了或注意改动范围,否则会导致代码已经合并到主干了,其他的项目却没上线,没能及时发现问题
  • 测试用例
    • 注意实体转换的关键字段也需要覆盖用例,不然漏设置都无法察觉
  • 不能迷信单元测试,需要整条链路断点走一遍
    • 有时可能因为少传一个参数,参数是下一个方法用到的,局部的单元测试很难发现
  • 复查测试环境和生产环境的表结构是否一致
    • 有时为了方便快速在测试执行变更语句,而忘记同步到线上的变更SQL中
  • 上线申请事项记录

《枪炮、病菌与钢铁:人类社会的命运》-笔记

发表于 2020-12-19
  • 为什么最后带来枪炮、凶恶的病菌和钢铁的竟是欧洲人,而不是非洲人或印第安人?
  • 对本书来说,这样的一句话就是:“不同民族的历史遵循不同的道路前进,其原因是民族环境的差异,而不是民族自身在生物学上的差异。”

第一章 走上起跑线

  • 随着人类在澳大利亚/新几内亚的定居,现在人类已占据了可以居住的5个大陆中的3个。(在本书中,我始终把欧亚大陆算作一个大陆,我没有把南极大陆计算在内,因为南极大陆直到19世纪才有人到达,而且从来没有任何自给自足的居民。)这样就只剩下两个大陆:北美洲和南美洲。它们无疑是最后两个有人定居的大陆,这原因很明显,因为从旧世界到达美洲要么用船(甚至在印度尼西亚直到4万年前才有证据表明已有了船,而欧洲要晚得多才有船)去渡海,要么得先占有西伯利亚(直到大约2万年前才有人居住)以便通过白令陆桥。
  • 克罗维的猎人们在美洲向南推进,遇到了以前从未见过人的大型动物。他们可能发现这些美洲动物很容易杀死,于是就把它们消灭了。
  • 在可以住人的5个大陆中,北美洲和南美洲是人类史前史最短的两个大陆。
  • 在过去的700年中,唯一的无人居住、等待欧洲探险者光顾的地区就只剩下大西洋和印度洋中那些最偏远的岛屿(如亚速尔群岛和塞舌尔群岛)和南极大陆了。
  • 如果现代人类的确是在大约10万年前出现在非洲,然后向其他大陆扩散,那么其他地方在这期间积累起来的优势都会被一扫而光,从而使非洲人取得新的领先优势。而且,人类遗传的多样性以非洲为最高;也许更多样的人类集体会带来更多样的发明创造。不过,我们的这位考古学家那时可能会想:就本书的论题来说,究竟什么是“领先优势”?

第二章 历史的自然实验

  • 一般地说,人口越多,人口密度越高,技术和组织就越复杂,专业程度就越高。简言之,人口密度高时,只有一部分人最后成为农民,但他们被调动起来去专门从事集约型的粮食生产,从而生产出剩余粮食去养活非生产者。能够调动农民的非生产者包括首领、神职人员、官员和战士。

总结

  • 人口密度的重要性!

第三章 卡哈马卡的冲突

  • 现代最大的人口变迁是欧洲人对新大陆的移民,以及随之发生的对美洲土著(美洲印第安人)的征服、土著人数的减少或完全消失。
  • 阿塔瓦尔帕在卡哈马卡的出现突出了世界史上的一个关键因素:具有相当免疫力的入侵民族把疾病传染给没有免疫力的民族。天花、麻疹、流行性感冒、斑疹伤寒、腺鼠疫以及其他一些在欧洲流行的传染病,毁灭了其他大陆的许多民族,从而在欧洲人的征服中起了一种决定性的作用。
  • 我并不是要暗示历史上疾病的作用只限于为欧洲人的扩张铺平道路。疟疾、黄热病以及热带非洲、印度、东南亚和新几内亚的一些其他疾病,是欧洲在这些热带地区进行殖民的最大障碍。
  • 皮萨罗成功的直接原因包括:以枪炮、钢铁武器和马匹为基础的军事技术;欧亚大陆的传染性流行病;欧洲的航海技术;欧洲国家集中统一的行政组织和文字。
  • 为什么这种直接优势总是在欧洲一边,而不是在新大陆一边。为什么不是印加人发明枪炮和钢刀,骑上像战马一样的令人生畏的牲口,携带对欧洲人来说没有抵抗力的疾病,修造远洋船只和建立先进的行政组织,并能从几千年有文字记载的历史吸取经验?

总结

  • 病菌的影响

第四章 农民的力量

  • 从间接的意义说,粮食生产是枪炮、病菌和钢铁发展的一个先决条件。
  • 第一个因果关系是最直接的因果关系:能够获得更多的可消耗的卡路里就意味着会有更多的人。在野生的动植物物种中,只有很少一部分可供人类食用,或值得猎捕或采集。
  • 通往使某些民族能够征服另一些民族的近似因素(如枪炮、马匹和疾病)的因果关系链。例如,人类的各种各样疾病是在有许多适于驯化的动植物物种的地区演化的,这一部分是由于生产出的农作物和饲养的牲畜帮助养活了使流行疾病得以保持的人口稠密的社会;一部分是由于这些疾病是从驯化的动物身上的病菌演化而来。
  • 作为传统社会中的一个燃料来源,动物粪便也有其价值。此外,最大的驯化哺乳动物与驯化植物相互作用,以增加粮食产量,这表现在它们可以用来拉犁,从而使人们可以去耕种以前如用来耕种则代价太高的土地。
  • 许多狩猎采集社会里的人经常跑来跑去寻找野生食物,但农民必须留在他们的田地和果园附近。因此而产生的固定居所由于缩短了生育间隔期而促使人口变得更稠密起来。
  • 事实上,到处流浪的以狩猎采集为生的人通过哺乳期无月经、禁欲、杀婴和堕胎等办法,把孩子出生的间隔安排为大约每4年一个。相比之下,定居的部族由于没有在迁移途中携带小孩这种问题的限制,他们可以多生多养,只要养得活就行。
  • 评论:采集社会时期的人类同样采取抛弃弱小,这样做用现代评价标准就是灭绝人性,但在当时是不得不采取的手段。
  • 定居生活的另一个结果是人们可以把多余的粮食贮藏起来,因为如果人们不能留在附近看管贮藏的粮食,那么贮藏就是毫无意义的。虽然有些到处流浪的狩猎采集部族可能偶尔也把几天吃不完的食品收藏起来,但这种富余对他们几乎毫无用处,因为他们不能保护它。但贮藏的粮食对于养活不生产粮食的专门人材是必不可少的,而对于养活全村社的人肯定是必不可少的。因此,到处流浪的狩猎采集社会几乎没有或完全没有这类专职的专门人材,这种人材首先出现在定居社会中。
  • 一旦有了粮食储备,行政上层人物就可以控制别人生产的粮食,维护征税的权利,无需去养活自己,而以全部时间从事行政活动
  • 通过税收建立剩余粮食储备,除了养活国王和官员外,还能养活其他专职的专门人材。与征服战争关系最直接的是,剩余粮食储备可以用来养活职业军人。这是不列颠帝国最终打败新西兰武装精良的本土毛利人的决定性因素。
  • 至此,我已着重指出了作为粮食的农作物和家畜的直接和间接的价值。然而,它们还有其他用途,例如帮我们保暖和向我们提供有价值的材料。农作物和家畜生产出的天然纤维,可以用来做衣服、毯子、网和绳子。
  • 驯化的大型哺乳动物在19世纪铁路发展起来之前成为我们主要的陆路运输手段,从而进一步使人类社会发生了革命性的剧变。最后以蒙古人于公元13世纪和14世纪征服亚洲和俄罗斯的许多地方而达到高潮。只是由于在第一次世界大战中采用了卡车和坦克,马的作用才最后被取代,而不再是战争中主要的突击手段和快速运输的工具。
  • 在征服战争中同样重要的是在驯养动物的社会中演化的病菌。像天花、麻疹和流行性感冒这类传染病作为人类的专化病菌出现了,它们原是动物所感染的十分类似的祖代病菌由于突变而衍生出来的
    驯养动物的人成了这些新演化出来的病菌的第一个受害者,而这些人接着又逐步形成了对这些新的疾病的强大的抵抗力。当这些有部分免疫力的人与以前从来没有接触过这种病菌的人接触时,流行病于是产生了,99%的以前没有接触过这种病菌的人因之而丧命。从驯养的动物那里最后获得的病菌,在欧洲人对美洲、澳大利亚、南非和太平洋诸岛的土著的征服中起了决定性的作用。
  • 总之,动植物的驯化意味着人类的粮食越来越多,因而也就意味着人口越来越稠密。因此而带来的粮食剩余和(在某些地区)利用畜力运输剩余粮食,成了定居的、行政上集中统一的、社会等级分明的、经济上复杂的、技术上富有革新精神的社会的发展的先决条件。因此,能否利用驯化的动植物,最终说明了为什么帝国、知书识字和钢铁武器在欧亚大陆最早发展起来,而在其他大陆则发展较晚,或根本没有发展起来。

总结

  1. 粮食生产的重要
  2. 定居生活
  3. 病菌的产生

第五章 历史上的穷与富

  • 为什么粮食生产首先在看似相当贫瘠的土地上形成,只是到后来才在今天最肥沃的农田和牧场发展起来?所有这些问题都涉及不同的发展阶段,而正是这些不同的发展阶段决定了哪些民族成了历史上的贫穷民族,哪些民族成了历史上的富有民族。
  • 世界上只有几个地区发展了粮食生产,而且这些地区发展粮食生产的时间也差异甚大。一些邻近地区的狩猎采集族群从这些核心地区学会了粮食生产,而其他一些邻近地区的族群则被来自这些核心地区的粮食生产者所更替了——更替的时间仍然差异甚大。最后,有些族群虽然生活在一些生态条件适于粮食生产的地区,但他们在史前期既没有发展出农业,也没有学会农业;他们始终以狩猎采集为生,直到现代世界最后将他们淘汰。在粮食生产上具有领先优势的那些地区里的族群,因而在通往枪炮、病菌和钢铁的道路上也取得了领先的优势。其结果就是富有社会与贫穷社会之间一系列的长期冲突。

第六章 种田还是不种田

  • 由于粮食生产较狩猎采集有更高的每英亩可摄入卡路里数,人口密度也倾向于更高。而在另一方面,整个更新世后期的人口密度本来就在持续上升,归因于人类采集加工野生食物的技术进步。随着人口增长,粮食生产越来越受青睐,因为它提供了给养所有人所必需的粮食增产。这就是说,采纳粮食生产为所谓的自身催化过程提供了例证——这是一个在正反馈循环中自身催化的过程,这个过程一旦开始,速度就越来越快。人口密度的逐步增加,迫使人们去奖励那些无意中增加了粮食产量的人,以获得更多的粮食。一旦人们开始生产粮食并过定居的生活,他们就能够缩短生育间隔期,生出更多的人来,从而也就需要更多的粮食。粮食生产与人口密度之间的这种双向关系,说明了一种矛盾的现象,即粮食生产一方面增加了每英亩可摄入的卡路里的数量,一方面却又使这些粮食生产者的营养不及他们所承继的那些以狩猎采集为生的人。这种矛盾之所以产生,是因为人口密度的增加速度要稍高于粮食的增加速度。

第七章 怎样识别杏仁

  • 植物驯化可以定义为:栽种某一植物并由此有意或无意地使其发生不同于其野生祖先的、更有利于人类消费的遗传变化。
  • 不同的植物由于十分不同的或甚至相反的特点而得到选择。有些植物(如向日葵)由于大得多的种子而得到选择,而另一些植物(如香蕉)则由于种子小或没有种子而得到选择。选择生菜则取其茂盛的叶子而舍其种子或果实;选择小麦和向日葵则取其种子而舍其叶;选择南瓜则取其果实而舍其叶。特别有意思的是,由于不同的目的,对于一种野生植物可以有不同的选择,从而产生了外观十分不同的作物。
  • 新月沃地的小麦和大麦是被称之为谷物(属禾本科)的那类作物的代表,而新月沃地的豌豆和兵豆则是豆类(属豆科,包括大豆)的代表。谷类作物的优点是生长快,碳水化合物含量高,每公顷耕地可产1吨食物。因此,今天的谷物占人类消耗的全部卡路里的半数以上,并包括现代世界上12种主要作物中的5种(小麦、玉米、稻米、大麦和高粱)。许多谷类作物蛋白质含量低,但这一缺陷可以由豆类来弥补,因为豆类的蛋白质通常达25%(大豆为38%)。因此,谷物和豆类一起为均衡饮食提供了许多必不可少的成分。

第八章 问题在苹果还是在印第安人

  • 人类历史的主要事实之一,是西南亚的那个叫做新月沃地的地区(因其在地图上的新月状高地而得名,见图8.1)在人类发展早期的重要性。那个地区似乎是包括城市、文字、帝国以及我们所说的文明(不论是福是祸)在内的一连串新情况发生的地方。而所有这些新情况之所以发生,都是由于有了稠密的人口,有了剩余粮食的贮存,以及可以养活不从事农业的专门人材,凡此种种之所以可能又都是由于出现了以作物栽培和牲口饲养为形式的粮食生产。粮食生产是新月沃地出现的那些重要新事物中的第一个新事物。因此,如果想要了解现代世界的由来,就必须认真对待这样的问题,即为什么新月沃地的驯化动植物使它获得了如此强大的领先优势。
  • 原来地中海气候带,尤其是在新月沃地那个地区,具有胜过其他地中海气候带的5个有利条件。第一,欧亚大陆西部显然是世界上属于地中海气候带的最大地区。因此,那里的野生动植物品种繁多,超过了澳大利亚西南部和智利这些比较小的地中海气候带。第二,在地中海气候带中,欧亚大陆西部的地中海气候带的气候变化最大,每一季、每一年气候都有不同。这种气候变化有利于植物群中数量特别众多的一年生植物的演化。物种多和一年生植物多这两个因素结合起来,就意味着欧亚大陆西部的地中海气候带显然是一年生植物品种最繁多的地区。智利的地中海型气候带只有2种,加利福新月沃地的地中海气候带的第三个有利条件,是它在短距离内高度和地形的富于变化。它的高度从地球上的最低点(死海)到18000英尺的高山(在德黑兰附近),应有尽有,从而保证了环境的相应变化,也因此保证品种繁多的野生植物可供成为作物的祖先。
  • 地球上不同地区的当地作物并不是同样多产的。
  • 采纳粮食生产涉及粮食生产的生活方式与狩猎采集的生活方式之间的竞争。而受到损害的则是那些没有这种机会或意愿的部落。因此,新几内亚土生土长的粮食生产所受到的限制与新几内亚的族群没有任何关系,而是与新几内亚的生物区系和环境有着最密切的关系。
  • 事实上,在整个大陆和其他一些包含数以百计的互相竞争的广大地区,有些社会对新事物可能比较开放,有些社会对新事物可能比较抵制。那些接受新作物、新牲畜或新技术的社会因而可能吃得更好,繁殖得更快,从而取代、征服或杀光那些抵制新事物的社会。
  • 澳大利亚这个据称最“落后的”大陆很好地说明了这个问题。澳大利亚东南部是这个大陆上水源充足、最适合粮食生产的地方。那里的土著社会在最近的几千年里似乎一直在按照一种可能最终导致本地粮食生产的发展轨迹在演化。它们已经建立了过冬的村庄。它们已经开始加强利用它们的环境,建造渔栅、编织渔网,甚至挖掘长长的水渠来从事渔业生产。如果欧洲人没有在1788年向澳大利亚殖民,从而中途破坏了那个独立的发展轨迹,那么澳大利亚土著也许不消几千年就可成为粮食生产者,照料一池池驯化了的鱼,种植驯化了的澳大利亚薯蓣和小籽粒的禾本科植物。根据这一点,我现在就能够回答包含在本章标题里的那个问题。我提出的那个问题是:北美印第安人未能驯化北美苹果的原因是在印第安人还是在苹果。
  • 在欧洲人到达时印第安人仍未能驯化北美的苹果,其原因不在印第安人,也不在苹果。就苹果驯化必要的生物条件而言,北美印第安农民和欧亚大陆农民一样,北美的野生苹果也和欧亚大陆的野生苹果一样。事实上,本章读者现在正在津津有味地吃着的从超市上买来的苹果,有些品种就是不久前将欧亚大陆的苹果同北美的野生苹果进行杂交而培育出来的。印第安人未能驯化苹果的原因却是在于印第安人所能得到的整个野生动植物组合。这个组合的微弱的驯化潜力,就是北美粮食生产很晚才开始的主要原因。

总结

  1. 新月沃地(地中海气候)
  2. 美洲整个野生动植物组合,微弱的驯化潜力。

第九章 斑马、不幸的婚姻和安娜·卡列尼娜原则

  • 驯养的大型哺乳动物对那些拥有它们的人类社会产生重大影响的那许多方面。最显著的是,这些动物提供了肉食、奶制品、肥料、陆上运输、皮革、军事突击手段、犁具牵引、毛绒以及使先前没有抵抗力的民族失去生命的病菌。
  • 各大陆之间野生祖先的这种十分不均匀的分布,成了欧亚大陆人而不是其他大陆的人最后得以拥有枪炮、病菌和钢铁的一个重要原因。
  • 欧亚大陆何以一直是大型哺乳动物驯化的主要场所,对这个问题的部分解释是:它是一个一开始就拥有最多的可供驯化的野生哺乳动物的大陆,在过去的40000年中,那里这样的动物因绝种而消失的也最少。
  • 在驯化动物方面现代尝试的失败,提供了最后一个证据,表明过去在驯化剩下的大批候补野生动物方面的失败是由于这些动物本身的缺点,而不是由于古代人的缺点。
  • 每一次某种动物在吃某种植物或另一种动物时,食物生物量转换为取食者生物量的效率远远低于100%:通常在10%左右。就是说,要花费10000磅左右的玉米才能喂养出一头1000磅重的牛。
  • 动物的日常食物、生长速度、交配习惯、性情、容易受惊的倾向以及群居组织的几个不同的特点。只有很少一部分野生哺乳动物由于在上述所有这些方面都能协调一致而最终得以和人类结成美满的婚姻。
  • 欧亚大陆的民族碰巧比其他大陆的民族继承了多得多的可驯化的大型野生的哺乳类食草动物。这一结果及其为欧亚大陆社会带来的全部利益,来自哺乳动物地理学、历史和生物学这3个基本事实。
  • 欧亚大陆由于其广大面积和生态的多样性,一开始就拥有最多的可供驯化的候补动物。其次,澳大利亚和美洲,而不是欧亚大陆或非洲,在更新世晚期动物灭绝的大规模浪潮中失去了它们大多数可供驯化的候补动物——这可能是因为前两个大陆的哺乳动物不幸首先突然接触到人类,而且这时已是我们的进化史的后期阶段,我们的狩猎技巧已经得到了高度的发展。

总结

  • 欧亚大陆的民族碰巧比其他大陆的民族继承了多得多的可驯化的大型野生的哺乳类食草动物。

第十章 辽阔的天空与偏斜的轴线

  • 轴线走向的差异所产生的巨大的、有时是悲剧性的后果。轴线走向影响了作物和牲口的传播速度,可能还影响文字、车轮和其他发明的传播速度。
  • 某种作物的迅速传播可能不但抢先阻止了同一植物的野生祖先在其他某个地方的驯化,而且也阻止了有亲缘关系的野生植物的驯化。
  • 许多不同的现象归结为同一个结论:粮食生产从西南亚向外传播的速度要比在美洲快,而且也可能比在非洲撒哈拉沙漠以南的地区快。这些现象包括:粮食生产完全未能到达某些生态条件适合于粮食生产的地区;粮食生产传播的速度和选择性方面存在着差异;以及最早驯化的作物是否抢先阻止了对同一种植物的再次驯化或对近亲植物的驯化方面也存在着差异。
  • 提出所有这些差异,并不就是说分布很广的作物是值得赞美的,也不是说这些差异证明了欧亚大陆早期农民具有过人的智慧。这些差异只是反映了欧亚大陆轴线走向与美洲或非洲大陆轴线相比较的结果。历史的命运就是围绕这些轴线旋转的。

第十一章 牲畜的致命礼物

  • 粮食生产这个终极原因是如何导致病菌、文化修养、技术和集中统一的政府这些直接原因的。
  • 过去战争中的胜利者并不总是那些拥有最优秀的将军和最精良的武器的军队,而常常不过是那些携带有可以传染给敌人的最可怕病菌的军队。
  • 虽然被那些杀人不眨眼的西班牙征服者杀死的印第安人不计其数,但凶恶的西班牙病菌杀死的印第安人却要多得多。为什么在欧洲和美洲之间这种可怕的病菌的交流是这样不对等?为什么印第安人的疾病没有大批杀死西班牙入侵者,并传回欧洲,消灭掉欧洲95%的人口?同样的问题也出现在被欧亚病菌大批杀死的其他土著身上,以及企图征服亚非热带地区的欧洲人身上。
  • 作为流行病有几个共同的特点。首先,它们从一个受感染的人迅速而高效地传给近旁健康的人,结果使整个人口在很短时间内受到感染。其次,它们都是“急性”病:在很短时间内,你要么死掉,要么完全康复。第三,我们当中的确获得康复的那些幸运的人产生了抗体,使我们在很长时间内,也可能是一辈子不用担心这种病会复发。最后,这些病往往只在人类中传播;引起这些病的病菌往往不是生活在土壤中或其他动物身上。所有这4个特点也适用于美国人所认为的那些习见的儿童急性传染病,其中包括麻疹、风疹、急性腮腺炎、百日咳和天花。这4个特点结合起来往往造成了某种疾病的流行,其原因不难理解。简单地说,情况是这样的:病菌的迅速传播和症状的迅速发展,意味着当地人口中的每一个人很快就受到感染,之后不久他或者死去,或者康复并获得免疫力。仍然会受到感染的人都不会活下来。但由于这种病菌除了在活人体内是不可能生存的,所以人死了这种病也就消失了,直到又一批儿童达到易受感染的年纪——直到一个受到感染的外来人使一场流行病重新开始。
  • 关于这些疾病是怎样流行起来的,有一个典型的事例是大西洋上叫做法罗群岛[插图]的与世隔绝的岛屿上的麻疹病史。1781年,一场严重的麻疹流行病到达法罗群岛,接着又消失了,其后该群岛就不再有麻疹发生,直到1846年,一个受到感染的木匠从丹麦坐船到来。不出3个月,法罗群岛的几乎全部人口(7782人)都得了麻疹,于是有的人死去,有的人康复,麻疹病毒又一次消失,直到下一次流行。一些研究表明,麻疹可能会在任何少于50万人的人口中消失。只有在比较多的人口中,这种病才会从一个地区转移到另一个地区,直到原先受感染地区里出生的婴儿达到足够的数目,麻疹又会卷土重来。适用于法罗群岛上麻疹的情况,也适用于世界上其他一些我们所熟悉的急性传染病。为了维持自身的存在,这些病需要有足够多的人口,足够拥挤的稠密人口,这样,到这种病不然就会衰退的时候,又有一大批易受感染的儿童成为感染对象。因此,麻疹和一些类似的疾病也叫做人群病。
  • 人群病不可能在小群狩猎采集族群和刀耕火种的农民中存在下去。现代亚马孙河地区印第安人和太平洋岛民的悲惨经历表明,整个小部落可能被一个外来人带来的一种流行病几乎全部消灭——因为这个小部落中没有一个人有任何抵抗这种病菌的抗体。小部落人口少,这一点不但说明了为什么他们承受不住从外面带来的流行病,而且也说明了为什么他们没有能演化出自己的流行病去回敬外来人。
  • 为什么农业的出现会成为我们人群传染病形成的开端?其中一个原因前面已经提到,那就是农业比狩猎采集的生活方式维持了高得多的人口密度——平均要高10倍到100倍。另外,狩猎采集族群经常变换营地,留下了一堆堆排泄物,上面聚集了大量病菌和寄生虫的幼虫。但农民是定居的,他们生活在自己排放出来的污水之中,从而为病菌从一个人的身体进入另一个人的饮用水源提供了捷径。有些农业人口把自己的粪便收集起来,当作肥料撒到人们劳动的田里,从而使粪便中的病菌和寄生虫去感染新的受害者变得甚至更加容易。
  • 在动物中,流行病同样需要稠密的大种群,而不是只去折磨任何某一只动物:这些流行病主要发生在需要有大的种群的群居动物中。因此,当我们驯养牛和猪这类群居动物时,它们已经受到了一些流行病的折磨,只不过在等待着转移给我们罢了。所有这些病菌仍然处在向人类病原体演化的早期阶段。
  • 美洲之所以未能出现流行的致命的人群病的主要原因就一定会变得很清楚。这个问题就是,想象一下这些疾病可能会从什么病菌演化而来?我们已经看到,欧亚大陆的人群病是从欧亚大陆驯化的群居动物的疾病演化而来的。尽管欧亚大陆有许多这样的动物,但在美洲驯化的动物只有5种:墨西哥和美国西南部的火鸡、安第斯山脉地区的美洲驼/羊驼和豚鼠、热带南美的美洲家鸭和整个美洲的狗。反过来,我们也看到,新大陆驯化动物的这种极端缺乏,反映了用以启动驯化的野生动物的缺乏。在大约13000年前上一次冰期结束时,美洲有大约80%的大型野生哺乳动物便已灭绝了。
  • 源于动物的疾病在历史上的重要性,远远超过了旧大陆与新大陆之间的冲突。欧亚大陆的病菌在大量消灭世界上其他许多地方的土著民族方面起了关键的作用。
  • 毫无疑问,欧洲人在武器、技术和行政组织方面拥有对他们所征服的大多数非欧洲民族的巨大优势。但仅仅这种优势还不能完全说明开始时那么少的欧洲移民是如何取代美洲和世界上其他一些地区那么多的土著的。如果没有欧洲送给其他大陆的不祥礼物——从欧亚大陆人与家畜的长期密切关系中演化出来的病菌,这一切也许是不会发生的。

总结

  1. 流行病(人群病)

第十二章 蓝图和借用字母

  • 知识带来力量。因此,文字也给现代社会带来了力量,用文字来传播知识可以做到更准确、更大量和更详尽,在地域上可以做到传播得更远,在时间上可以做到传播得更久。
  • 文字同武器、病菌和集中统一的行政组织并驾齐驱,成为一种现代征服手段。
  • 发明的传播形式有一系列形式。形式的一端是“蓝图复制”,就是对现有的一幅详尽的蓝图进行复制或修改。另一端是“思想传播”,就是仅仅把基本思想接受过来,然后必须去重新创造细节。
  • 虽然蓝图的复制和修改是传播技术的最直接的选择,但有时候这种选择不一定能够得到。蓝图可能被隐藏起来,而且不是深于此道的人对蓝图也不一定能够读懂。对于在远处某个地方发明了某个东西,人们可能有所耳闻,但详细情况则可能无从知晓。也许所知道的只是这样的基本思想:某人以某种方法成功地取得了某种最后的成果。然而,知道了这一点,可能就是通过思想传播去启发别人设计他们自己的取得此种成果的途径。
  • 大多数有文字的社会之所以获得文字,或是通过向邻近的社会借用,或是由于受到它们的启发而发明出文字,而不是靠自己独立创造出来的。
  • 文字史引人注目地表明了类似的情况:地理和生态条件影响了人类发明的传播。

第十三章 需要之母

  • 事实上,许多发明或大多数发明都是一些被好奇心驱使的人或喜欢动手修修补补的人搞出来的,当初并不存在对他们所想到的产品的任何需要。一旦发明了一种装置,发明者就得为它找到应用的地方。只有在它被使用了相当一段时间以后,消费者才会感到他们“需要”它。还有一些装置本来是只为一个目的而发明出来的,最后却为其他一些意料之外的目的找到了它们的大多数用途。
  • 寻求使用的这些发明包括现代大多数重大的技术突破,从飞机和汽车到内燃机和电灯泡再到留声机和晶体管,应有尽有。了解到这一点,也许会令人感到吃惊。因此,发明常常是需要之母,而不是相反。
  • 有一项大发明最终得到使用,就会有不计其数的其他发明得不到使用。甚至有些发明当初本来是为了满足特定的需要而设计的,后来可能在满足意外需要方面证明是更有价值的。虽然詹姆士·瓦特设计他的蒸汽机是为了从煤矿里抽水,但它很快就为棉纺厂提供动力,接着又(以大得多的利润)推动着机车和轮船前进。
  • 技术的发展是长期积累的,而不是靠孤立的英雄行为;技术在发明出来后大部分都得到了使用,而不是发明出来去满足某种预见到的需要。
  • 一旦发明家发现了一项新技术的用途,下一步就是说服社会来采用它。仅仅有一种更大、更快、更有效的工作装置还不能保证人们会乐于接受。无数的此类技术要么根本没有被采用,要么只是在长期的抵制之后才被采用。
  • 即使在同一个大陆上,各社会之间在发展和接受新事物方面也是大不相同的。即使是在同一个社会内,在时间上也会有所不同。在任何时候,在任何大陆上都有富于创新精神的社会,也有保守的社会。此外,在同一个地区内,对新事物的接受能力迟早会产生波动。
  • 一个用途广泛的发明在一个社会出现后,接着它便往往以两种方式向外传播。一种方式是:其他社会看到或听说了这个发明,觉得可以接受,于是便采用了。另一种方式是:没有这种发明的社会发现与拥有这种发明的社会相比自己处于劣势,如果这种劣势大到一定程度,它们就会被征服并被取而代之。
  • 在发明的传播中最容易接受发明的社会是大陆上的一些根基深厚的社会。在这些社会中技术发展最快,因为它们不但积累了自己的发明,而且也积累了其他社会的发明。
  • 我们往往想当然地认为,有用的技术一旦获得,就必然会流传下去,直到有更好的技术来取而代之。事实上,技术不但必须获得,而且也必须予以保持,而这也取决于许多不可预测的因素。任何社会都要经历一些社会运动和时尚,此时一些没有经济价值的东西变得有价值起来,而一些有用的东西也变得暂时失去了价值。今天,当地球上几乎所有社会相互联系在一起的时候,我们无法想象某种时尚会发展到使人们竟然抛弃一项重要的技术。一个著名的例子是日本放弃枪支。
  • 在同时代的欧洲也有一些鄙视枪支并竭力限制枪支使用的统治者。但这些限制措施在欧洲并未发生多大作用,因为任何一个欧洲国家,哪怕是短暂地放弃了火器,很快就会被用枪支武装起来的邻国打垮。只是因为日本是一个人口众多的孤立的海岛,它才没有因为拒绝这种具有巨大作用的新军事技术而受到惩罚。日本拒绝枪支和中国抛弃远洋船只(以及抛弃机械钟和水力驱动纺纱机),是历史上孤立或半孤立社会技术倒退的著名例子。
  • 在这漫长的加速发展的历史中,我们可以挑出两次意义特别重大的飞跃。第一次飞跃发生在100000年到50000年前,其所以能够发生,大概是由于我们身体的遗传变化,即人体的现代解剖学进化使现代语言或现代大脑功能或两者成为可能。这次飞跃产生了骨器、专用石器和复合工具。第二次飞跃来自我们选定的定居生活方式,这种生活方式在世界的不同地区发生的时间不同,在有些地区早在13000年前就发生了,在另一些地区即使在今天也还没有发生。就大多数情况而言,选定定居的生活方式是同我们采纳粮食生产联系在一起的,因为粮食生产要求我们留在我们的作物、果园和剩余粮食储备的近旁。
  • 定居生活对技术史具有决定性的意义,因为这种生活使人们能够积累不便携带的财产。四处流浪的狩猎采集族群只能拥有可以携带的技术。如果你经常迁移而且又没有车辆或役畜,那么你的财产就只能是小孩、武器和最低限度的其他一些便于携带的小件必需品。你在变换营地时不能有陶器和印刷机之类的累赘。这种实际困难或许可以说明何以有些技术出现得惊人地早,接着停了很长时间才有了进一步的发展。

总结

  1. 发明常常是需要之母,而不是相反。
  2. 不接受新事物,很可能会被征服并被取而代之。
  3. 日本放弃枪支;
  4. 定居生活的意义;

总结(重点!!!)

  • 现在,让我们来总结一下,粮食生产开始的时间、技术传播的障碍和人口的多寡这3大因素的变化,是怎样直接导致我们所看到的各大陆之间在技术发展方面的差异的。
  • 欧亚大陆(实际上也包括北非在内)是世界上最大的陆块,包含有数量最多的互相竞争的社会。它也是最早开始拥有粮食生产的两个中心的陆块,这两个中心就是新月沃地和中国。它的东西向的主轴线,使欧亚大陆一个地区采用的许多发明得以较快地传播到欧亚大陆具有相同纬度和气候的其他地区的社会。它的沿次轴线(南北轴线)的宽度,同美洲巴拿马地峡的狭窄形成了对照。它没有把美洲和非洲的主轴线切断的那种严峻的生态障碍。因此,对技术传播的地理和生态障碍,在欧亚大陆没有在其他大陆那样严峻。由于所有这些因素,后更新世技术的加速发展,在欧亚大陆开始得最早,从而导致了本地最大的技术积累。
  • 北美洲和南美洲在传统上被看作是两个不同的大陆,但它们连接在一起已有几百万年之久,有着类似的历史问题,因此可以把它们放在一起来考虑,以便和欧亚大陆相比较。美洲构成了世界上第二大的陆块,但比欧亚大陆小得多。不过,它们在地理和生态上却支离破碎:巴拿马地峡宽不过40英里,等于在地理上把美洲给腰斩了,就像这个地峡上的达里安雨林和墨西哥北部的沙漠在生态上所做的那样。墨西哥北部的沙漠把中美洲人类的先进社会同北美洲的社会分隔开了,而巴拿马地峡则把中美洲的先进社会同安第斯山脉地区和亚马孙河地区的社会分隔开了。此外,美洲的主轴线是南北走向,从而使大部分的技术传播不得不逆纬度(和气候)的梯度而行,而不是在同一纬度内发生。例如,轮子是在中美洲发明的,而美洲驼是不迟于公元前3000年在安第斯山脉中部驯化的,但过了5000年,美洲的这唯一的役畜和唯一的轮子仍然没有碰头,虽然中美洲玛雅社会同印加帝国北部边界之间的距离(1200英里)比同时享有轮子和马匹的法国同中国之间6000英里的距离要短得多。在我看来,这些因素足以说明美洲在技术上落后于欧亚大陆这个事实。
  • 非洲撒哈拉沙漠以南地区是世界上第三大的陆块,但比美洲小得多。在人类的大部分历史中,到欧亚大陆比到美洲容易多了,但撒哈拉沙漠仍然是一个主要的生态障碍,把非洲撒哈拉沙漠以南地区同欧亚大陆和北非隔开。非洲的南北轴线造成了欧亚大陆与非洲撒哈拉沙漠以南地区之间以及撒哈拉沙漠以南地区本身内部技术传播的又一障碍。作为后一障碍的例子,陶器和炼铁术出现在或到达非洲撒哈拉沙漠以南的萨赫勒地带(赤道以北),至少同它们到达西欧一样早。然而,陶器直到公元元年才到达非洲的南端,而冶金术在从欧洲由海路到达非洲南端时,还不曾由陆路传播到那里。最后,澳大利亚是最小的一个大陆。澳大利亚大部分地区雨量稀少,物产贫乏,因此,就其所能养活的人口来说,它实际上就显然甚至更小。它也是一个最孤立的大陆。加之,粮食生产也从来没有在澳大利亚本地出现过。这些因素加在一起,就使澳大利亚成为唯一的在现代仍然没有金属制品的大陆。
  • 人口多意味着搞发明的人和互相竞争的社会也多。
  • 各大陆之间在面积、人口、技术传播的难易程度和粮食生产的开始时间等方面存在着差异,而这些差异又对技术的出现产生了种种影响,但所有这些影响都被夸大了,因为技术可以催化自身。欧亚大陆在开始时的巨大优势因此就变成了自1492年[插图]起的巨大的领先优势——其原因是欧亚大陆独特的地理条件,而不是那里的人特别聪明。我所认识的那些新几内亚人中就有潜在的爱迪生。不过,他们把自己的聪明才智用于解决适合自己情况的技术问题:不靠任何进口物品而在新几内亚丛林中生存的问题,而不是发明留声机的问题。

第十四章 从平等主义到盗贼统治

  • 有些社会最早实现了集中统一的政府和有组织的宗教,而这些社会的子孙后代最后主宰了现代世界。政府和宗教就是这样结合起来发挥了作用,它们是产生历史最广泛模式的4组主要的直接动力之一,另外3组动力是病菌、文字和技术。

  • 超越族群的那些阶段中的第一个阶段是部落。部落与族群的区别是它比较大(一般有几百人,而不是几十人),而且通常有固定的居住地。然而,有些部落,甚至有些由酋长管辖的部落,却是由随季节而迁移的牧人组成的部落是由不止一个的得到正式承认的亲属群体所组成,这些群体称为氏族,氏族之间互相通婚。土地属于某个氏族,不属于整个部落。然而,部落的人数仍然很少,每一个人都知道另外每一个人的名字和他的各种亲属关系。对人类其他类型的群体来说也是一样,在一个群体里如要做到彼此了解,这个群体的人数最多似乎以“几百人”为宜。

  • 早期的国王本人就是国家宗教的领袖,否则就另外设立一个大祭司。美索不达米亚的寺庙不但是宗教活动的中心,而且也是经济再分配、文字和手工技术的中心。

  • 同样明显的是,国家在与较简单的实体发生冲突时所以能取得胜利,部分原因是国家拥有武器和其他技术方面的优势,同时也拥有人口数量上的优势。但酋长管辖地和国家还有另外两个固有的潜在优势。首先,中央决策者拥有集中军队和资源的优势。其次,许多国家的官方宗教和爱国热忱使它们的军队在作战中视死如归,心甘情愿地为国捐躯。在现代国家中,乐于为国牺牲的思想由我们的学校、教会和政府大力灌输给我们公民,使我们忘记了它标志着同以往人类历史的彻底决裂。

  • “几百人”是个界限,在这个界限内每个人能够认识另外每个人,一旦超过这个界限,越来越多的两人组合就成了一对对没有亲属关系的陌生人了。当陌生人打架时,在场的人很少会是打架双方的朋友或亲属,没有什么私利要他们去制止打架。相反,如果许多旁观者是打架一方的朋友或亲属,他们就会站在他的一边,这样,本来是两个人的打架结果就逐步升级为一场乱哄哄的群殴。因此,一个继续把冲突交给全体成员去解决的大型社会必然会分崩离析。随着人口的增加,共同决策越来越难以做到。

  • 部落之间进行征服或兼并以达到了酋长管辖地的规模,酋长管辖地之间进行征服或兼并以达到了国家的规模,国家之间进行征服或兼并以形成帝国。更一般地说,大的单位可能拥有对各个小的单位的某种优势。小型社会的领袖和大型社会的领袖一样,珍惜自己的独立和特权。合并的发生不外乎下面的两种方式之一:在外力的威胁下合并,或通过实际的征服。

  • 战败民族的命运取决于人口的密度,这有3种可能的后果:凡是人口密度很低的地方,就像在狩猎采集族群占据的地区所常见的那样,战败群体的幸存者只要离开他们的敌人远一点就行了。新几内亚和亚马孙河地区游牧部族之间战争的结果往往就是这样。凡是人口密度中等的地方,就像粮食生产部落占据的地区那样,没有大片空旷的地方可以让战败族群的幸存者逃避。但是,

  • 没有集约型粮食生产的部落社会不使用奴隶,也不能生产出可以作为很大一部分贡品的足够的剩余粮食。因此,战败部落的幸存者对胜利者来说毫无用途,除非娶他们的女人为妻。战败的男人都被杀死了,他们的地盘也可能为胜利者所占有。

  • 凡是人口密度高的地方,就像国家或酋长管辖地所占有地区那样,被打败的人仍然无处可逃,但胜利者不杀死他们而有了利用他们的两种选择。由于酋长管辖地社会和国家社会已出现了经济专业化,被打败的人可以当奴隶来使用,就像在《圣经》时代通常发生的那样。或者,由于许多这样的社会已经有了能够生产大量剩余粮食的集约型粮食生产系统,胜利者可以让战败者仍然从事原来的劳作,只是剥夺了他们的政治自主权,要他们定期地用粮食或货物来纳贡,并把他们的社会合并入获胜的国家或酋长管辖地。

  • 粮食生产及社会之间的竞争与混合,产生了征服的直接原动力:病菌、文字、技术和中央集权的政治组织。这些原动力往往是相互联系着一起出现的,不过这种联系并不是绝对的。

总结

  1. 中央集权政治组织
  2. 精神控制手段
  3. 大型社会必须要有复杂组织主要有四个原因:陌生人之间的人际冲突调节,共同决策越来越难做到,货物交换变得复杂,土地面积不变人口的增加使得人们从别的地区获得需要品。

第十五章 耶利的族人

  • 澳大利亚是最干燥、最小、最平坦、最贫瘠、气候最变化无常、生物品种最稀少的大陆。
  • 由于新几内亚人是粮食生产者,不是以狩猎采集为生的人,所以他们的平均人口密度比澳大利亚人高得多:新几内亚的面积只有澳大利亚的十分之一,但它所养活的当地人口却数倍于澳大利亚。
  • 新几内亚有几个不利于它的生物因素和地理因素。首先,虽然本地的粮食生产的确是在新几内亚高原地区出现的,但我们已在第八章中看到,它产出的蛋白质很少。当地的主食都是低蛋白的根用作物,而仅有的驯化动物(猪和鸡)的产量又太低,不能为人们提供大量的蛋白质。既然无法把猪或鸡套起来拉车,高原地区的居民除了两臂力气外,仍然没有其他动力来源,而且也未能发展出流行疾病以击退终于侵入的欧洲人。对高原地区人口数量的第二个限制,是能够利用的土地面积有限:新几内亚高原地区只有几处宽阔的谷地(最显著的是瓦吉谷地和巴利姆谷地)能够养活稠密的人口。第三个限制是这样的现实,即4000英尺至9000英尺之间的中间山地森林地带,是新几内亚唯一适于集约型粮食生产的高程地带。在9000英尺以上的新几内亚高山生境根本没有任何粮食生产,在4000英尺至1000英尺之间的山坡上几乎没有什么粮食生产,而在低地地区也只有低密度的刀耕火种农业。因此,在不同海拔高度专门从事不同类型粮食生产的一些社会之间对粮食的大规模经济交换,在新几内亚从未发展起来。在安第斯山脉、阿尔卑斯山脉和喜马拉雅山脉,这种交换不但向各个海拔高度的人提供一种比较均衡的饮食,从而增加了这些地区的人口密度,而且也促进了地区的经济和政治一体化。
  • 任何在澳大利亚本地出现的粮食生产,都可能会由于可驯化的动植物的缺乏以及土壤贫瘠和气候恶劣而受到限制。流浪的生活、狩猎采集的生活方式以及对住所和财物的最小的投资,是因受澳大利亚厄尔尼诺南移影响而无法预知可以得到何种资源时的明智的适应行为。在当地条件恶化时,土著居民只是迁往一个暂时条件较好的地区。

总结

  • 新几内亚地区环境比澳大利亚大陆好,相对比较”先进”,但还是比不上亚欧大陆。
  • 总结一下,新几内亚社会发展落后的几大原因:一是可供驯化的动植物品种少且蛋白质低;二是可利用的土地资源少;三是(基于以上原因)人口少密度低,且分散化严重,彼此争斗不休;四是外部地理的隔绝,难以传入借鉴先进的技术和思想。

第十六章 中国是怎样成为中国人的中国的

  • 近代民族大熔炉这一普遍现象的重大例外是世界上人口最多的国家——中国。
  • 中国过去也曾经是形形色色、变化多端的,就像其他所有人口众多的国家现在仍然表现出来的那样。中国的不同之处仅仅在于它在早得多的时候便已统一了。
  • 虽然中国的南北梯度妨碍了作物的传播,但这种梯度在中国不像在美洲或非洲那样成为一种障碍,因为中国的南北距离较短;同时也因为中国的南北之间既不像非洲和墨西哥北部那样被沙漠阻断,也不像中美洲那样被狭窄的地峡隔开。倒是中国由西向东的大河(北方的黄河、南方的长江)方便了沿海地区与内陆之间作物和技术的传播,而中国东西部之间的广阔地带和相对平缓的地形最终使这两条大河的水系得以用运河连接起来,从而促进了南北之间的交流。所有这些地理因素促成了中国早期的文化和政治统一,而西方的欧洲虽然面积和中国差不多,但地势比较高低不平,也没有这样连成一体的江河,所以欧洲直到今天都未能实现文化和政治的统一。
  • 在中国,有些新事物是由南向北传播的,尤其是铁的冶炼和水稻的栽培。但主要的传播方向是由北向南。这个趋向在文字上表现得最为明显:欧亚大陆西部曾产生过太多的书写系统,如苏美尔的楔形文字、埃及的象形文字、赫梯文字[插图]、弥诺斯文字和闪语字母。中国则不同,它只产生了一种得到充分证明的书写系统。它在华北得到完善,并流传各地,预先制止了任何其他不成熟的书写系统的发展或取而代之,最后演化为今天仍在中国使用的文字。
  • 中国的3个最早的王朝——夏、商、周都是在公元前第二个一千年间在华北兴起的。现存的公元前第一个千年的著作表明,当时的华夏族就已常常(就像今天许多人仍然在做的那样)觉得在文化上比非华夏族的“野蛮人”优越,而华北人也常常甚至把华南人也看作野蛮人。
  • 由于东亚最早的农民所取得的成就,中国成了中国人的中国。

总结

  1. 南北距离较短,且无地理隔离
  2. 很早就文化统一
  3. 有粮食生产

第十七章 驶向波利尼西亚的快艇

  • 南岛语系的4个语族中有3个集中在台湾,这表明台湾就是今天各地南岛语的故乡,在过去几千年的大部分时间里,这些语言一直在台湾使用,因此有最长的时间来产生分化。这样看来,从马达加斯加到复活节岛,所有其他南岛语可能都起源于台湾向外的人口扩张。
  • 南岛人在新几内亚地区扩张的结果与在印度尼西亚和菲律宾扩张的结果全然不同。在印度尼西亚和菲律宾,当地的人口消失了——大概是被这些入侵者赶走、杀死、用传染病害死或甚至同化了。而在新几内亚,当地的人口多半把这些入侵者挡在外面。
  • 中国的华南人发展了本地的粮食生产和技术,接受了华北的文字、更多的技术和政治组织,又进而向热带东南亚和台湾移民,大规模地取代了这些地区的原有居民。
  • 与澳大利亚和美洲不同,东亚和大多数太平洋岛屿仍然为东亚民族和太平洋民族所占有。

总结

  • 学会粮食生产的重要性。可以很大程度确保最终不会被替代。

第十八章 两个半球的碰撞

  • 过去13000年中最大的人口更替是新、旧大陆社会之间新近的碰撞引起的。我们在第三章看到,这种碰撞的最富戏剧性也最具决定性的时刻,是皮萨罗的小小西班牙军队俘虏了印加帝国皇帝阿塔瓦尔帕。阿塔瓦尔帕是最大、最富有、人口最多、管理和技术最先进的印第安国家的独裁统治者,他的被俘成了欧洲人征服美洲的象征,因为造成这一事件的相同的各种近似因素,也是欧洲人征服其他印第安社会的部分原因。
  • 为什么是欧洲人到达了印第安人的国家并征服了它,而不是相反?我们讨论的起始点就是把欧亚大陆社会和印第安社会作一比较,时间是到公元1492年即哥伦布“发现”美洲的那一年为止。
  • 粮食生产方面的这些差异,构成了欧亚大陆社会与印第安社会之间差异的一个重要的终极原因。在由此而产生的实现征服的近似因素中,最重要的因素包括病菌、技术、政治组织和文字方面的差异。
  • 其中与粮食生产方面的差异关系最直接的差异是病菌。有些传染病经常光顾人口拥挤的欧亚大陆社会,许多欧亚大陆人因而逐步形成了免疫力或遗传抵抗力。这些传染病包括历史上所有最致命的疾病:天花、麻疹、流行性感冒、瘟疫、肺结核、斑疹伤寒、霍乱、疟疾和其他疾病。对照这个令人望而生畏的疾病名单,唯一可以有把握归之于哥伦布以前印第安人社会的群众传染病是非梅毒密螺旋体病。
  • 大陆之间在有害的病菌方面的这种差异竟是来自有用的牲畜方面的差异。在拥挤的人类社会引起传染病的大多数病菌,是从引起家畜传染病的那些十分相似的祖代病菌演化而来的,而在大约10000年前,粮食生产者就已开始每天同这些家畜进行密切的接触了。
  • 在帮助欧洲征服美洲的一些直接因素中,可与病菌相提并论的是技术的各方面的差距。这些差距归根到底是由于欧亚大陆有历史悠久得多的依靠粮食生产的人口稠密、经济专业化、政治集中统一、相互作用、相互竞争的社会。
  • 为什么所有主要发展结果的发展轨迹在年代上美洲要晚于欧亚大陆?这有4组原因:起步晚,可用于驯化的野生动植物系列比较有限,较大的传播障碍,以及稠密的人口在美洲生活的地区可能比在欧亚大陆小,或者可能比在欧亚大陆孤立。
  • 与欧亚大陆始终如一的东西宽度不同,新大陆在中美洲的那一段特别是在巴拿马变窄了。尤其是,美洲被一些不适于粮食生产也不适于稠密人口的地区分割开来。
    这些生态障碍包括:把中美洲社会同安第斯山脉地区和亚马孙河地区社会分隔开来的巴拿马地峡雨林;把中美洲社会同美国西南部和东南部社会分隔开来的墨西哥北部沙漠;把美国西南部同东南部分隔开来的得克萨斯州干旱地区;把本来可能适于粮食生产的美国太平洋沿岸地区隔开的沙漠和高山。因此,在中美洲、美国东部、安第斯山脉地区和亚马孙河地区这些新大陆的中心之间,完全没有家畜、文字和政治实体方面的交流,以及只有在作物和技术方面的有限的缓慢的交流。
  • 起源于东地中海的字母从英格兰到印度尼西亚,传遍了欧亚大陆的各个复杂社会,只有东亚地区是例外,因为中国书写系统派生出来的文字已在那里占主导地位。
  • 我们已经找到了3组有利于欧洲人入侵美洲的终极因素:欧亚大陆人类定居时间长的领先优势;由于欧亚大陆可驯化的野生植物尤其是动物的资源比较丰富而引起的比较有效的粮食生产;欧亚大陆范围内对传播交流的地理和生态障碍并非那样难以克服。
  • 在不适宜大部分粮食生产的纬度过高地区,在欧洲穷国之一的无力支持下,几个古挪威人手中的铁器没有斗得过爱斯基摩人和印第安狩猎采集族群手中的石器、骨器和木器,要知道这后两种人是世界上掌握在北极地区生存技巧的最杰出的大师!在适合欧洲的粮食生产和欧洲人生理机能的气候最温和的地区,人口众多的印第安社会被消灭了。

总结

  • 粮食生产差异->病菌差异
  • 在帮助欧洲征服美洲的一些直接因素中,可与病菌相提并论的是技术的各方面的差距。这些差距归根到底是由于欧亚大陆有历史悠久得多的依靠粮食生产的人口稠密、经济专业化、政治集中统一、相互作用、相互竞争的社会。
  • 3组有利于欧洲人入侵美洲的终极因素:欧亚大陆人类定居时间长的领先优势;由于欧亚大陆可驯化的野生植物尤其是动物的资源比较丰富而引起的比较有效的粮食生产;欧亚大陆范围内对传播交流的地理和生态障碍并非那样难以克服。

第十九章 非洲是怎样成为黑人的非洲的

  • 在白人殖民主义者来到之前,已经生活在非洲的不仅有黑人,还有(我们将要看到)世界上6大人种中的5种,其中3种只生活在非洲。世界上的语言,有四分之一仅仅在非洲才有人说。没有哪一个大陆在人种的多样性方面可以与非洲相提并论。

  • 非洲多样化的人种来自它的多样化的地理条件和悠久的史前史。非洲是唯一的地跨南北温带的大陆,同时它也有几处世界上最大的沙漠、最大的热带雨林和最高的赤道山脉。人类在非洲生活的时间比在任何其他地方都要长得多。

  • 到公元1000年,这5个主要的人类群体已经把非洲当作自己的家园。外行人不严密地把他们称为黑人、白人、非洲俾格米人、科伊桑人和亚洲人。

  • 地球上所有人类群体只要和其他每一个群体中的人接触,就会发生婚配关系。

  • 俾格米人的家园被淹没在入侵的黑人农民的汪洋大海之中,硕果仅存的一些俾格米人采用了这些农民的语言,而他们原来的语言只在某些词和发音上留下了一些蛛丝马迹。

  • 科伊桑人和俾格米人之所以未能发展出农业,不是由于他们没有农民的资格,而仅仅是由于碰巧非洲南部的野生植物大都不适于驯化。无论是班图农民还是白人农民,尽管他们继承了几千年的农业经验,后来还是没有能把非洲南部的本地植物培育成粮食作物。

  • 至于非洲的驯化动物,概括地介绍起来可以比介绍植物快得多,因为那里的驯化动物实在太少。

  • 现代南非的问题至少一部分源自地理上的偶然因素。好望角科伊桑人的家园碰巧很少有适于驯化的野生植物;班图人碰巧从他们5000年前的祖先那里继承了适应夏雨的作物;而欧洲人碰巧从他们近10000年前的祖先那里继承了适应冬雨的作物。

  • 正如他们与印第安人遭遇时的情况一样,进入非洲的欧洲人拥有三重优势:枪炮和其他技术、普及的文化以及为维持探险和征服的昂贵计划所必不可少的政治组织。从历史上看,所有这三者都来自粮食生产的发展。但粮食生产在非洲撒哈拉沙漠以南地区被延误了(与欧亚大陆相比),其原因是非洲缺少可以驯化的本地动植物物种,它的适于本地粮食生产的小得多的面积,以及它的妨碍粮食生产和发明的传播的南北轴向。第二个因素是非洲撒哈拉沙漠以南地区和欧亚大陆之间在可驯化的植物方面的一种虽然不是那样极端但也相当大的差异。第三个因素是非洲的面积仅及欧亚大陆的面积的一半左右。

  • 总之,欧洲在非洲的殖民并不像某些白人种族主义者所认为的那样与欧洲民族和非洲民族本身之间的差异有关。恰恰相反,这是由于地理学和生物地理学的偶然因素所致——特别是由于这两个大陆之间不同的面积、不同的轴线方向和不同的动植物品种所致。就是说,非洲和欧洲的不同历史发展轨迹归根到底来自它们之间的“不动产”的差异。

  • 非洲历史悠久,是人类生活时间最长的大陆,拥有世界上最大的沙漠、最大的热带雨林和最高的赤道山脉,人种多样化,世界上6大人种有5种生活在非洲(其中3种只生活在非洲),语言、文化也是世界最复杂的。但非洲过去没有文字,使得我们很难正确还原非洲历史。

  • 非洲的5个主要人类群体:黑人、白人、俾格米人、科伊桑人、亚洲人,在1000年已经定居在非洲。其中俾格米人与黑人一样有着深色皮肤和浓密头发,但身材矮小得多、批复为红色较多黑色较少、体毛较多、前额眼镜和牙齿较突出,分布在中非的雨林中,大多过着狩猎采集生活;科伊桑人以前分布在非洲南部,他们皮肤微黄,头发浓密且卷曲,由于被欧洲殖民者入侵,科伊桑人数量大大减少,幸存者与欧洲人生下混血种;非洲白人主要分布在北部,因为与近东和欧洲邻近地区往来较多。

  • 非洲落后于欧亚大陆的原因与美洲类似:缺乏可驯化的动物,驯化的植物无法大规模种植,撒哈拉沙漠阻止了南北的传播,大陆面积较小。归根到底也是地理学和生物地理学的偶然因素所致。

  • 非洲是怎样成为黑人的非洲的:天意。黑人的这个部落在恰当的时间和恰当的地点得到了恰当的作物,促进了人口和其他一切的增长,开始了“淹没”。

总结

  • 非洲有粮食生产(黑人),但有地理原因,没能发展壮大。所以也没被完全替换。

尾声 人类史作为一门科学的未来

  • 耶利的问题触及了人类现状的实质,也是更新世后人类历史的关键所在。既然我们已经完成了这次对各大陆的短暂的巡视,我们将怎样来回答耶利呢?我会对耶利这样说:各大陆民族长期历史之间的显著差异,不是源自这些民族本身的天生差异,而是源自他们环境的差异。

  • 耶利的问题触及了人类现状的实质,也是更新世后人类历史的关键所在。既然我们已经完成了这次对各大陆的短暂的巡视,我们将怎样来回答耶利呢?我会对耶利这样说:各大陆民族长期历史之间的显著差异,不是源自这些民族本身的天生差异,而是源自他们环境的差异。我猜想,如果在更新世晚期能够使澳大利亚土著人口和欧亚大陆土著人口互换位置,那么,原来的澳大利亚土著现在可能不但占领了欧亚大陆,而且也占领了美洲和澳大利亚的大部分地区,而原来的欧亚大陆土著现在可能已沦为澳大利亚的一些遭受蹂躏的零星分散的人口。对于这种说法,你一开始可能会认为毫无意义而不屑一顾,因为这个实验是想象出来的,而我所说的那种结果也是不可能被证明的。但历史学家却能用回溯试验法对有关的假说进行评价。

  • 第一组差异是各大陆在可以用作驯化的起始物种的野生动植物品种方面的差异。这是因为,粮食生产之所以具有决定性的意义,在于它能积累剩余粮食以养活不从事粮食生产的专门人材,同时也在于它能形成众多的人口,从而甚至在发展出任何技术和政治优势之前,仅仅凭借人多就可以拥有军事上的优势。由于这两个原因,从小小的不成熟的酋长管辖地阶段向经济上复杂的、社会上分层次的、政治上集中的社会发展的各个阶段,都是以粮食生产为基础的。但大多数野生的动植物品种证明是不适于驯化的:粮食生产的基础一直是比较少的几种牲畜和作物。原来,各大陆在可以用于驯化的野生动植物的数量方面差异很大,因为各大陆的面积不同,而且在更新世晚期大型哺乳动物灭绝的情况也不同。大型哺乳动物灭绝的情况,在澳大利亚和美洲要比在欧亚大陆或非洲严重得多。因此,就生物物种来说,欧亚大陆最为得天独厚,非洲次之,美洲又次之,而澳大利亚最下,就像耶利的新几内亚那种情况(新几内亚的面积为欧亚大陆的七十分之一,而且其原来的大型哺乳动物在更新世晚期即已灭绝)。

  • 在每一个大陆,动植物的驯化集中在只占该大陆总面积很小一部分的几个条件特别有利的中心地。就技术创新和政治体制来说,大多数社会从其他社会获得的要比它们自己发明的多得多。因此,一个大陆内部的传播与迁移,对它的社会的发展起着重要的促进作用,而从长远来看,由于毛利人的新西兰火枪战争以如此简单的形式所揭示的过程,这些社会又(在环境许可的情况下)分享彼此的发展成果。就是说,起初缺乏某种有利条件的社会或者从拥有这种条件的社会那里得到,或者(如果做不到这一点)被其他这些社会所取代。

  • 因此,第二组因素就是那些影响传播和迁移速度的因素,而这种速度在大陆与大陆之间差异很大。在欧亚大陆速度最快,这是由于它的东西向主轴线和它的相对而言不太大的生态与地理障碍。对于作物和牲畜的传播来说,这个道理是最简单不过的,因为这种传播大大依赖于气候因而也就是大大依赖于纬度。同样的道理也适用于技术的发明,如果不用对特定环境加以改变就能使这些发明得到最充分的利用的话。传播的速度在非洲就比较缓慢了,而在美洲就尤其缓慢,这是由于这两个大陆的南北向主轴线和地理与生态障碍。在传统的新几内亚,这种传播也很困难,因为那里崎岖的地形和高山漫长的主脉妨碍了政治和语言统一的任何重大进展。

  • 与影响大陆内部传播的这些因素有关的,是第三组影响大陆之间传播的因素,这些因素也可能有助于积累一批本地的驯化动植物和技术。大陆与大陆之间传播的难易程度是不同的,因为某些大陆比另一些大陆更为孤立。在过去的6000年中,传播最容易的是从欧亚大陆到非洲撒哈拉沙漠以南地区,非洲大部分牲畜就是通过这种传播得到的。但东西两半球之间的传播,则没有对美洲的复杂社会作出过任何贡献,这些社会在低纬度与欧亚大陆隔着宽阔的海洋,而在高纬度又在地形和适合狩猎采集生活的气候方面与欧亚大陆相去甚远。对于原始的澳大利亚来说,由于印度尼西亚群岛的一道道水上障碍把它同欧亚大陆隔开,欧亚大陆对它的唯一的得到证明的贡献就是澳洲野狗。

  • 第四组也是最后一组因素是各大陆之间在面积和人口总数方面的差异。更大的面积或更多的人口意味着更多的潜在发明者,更多的互相竞争的社会,更多的可以采用的发明创造——以及更大的采用和保有发明创造的压力,因为任何社会如果不这样做就往往会被竞争对手所淘汰。

  • 非洲的俾格米人和其他许多被农民取代的狩猎采集群体,就曾碰到这样的命运。相反的例子是格陵兰岛上顽固保守的古挪威农民,他们也碰到了被爱斯基摩狩猎采集族群所取代的命运,因为在格陵兰的条件下,这些爱斯基摩人的生存方法和生存技术都比这些古挪威人优越得多。在全世界的陆块中,欧亚大陆的面积最大,相互竞争的社会的数量也最多,澳大利亚和新几内亚在这方面就差得多,而塔斯马尼亚更是瞠乎其后。美洲的总面积虽然很大,但却在地理上和生态上支离破碎,实际上就像几个没有紧密联系的较小的大陆。这4组因素构成了环境的巨大差异,这些差异可以客观地用数量来表示,而且不会引起争议。

  • 所有的人类社会都拥有有发明才能的人。事情恰恰是有些环境比另一些环境提供了更多的起始物种和利用发明的更有利的条件。

  • 在欧亚大陆范围内,为什么是欧洲社会,即在美洲和澳大利亚殖民的那些社会,而不是新月沃地的社会或中国和印度的社会,在技术上领先,并在现代世界上占据政治和经济的支配地位?如果一个历史学家生活在从公元前8500年到公元1450年的任何一段时间内,如果他当时试图预测未来的历史发展轨迹,他肯定会认为,欧洲最终的支配地位是最不可能发生的结果,因为欧洲在过去那1万年的大部分时间里是旧大陆的那3个地区中最落后的一个地区。

  • 从公元前8500年开始,直到公元500年后希腊与意大利的先后兴起这一段时间里,欧亚大陆西部几乎所有的重大发明——动物驯化、植物驯化、文学、冶金术、轮子、国家等等——都是在新月沃地或其附近出现的。在水磨于大约公元900年后大量传播之前,阿尔卑斯山以西或以北的欧洲没有对旧大陆的技术或文明作出过任何有意义的贡献,它只是一个从地中海以东、新月沃地和中国接受发展成果的地方。甚至从公元1000年到1450年,科学和技术绝大多数都是从印度与北非之间的伊斯兰社会传入欧洲,而不是相反。就在那几个世纪中,中国在技术上走在世界的前列,几乎和新月沃地一样早地开始了粮食生产。那么,为什么新月沃地和中国把它们几千年的巨大的领先优势最后让给了起步晚的欧洲?当然,人们可以指出促使欧洲兴起的一些直接因素:它的商人阶级、资本主义和对发明的专利保护的逐步形成,它未能产生的专制独裁君主和使人不堪重负的税收,以及它的希腊——犹太教——基督教的批判经验主义调查研究的传统。不过,对于所有这些直接原因,人们一定会提出关于终极原因的问题:为什么这些直接因素出现在欧洲,而不是出现在中国或新月沃地?

  • 新月沃地和东地中海社会不幸在一个生态脆弱的环境中兴起。它们破坏了自己的资源基础,无异于生态自杀。从东方(新月沃地)最古老的社会开始,每一个东地中海社会都在轮流地自挖墙脚,而就在这个过程中,权力西移了。欧洲北部和西部没有遭到同样的命运,这不是因为那里的居民比较明智,而是因为他们运气好,碰巧生活在一个雨量充沛、植被再生迅速的好环境里。在粮食生产传入7000年之后,欧洲北部和西部的广大地区今天仍能维持高产的集约农业。事实上,欧洲是从新月沃地得到它的作物、牲畜、技术和书写系统的,而新月沃地后来反而使自己失去了作为一个主要的权力和发明中心的地位。这就是新月沃地失去它对欧洲的巨大的早期领先优势的情形。

  • 为什么中国也失去了这种领先优势呢?中国的落后起初是令人惊讶的,因为中国拥有无可置疑的有利条件:粮食生产的出现似乎同在新月沃地一样早;从华北到华南,从沿海地区到西藏高原的高山地区的生态多样性,产生了一批不同的作物、动物和技术;幅员广阔,物产丰富,养活了这一地区世界上最多的人口;一个不像新月沃地那样干旱或生态脆弱的环境,使中国在将近10000年之后仍能维持高产的集约农业,虽然它的环境问题日益增多,而且比欧洲西部严重。

  • 这些有利条件和领先优势使得中世纪的中国在技术上领先世界。中国一长串重大的技术第一包括铸铁、罗盘、火药、纸、印刷术以及前面提到过的其他许多发明。它在政治权力、航海和海上管制方面也曾在世界上领先。15世纪初,它派遣宝船队[插图]横渡印度洋,远达非洲东海岸,每支船队由几百艘长达400英尺的船只和总共28000名船员组成。这些航行在时间上也比哥伦布率领3艘不起眼的小船渡过狭窄的大西洋到达美洲东海岸要早好几十年。法斯科·达·伽马率领他的3艘不起眼的小船,绕过非洲的好望角向东航行,使欧洲开始了对东亚的殖民。为什么中国的船只没有在伽马之前绕过好望角向西航行并在欧洲殖民?为什么中国的船只没有横渡太平洋到美洲西海岸来殖民?简而言之,为什么中国把自己在技术上的领先优势让给原先十分落后的欧洲呢?

  • 中国西洋舰队的结局给了我们一条线索。从公元1405年到1433年,这些船队一共有7次从中国扬帆远航。后来,由于世界上任何地方都可能发生的局部政治变化,船队出海远航被中止了:中国朝廷上的两派(太监和反对他们的人)之间发生了权力斗争。前一派支持派遣和指挥船队远航。因此,当后一派在权力斗争中取得上风时,它停止派遣船队,最后还拆掉船坞并禁止远洋航运。这一事件使我们想起了19世纪80年代伦敦的扼杀公共电灯照明的立法、第一次和第二次世界大战之间美国的孤立主义和许多国家全都由于局部的政治争端而引发的许多倒退措施。但在中国,情况有所不同,因为那整个地区在政治上是统一的。一个决定就使整个中国停止了船队的航行。那个一时的决定竟是不可逆转的,因为已不再有任何船坞来造船以证明那个一时决定的愚蠢,以及用作重建新船坞的中心。

  • 现在来对比一下中国的这些事件,和探险船队开始从政治上分裂的欧洲远航时所发生的事情。克里斯托弗·哥伦布出生在意大利,后来转而为法国的昂儒公爵服务,又后来改事葡萄牙国王。哥伦布曾请求国王派船让他向西航行探险。他的请求被国王拒绝了,于是他就求助于梅迪纳——塞多尼亚公爵,也遭到了拒绝,接着他又求助于梅迪纳——塞利伯爵,依然遭到拒绝,最后他又求助于西班牙的国王和王后,他们拒绝了他的第一次请求,但后来在他再次提出请求时总算同意了。如果欧洲在这头3个统治者中任何一个的统治下统一起来,它对美洲的殖民也许一开始就失败了。事实上,正是由于欧洲是分裂的,哥伦布才成功地于第五次在几百个王公贵族中说服一个来赞助他的航海事业。一旦西班牙这样开始了欧洲对美洲的殖民,其他的欧洲国家看到财富滚滚流入西班牙,立刻又有6个欧洲国家加入了对美洲殖民的行列。对于欧洲的大炮、电灯照明、印刷术、小型火器和无数的其他发明,情况也是如此:每一项发明在欧洲的一些地方由于人们的习性起先或者被人忽视,或者遭人反对,但一旦某个地区采用了它,它最后总能传播到欧洲的其余地区。

  • 欧洲分裂所产生的这些结果与中国统一所产生的结果形成了鲜明的对比。除了作出停止海外航行的决定外,中国的朝廷还作出停止其他一些活动的决定:放弃开发一种精巧的水力驱动的纺纱机,在14世纪从一场产业革命的边缘退了回来,在制造机械钟方面领先世界后又把它拆毁或几乎完全破坏了,以及在15世纪晚期以后不再发展机械装置和一般技术。统一的这些潜在的有害影响在现代中国又死灰复燃,特别是20世纪60年代和70年代“文化大革命”中的那种狂热,当时一个或几个领导人的决定就把全国的学校系统关闭了5年之久。

  • 中国的经常统一与欧洲的永久分裂都由来已久。

  • 现代中国的最肥沃地区于公元前221年第一次在政治上统一起来,并从那时以来的大部分时间里一直维持着这个局面。中国自有文字以来就一直只有一种书写系统,长期以来只有一种占支配地位的语言,以及2000年来牢固的文化统一。相比之下,欧洲与统一始终相隔十万八千里:14世纪时它仍然分裂成1000个独立的小国,公元1500年有小国500个,20世纪80年代减少到最低限度的25国,而现在就在我写这句话的时候又上升到将近40个国家。欧洲仍然有45种语言,每种语言都有自己的经过修改的字母表,而文化的差异甚至更大。欧洲内部的分歧今天在继续挫败甚至是想要通过欧洲经济共同体(EEC)来实现欧洲统一的并不过分的企图,这就表明欧洲对分裂的根深蒂固的执著。

  • 了解中国把政治和技术的卓越地位让给欧洲的关键所在就是去了解中国的长期统一和欧洲的长期分裂的问题。答案又一次用地图表示出来(见下图)。欧洲海岸线犬牙交错,它有5大半岛,每个半岛都近似孤悬海中的海岛,在所有这些半岛上形成了独立的语言、种族和政府:希腊、意大利、伊比利亚半岛、丹麦和挪威/瑞典。中国的海岸线则平直得多,只有附近的朝鲜半岛才获得了作为单独岛屿的重要性。欧洲有两个岛(大不列颠岛和爱尔兰岛),它们的面积都相当大,足以维护自己的政治独立和保持自己的语言和种族特点,其中的一个岛(大不列颠岛)因为面积大,离欧洲大陆又近,所以成了一个重要的欧洲独立强国。

  • 但即使是中国的两个最大的岛——台湾岛和海南岛,面积都不到爱尔兰岛的一半,这两个岛都不是重要独立的政体;而日本在地理上的孤立地位使它在现代以前一直处于与亚洲大陆的政治隔绝状态,其程度远远超过了大不列颠与欧洲大陆的政治隔绝状态。欧洲被一些高山(阿尔卑斯山脉、比利牛斯山脉、喀尔巴阡山脉和挪威边界山脉)分隔成一些独立的语言、种族和政治单位,而中国在西藏高原以东的山脉则不是那样难以克服的障碍。中国的中心地带从东到西被肥沃的冲积河谷中两条可通航的水系(长江和黄河)连接了起来,从南到北又由于这两大水系(最后有运河连接)之间比较方便的车船联运而成为一体。因此,中国很早就受到了地域广阔的两个高生产力核心地区的决定性影响,而这两个地区本来彼此只有微不足道的阻隔,最终又合并为一个中心。欧洲的两条最大的河流——莱茵河与多瑙河则比较小,在欧洲流经的地方也少得多。与中国不同,欧洲有许多分散的小的核心地区,没有一个大到足以对其他核心地区产生长期的决定性影响,而每一个地区又都是历史上一些独立国家的中心。中国一旦于公元前221年最后获得统一,就再没有任何其他的独立国家有可能在中国出现并长期存在下去。虽然在公元前221年后有几个时期出现了分裂局面,但最后总是重新归于统一。但欧洲的统一就连查理曼[插图]、拿破仑和希特勒这些下定决心的征服者都无能为力;甚至罗马帝国在其鼎盛时期所控制的地区也没有超过欧洲的一半。因此,地理上的四通八达和非常一般的内部障碍,使中国获得了一种初始的有利条件。华北、华南、沿海地区和内陆的不同作物、牲畜、技术和文化特点,为中国的最后统一作出了贡献。例如,黍的栽培、青铜技术和文字出现在华北,而水稻的栽培和铸铁技术则出现在华南。我用本书的很大篇幅着重讨论了在没有难以克服的障碍的情况下技术的传播问题。但中国在地理上的四通八达最后却成了一个不利条件,某个专制君主的一个决定就能使改革创新半途而废,而且不止一次地这样做了。相比之下,欧洲在地理上的分割形成了几十个或几百个独立的、相互竞争的小国和发明创造的中心。如果某个国家没有去追求某种改革创新,另一个国家会去那样做的,从而迫使邻国也这样去做,否则就会被征服或在经济上处于落后地位。欧洲的地理障碍足以妨碍政治上的统一,但还不足以使技术和思想的传播停止下来。欧洲还从来没有哪一个专制君王能够像在中国那样切断整个欧洲的创造源泉。

  • 这些比较表明,地理上的四通八达对技术的发展既有积极的影响,也有消极的影响。因此,从长远来看,在地理便利程度不太高也不太低而是中等适度的地区,技术可能发展得最快。中国、欧洲,可能还有印度次大陆的过去1000多年的技术发展过程便是例子,它分别表明了高、中、低3种不同程度的地理便利条件所产生的实际效果。

  • 新月沃地的居间的地理位置,控制了把中国和印度与欧洲连接起来的贸易路线,以及中国距离欧亚大陆其他先进的文明国家路途遥远,使中国实际上成为一个大陆内的一个巨大孤岛。中国的相对孤立状态与它先是采用技术后来又排斥技术这种做法有着特别重要的关系,这使人想起了塔斯马尼亚岛和其他岛屿排斥技术的情形(第十三章和第十五章)。不过,这一简略的讨论至少可以表明,环境因素不但与历史的最广泛模式有关,而且也与较小规模和较短时期的历史模式有关。

  • 新月沃地和中国的历史还为现代世界留下了一个有益的教训:环境改变了,过去是第一并不能保证将来也是第一

  • 物理学家和化学家能够在宏观的层次上系统地阐述带有普遍性的决定论的规律,但生物学家和历史学家只能系统地阐述统计学上的趋势。

  • 历史系统尽管有其终极的确定性,但其复杂性和不可预测性是不待言的。描述这种复杂性和不可预测性的另一个办法就是指出,长长的一连串因果关系可能把最后结果同存在于那一科学领域之外的终极原因分开。例如,一颗小行星对地球的撞击可能导致了恐龙的灭绝,但那颗小行星的轨道却是完全由古典力学的定律决定的。但如果有古生物学家生活在6700万年前,他们也不可能预测到恐龙的灭亡迫在眉睫,因为小行星属于一个在其他方面都与恐龙生物学关系疏远的科学领域研究的对象。同样,公元1300年至1500年之间的小冰期也是格陵兰岛上古挪威人灭绝的部分原因,但没有哪个历史学家,也许甚至也没有哪一个现代气候学家能够预测到小冰期的到来。

  • 因此,历史学家在确定人类社会史的因果关系时所碰到的困难,大致上类似于天文学家、气候学家、生态学家、演化生物学家、地质学家和古生物学家所碰到的困难。

日本人乃何许人也

  • 至于阿伊努人,他们独特的相貌也招致了关于其由来和亲缘的众多研究,数量之多超过了地球上任何其他的民族。阿伊努男子胡须浓密,体毛之丰居各人种之首。这个体征,加上其它一些遗传性状如指纹和耳垢类型,使得他们常常被归入不知何故从欧亚东迁最终落脚日本的高加索人种(即所谓白人)。但是,纵观其基因特征,阿伊努人和包括日本人、朝鲜人和冲绳人这些东亚人种还是有瓜葛。
  • 阿伊努人是日本以采集涉猎为生的原住民后代,而日本人则是晚近从亚洲大陆而来的入侵者。
  • 边境之外的日本最北端岛屿北海道和居住在那里的阿伊努涉猎采集者甚至都不被视为日本国的一部分,直到19世纪这里才加入日本。
  • 来自朝鲜的移民确实对现代日本民族作出了巨大贡献,虽然我们尚不能确信起因是因为移民本就人数众多,还是因为数量不多的移民凭借高速人口增长而扩张的结果。阿伊努人的基因更接近日本古代绳纹居民,又掺杂了弥生殖民者和现代日本人的朝鲜基因。
  • 早期的朝鲜编年史告诉我们,不同的王国拥有不同的语言。虽然被新罗打败的两个王国的语言已鲜为人知,但作为战败国之一的高句丽(Koguryo),其留存下的少数几个词汇与古日语词汇的近似程度远胜于现代朝鲜语词汇。在政治统一进程到达三足鼎立的阶段之前,公元前400年的朝鲜语,也许多样性更甚。我怀疑公元前400年传入日本并发展成为现代日语的朝鲜语,与发展成为现代朝鲜语的新罗语大不相同。因此,现代日本人和朝鲜人之间的外形和基因的相似性远超两种语言的近似性,我们对此不应该感到奇怪。
  • 考虑到日本民族和朝鲜民族目前的相互仇视,这个结论很可能在日本和朝鲜都同样不受欢迎。历史给了他们对彼此产生厌恶的充分原因,而其中又尤以朝鲜人对日本人的厌恶为甚。正如阿拉伯人和犹太人,朝鲜人和日本人是血脉相连的民族,但又深陷积怨的迷障。而积怨具有双向的毁灭性,这一点在东亚和中东都是如此。即便日本人和朝鲜人都不愿意承认,事实上,他们就像一对共享了成长岁月的孪生兄弟。在很大程度上,东亚的政治前景取决于他们是否能够成功地重新找回联系彼此的古老纽带。

总结

  1. 日本来自朝鲜的移民
  2. 和日本当地其他族融合并替换

附录 2003后记:《枪炮、病菌与钢铁》今日谈

  • 公元前400年左右,朝鲜农民扩张到了日本西南,继而朝着日本列岛的东北挺进。迁移的农民带来了精细的水稻农业和铁制工具,并与日本原住民(现代阿伊努人的祖先)融合产生了现代日本人,就像扩张的新月沃地农民与欧洲的土著狩猎采集人群融合产生了现代欧洲人。
  • 我认为欧洲超越中国的背后原因,比多数历史学家所提出的直接因素要来得深远(例如中国的儒家理论vs.欧洲的犹太基督教传统,西方科学的崛起,欧洲重商主义和资本主义的崛起,英国的毁林兴矿等等)。在上述以及其他直接因素背后,我看到了一个“最优分裂原则”:伴随着欧洲始终的分裂,导致中国较早统一并保持相对统一的终极地理因素。促成技术、科学的进步,带来推动各国竞争,以可替代的资源支持并提供给发明者,并为他们提供可躲避迫害的庇护所,并由此孕育资本主义的,不是中国的统一,而是欧洲的分裂。历史学家们后来也向我指出,欧洲的分裂,中国的统一,还有欧洲和中国的相对优势之复杂程度,都超出我书中的讲述。可以被分成“欧洲”和“中国”的政治/社会半径的地理边界在过去几个世纪内一直处于变动之中。直到至少15世纪以前,中国在技术上一直走在欧洲前面,在未来也有可能重续辉煌,那样的话,“为什么是欧洲,而非中国?”的问题可能就只是一种转瞬即逝的现象,没有深层原因可挖。政治分裂的复杂影响远不止提供一个用于竞争的建设性平台,例如,竞争有可能是建设性的,也有可能是破坏性的(想想一战和二战)。分裂本身是个多层面而非单一的概念,其对于创新的影响力依赖于自由等要素,如此,创意和人员才能在各个碎片之间跨界流动,不管这些碎片是独一无二的,还是彼此的克隆。至于分裂是否最优也随使用的最优衡量尺度而异,对于技术创新最优的政治分裂程度,也许就经济生产力、政治稳定或人类福祉而言并非最优。
  • 印度在地理上较欧洲更为分裂,但是其技术创新却不及欧洲。这让我想到了“最优分裂原则”:创新在带有最优中间程度分裂的社会里发展得最快:太过统一的社会处于劣势,太过分裂的社会也不占优。

不完全总结

美洲落后的原因?

  1. 整个野生动植物组合微弱的驯化潜力;
  2. 南北轴线,且南北接触地狭小,地理和生态上却支离破碎 (影响传播);
  3. 人少和可驯化动植物少导致不能产生很多流行病

澳大利亚落后的原因?

  1. 澳大利亚是最干燥、最小、最平坦、最贫瘠、气候最变化无常、生物品种最稀少的大陆;澳大利亚是最小的一个大陆。澳大利亚大部分地区雨量稀少,物产贫乏,因此,就其所能养活的人口来说,它实际上就显然甚至更小。它也是一个最孤立的大陆。

非洲落后的原因?

1.表面上是一个大陆,实际上由于气候和地形原因,隔离成很多个地区(影响传播);
撒哈拉沙漠仍然是一个主要的生态障碍,把非洲撒哈拉沙漠以南地区同欧亚大陆和北非隔开。非洲的南北轴线造成了欧亚大陆与非洲撒哈拉沙漠以南地区之间以及撒哈拉沙漠以南地区本身内部技术传播的又一障碍。

欧亚大陆

  1. 欧亚大陆何以一直是大型哺乳动物驯化的主要场所,对这个问题的部分解释是:它是一个一开始就拥有最多的可供驯化的野生哺乳动物的大陆,在过去的40000年中,那里这样的动物因绝种而消失的也最少。
  2. 东西轴线,同纬度易于传播

各大陆的差异

  • 各大陆之间在面积、人口、技术传播的难易程度和粮食生产的开始时间等方面存在着差异,而这些差异又对技术的出现产生了种种影响,但所有这些影响都被夸大了,因为技术可以催化自身。欧亚大陆在开始时的巨大优势因此就变成了自1492年[插图]起的巨大的领先优势——其原因是欧亚大陆独特的地理条件,而不是那里的人特别聪明。
  • 第一组差异是各大陆在可以用作驯化的起始物种的野生动植物品种方面的差异。
  • 第二组因素就是那些影响传播和迁移速度的因素,而这种速度在大陆与大陆之间差异很大。
  • 与影响大陆内部传播的这些因素有关的,是第三组影响大陆之间传播的因素,这些因素也可能有助于积累一批本地的驯化动植物和技术。大陆与大陆之间传播的难易程度是不同的,因为某些大陆比另一些大陆更为孤立。
  • 第四组因素是各大陆之间在面积和人口总数方面的差异。更大的面积或更多的人口意味着更多的潜在发明者,更多的互相竞争的社会,更多的可以采用的发明创造

欧洲领先的原因

  • 在欧亚大陆范围内,为什么是欧洲社会,即在美洲和澳大利亚殖民的那些社会,而不是新月沃地的社会或中国和印度的社会,在技术上领先,并在现代世界上占据政治和经济的支配地位?如果一个历史学家生活在从公元前8500年到公元1450年的任何一段时间内,如果他当时试图预测未来的历史发展轨迹,他肯定会认为,欧洲最终的支配地位是最不可能发生的结果,因为欧洲在过去那1万年的大部分时间里是旧大陆的那3个地区中最落后的一个地区。
  • 新月沃地和东地中海社会不幸在一个生态脆弱的环境中兴起。它们破坏了自己的资源基础,无异于生态自杀。
  • 但在中国,情况有所不同,因为那整个地区在政治上是统一的。一个决定就使整个中国停止了船队的航行。那个一时的决定竟是不可逆转的,因为已不再有任何船坞来造船以证明那个一时决定的愚蠢,以及用作重建新船坞的中心。
  • 事实上,正是由于欧洲是分裂的,哥伦布才成功地于第五次在几百个王公贵族中说服一个来赞助他的航海事业。一旦西班牙这样开始了欧洲对美洲的殖民,其他的欧洲国家看到财富滚滚流入西班牙,立刻又有6个欧洲国家加入了对美洲殖民的行列。对于欧洲的大炮、电灯照明、印刷术、小型火器和无数的其他发明,情况也是如此:每一项发明在欧洲的一些地方由于人们的习性起先或者被人忽视,或者遭人反对,但一旦某个地区采用了它,它最后总能传播到欧洲的其余地区。
  • 欧洲分裂所产生的这些结果与中国统一所产生的结果形成了鲜明的对比。除了作出停止海外航行的决定外,中国的朝廷还作出停止其他一些活动的决定:放弃开发一种精巧的水力驱动的纺纱机,在14世纪从一场产业革命的边缘退了回来,在制造机械钟方面领先世界后又把它拆毁或几乎完全破坏了,以及在15世纪晚期以后不再发展机械装置和一般技术。统一的这些潜在的有害影响在现代中国又死灰复燃,特别是20世纪60年代和70年代“文化大革命”中的那种狂热,当时一个或几个领导人的决定就把全国的学校系统关闭了5年之久。
  • 中国的经常统一与欧洲的永久分裂都由来已久。
  • 欧洲海岸线犬牙交错,它有5大半岛,每个半岛都近似孤悬海中的海岛,在所有这些半岛上形成了独立的语言、种族和政府:希腊、意大利、伊比利亚半岛、丹麦和挪威/瑞典。中国的海岸线则平直得多,只有附近的朝鲜半岛才获得了作为单独岛屿的重要性。欧洲有两个岛(大不列颠岛和爱尔兰岛),它们的面积都相当大,足以维护自己的政治独立和保持自己的语言和种族特点,其中的一个岛(大不列颠岛)因为面积大,离欧洲大陆又近,所以成了一个重要的欧洲独立强国。
  • 中国在地理上的四通八达最后却成了一个不利条件,某个专制君主的一个决定就能使改革创新半途而废,而且不止一次地这样做了。相比之下,欧洲在地理上的分割形成了几十个或几百个独立的、相互竞争的小国和发明创造的中心。如果某个国家没有去追求某种改革创新,另一个国家会去那样做的,从而迫使邻国也这样去做,否则就会被征服或在经济上处于落后地位。欧洲的地理障碍足以妨碍政治上的统一,但还不足以使技术和思想的传播停止下来。欧洲还从来没有哪一个专制君王能够像在中国那样切断整个欧洲的创造源泉。
  • 地理上的四通八达对技术的发展既有积极的影响,也有消极的影响。因此,从长远来看,在地理便利程度不太高也不太低而是中等适度的地区,技术可能发展得最快。中国、欧洲,可能还有印度次大陆的过去1000多年的技术发展过程便是例子,它分别表明了高、中、低3种不同程度的地理便利条件所产生的实际效果。
  • 新月沃地的居间的地理位置,控制了把中国和印度与欧洲连接起来的贸易路线,以及中国距离欧亚大陆其他先进的文明国家路途遥远,使中国实际上成为一个大陆内的一个巨大孤岛。中国的相对孤立状态与它先是采用技术后来又排斥技术这种做法有着特别重要的关系
  • 新月沃地和中国的历史还为现代世界留下了一个有益的教训:环境改变了,过去是第一并不能保证将来也是第一
  • “最优分裂原则”:创新在带有最优中间程度分裂的社会里发展得最快:太过统一的社会处于劣势,太过分裂的社会也不占优。
  • 总结:
    1. 过于统一
    2. “最优分裂原则”;有时需要多种声音,多样化,才能更加健康发展。

中国落后的原因?

  1. 中央集权决策错误闭关锁国
  2. 日本拒绝枪支和中国抛弃远洋船只(以及抛弃机械钟和水力驱动纺纱机),是历史上孤立或半孤立社会技术倒退的著名例子。

新月沃地落后的原因?

  • 新月沃地从领先到落后欧洲发展的原因:农业领先优势从技术传到西方后,由于西方的面积地域优势,逐步发展起来,并超过了新月沃地。另一方面,由于新月沃地过渡自然砍伐破坏,地貌上发生了根本性的变化,成为了沙漠,不利于更长久的发展,最终落后于欧洲。

个人总结

  1. 一旦发明家发现了一项新技术的用途,下一步就是说服社会来采用它。仅仅有一种更大、更快、更有效的工作装置还不能保证人们会乐于接受。无数的此类技术要么根本没有被采用,要么只是在长期的抵制之后才被采用。(现在的游戏直播,打赏,虚拟主播等,都是想办法让用户逐步接受)
  2. 事实上,在整个大陆和其他一些包含数以百计的互相竞争的广大地区,有些社会对新事物可能比较开放,有些社会对新事物可能比较抵制。那些接受新作物、新牲畜或新技术的社会因而可能吃得更好,繁殖得更快,从而取代、征服或杀光那些抵制新事物的社会。(接收新事物才不会被淘汰)
  3. 流行病(人群病)。拥有流行病的族类是战胜没流行病族类的重要因素。
    • 拥有流行病首先要死一部分人,你愿意成为拥有你族类的炮灰吗?
    • 如果处于无流行病的族群,那么可能当前是好的,但是对你后代不利;有流行病的族类则相反;
    • 族类之间相互融合,有时很难说清你究竟属于哪个族类;
    • 认清你觉得最重要的事情或人,作出相应的选择;在某些大环境下,作出选择后,可能需要欺骗自己,以更好的实施,保持政治正确;族类的精神控制
  4. “最优分裂原则”
  5. 一个人的命运,要靠自我奋斗,但也要考虑到历史的进程。外部环境则是历史进程的基础与原生动力。

其他网友

  • 读到这里对教育有了一些思考,终归还是要把孩子放出去多与其他人接触交流合作,才会受到伤害,才会由此产生自我认知,才会成长。而如果只是把孩子孤立起来,与社会少有接触,那就会像免疫力差的一碰即亡。
  • 人总是要吃饱饭才有力气干活,事实证明当劳动的效率越高,人们获得的能量也就越多也就促进了文明的发展,毕竟只有劳动之后的能量有剩余才能够养得起专职人员。
  • 让一群各自为战的人团结起来的最好办法 就是树立一个共同的敌人和威胁!

单元测试使用总结

发表于 2020-11-11

mockito使用

mock实例

  • Test test = mock(Test.class);
  • Test test = spy(new Test());

在Spring中使用

  • @Mock Test test;
  • @Spy Test test = new Test();
  • @MockBean Test test; (起spring容器时)
  • @InjectMocks Test test; (起mockito容器时的测试对象)
  • Spring boot使用@MockBean和@SpyBean来定义Mockito的mock和spy。
  • 使用@mockBean,未mock的方法可能会导致返回默认值,从而导致异常的逻辑造成脏数据(可能是代码本来不完善);可以注意尽量@SpyBean

mock使用

  • when().thenReturn()模式 和 doReturn().when()模式
    两种模式都用于模拟对象方法,在mock实例下使用时,基本上是没有差别的。但是,在spy实例下使用时,when().thenReturn()模式会执行原方法,而doReturn().when()模式不会执行原方法。

  • when(test.do(anyString())).thenReturn(true);

  • 返回值为void: doAnswer((Answer<Void>)invocation -> null).when(test).do(anyString(),anyLong(),anyString());

  • 抛异常:doThrow(new RuntimeException("error")).when(test).do(anyLong(), anyString());

  • doReturn(expected).when(spyList).get(100); (使用spy时)
    - org.mockito.Mockito.doReturn; 注意不要导入powermock的包
    - doReturn().when()模式: 用于模拟对象方法,直接返回期望的值、异常、应答,或调用真实的方法,无需执行原始方法

  • 直接调用真实方法:Mockito.doCallRealMethod().when(userService).getUser(userId);, Mockito.when(userService.getUser(userId)).thenCallRealMethod();

  • 模拟可空参数方法: Mockito.doReturn(user).when(userDAO).queryCompany(Mockito.anyLong(), Mockito.nullable(Long.class));

  • 匹配null对象,可以使用isNull方法,或使用eq(null):Mockito.doReturn(user).when(userDAO).queryCompany(Mockito.anyLong(), Mockito.isNull());, Mockito.when(userDAO.queryCompany(Mockito.anyLong(), Mockito.eq(null))).thenReturn(user);

  • 模拟final方法: PowerMock提供对final方法的模拟,方法跟模拟普通方法一样。但是,需要把对应的模拟类添加到@PrepareForTest注解中。

  • 模拟私有方法: PowerMock提供提对私有方法的模拟,但是需要把私有方法所在的类放在@PrepareForTest注解中。

    1
    2
    3
    4
    5
    PowerMockito.when(userService, "isSuperUser", userId).thenReturn(!expected);
    //通过模拟方法stub(存根),也可以实现模拟私有方法。但是,只能模拟整个方法的返回值,而不能模拟指定参数的返回值。
    PowerMockito.stub(PowerMockito.method(UserService.class, "isSuperUser", Long.class)).toReturn(!expected);
    Method method = PowerMockito.method(UserService.class, "isSuperUser", Long.class);
    Object actual = method.invoke(userService, userId);
  • 模拟构造方法: PowerMock提供PowerMockito.whenNew方法来模拟构造方法,但是需要把使用构造方法的类放在@PrepareForTest注解中。

    1
    2
    PowerMockito.whenNew(MockClass.class).withNoArguments().thenReturn(expectedObject);
    PowerMockito.whenNew(MockClass.class).withArguments(someArgs).thenReturn(expectedObject);
  • 调用无访问权限的构造方法: 调用无访问权限的构造方法,可以使用PowerMock提供的Whitebox.invokeConstructor方法。

  • 调用无权限访问的普通方法: 调用无访问权限的普通方法,可以使用PowerMock提供的Whitebox.invokeMethod方法。

  • 附加匹配器
    Mockito的AdditionalMatchers类提供了一些很少使用的参数匹配器,我们可以进行参数大于(gt)、小于(lt)、大于等于(geq)、小于等于(leq)等比较操作,也可以进行参数与(and)、或(or)、非(not)等逻辑计算等。
    PowerMockito.when(mockList.get(AdditionalMatchers.geq(0))).thenReturn(expected);

  • Whitebox.setInternalState方法
    现在使用PowerMock进行单元测试时,可以采用Whitebox.setInternalState方法设置私有属性值: Whitebox.setInternalState(Foo.class, "FIELD_NAME", "value");

mock静态方法

  • 返回值为void: PowerMockito.doNothing().when(FileUtils.class, "writeStringToFile", any(File.class), anyString());
  • BDDMockito.given(FileUtils.readFileToString(eq(new File(file)))).willReturn(IOUtils.toString(getClass().getClassLoader().getResourceAsStream("test.json")));
  • 前置初始化:
    1
    2
    3
    4
    5
    6
    7
    @RunWith(PowerMockRunner.class)
    @PrepareForTest({FileUtils.class})
    @Before
    {
    MockitoAnnotations.initMocks(this)
    PowerMockito.mockStatic(FileUtils.class);
    }

设置静态常量字段值

  • 有时候,我们需要对静态常量对象进行模拟,然后去验证是否执行了对应分支下的方法。比如:需要模拟Lombok的@Slf4j生成的log静态常量。但是,Whitebox.setInternalState方法和@InjectMocks注解并不支持设置静态常量,需要自己实现一个设置静态常量的方法:
1
2
3
4
5
6
7
public final class FieldHelper {
public static void setStaticFinalField(Class clazz, String fieldName, Object fieldValue) throws NoSuchFieldException, IllegalAccessException {
Field field = clazz.getDeclaredField(fieldName);
FieldUtils.removeFinalModifier(field);
FieldUtils.writeStaticField(field, fieldValue, true);
}
}

使用Answer来生成期望的返回

1
2
3
4
5
6
7
8
9
10
11
12
13
when(test.do(anyList())).thenAnswer(
(Answer)invocation -> {
Object[] args = invocation.getArguments();
//Object mock = invocation.getMock();
return list.stream().filter(v -> ((List<Integer>)args[0]).contains(v.getAid())).collect(
Collectors.toList());
});
doReturn(resp).when(test).do(argThat(new ArgumentMatcher<List<Long>>() {
@Override
public boolean matches(Object argument) {
return ((List<Long>)argument).size() == 1;
}
}));

verify使用

  • 校验执行次数:Mockito提供vertify关键字来实现校验方法是否被调用,从未被调用never(),调用次数times(1), verify(test).do(any());
  • 验证私有方法
    PowerMockito.verifyPrivate(userService).invoke("isSuperUser", userId);
  • 验证方法调用并捕获参数值: Mockito提供ArgumentCaptor类来捕获参数值,通过调用forClass(Class clazz)方法来构建一个ArgumentCaptor对象,然后在验证方法调用时来捕获参数,最后获取到捕获的参数值并验证。如果一个方法有多个参数都要捕获并验证,那就需要创建多个ArgumentCaptor对象。
  • verify语句, 除times外,Mockito还支持atLeastOnce、atLeast、only、atMostOnce、atMost等次数验证器。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

public class ListTest {
@Test
public void testAdd() {
List<Integer> mockedList = PowerMockito.mock(List.class);
PowerMockito.doReturn(true).when(mockedList).add(Mockito.anyInt());
mockedList.add(1);
mockedList.add(2);
mockedList.add(3);
InOrder inOrder = Mockito.inOrder(mockedList);
inOrder.verify(mockedList).add(1);
inOrder.verify(mockedList).add(2);
inOrder.verify(mockedList).add(3);
}
}
  • 验证调用参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

public class ListTest {
@Test
public void testArgumentCaptor() {
Integer[] expecteds = new Integer[] {1, 2, 3};
List<Integer> mockedList = PowerMockito.mock(List.class);
PowerMockito.doReturn(true).when(mockedList).add(Mockito.anyInt());
for (Integer expected : expecteds) {
mockedList.add(expected);
}
ArgumentCaptor<Integer> argumentCaptor = ArgumentCaptor.forClass(Integer.class);
Mockito.verify(mockedList, Mockito.times(3)).add(argumentCaptor.capture());
Integer[] actuals = argumentCaptor.getAllValues().toArray(new Integer[0]);
Assert.assertArrayEquals("返回值不相等", expecteds, actuals);
}
}
  • 确保验证完毕.
    Mockito提供Mockito.verifyNoMoreInteractions方法,在所有验证方法之后可以使用此方法,以确保所有调用都得到验证。如果模拟对象上存在任何未验证的调用
  • 验证静态方法。Mockito没有静态方法的验证方法,但是PowerMock提供这方面的支持。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RunWith(PowerMockRunner.class)
@PrepareForTest({StringUtils.class})
public class StringUtilsTest {
@Test
public void testVerifyStatic() {
PowerMockito.mockStatic(StringUtils.class);
String expected = "abc";
StringUtils.isEmpty(expected);
PowerMockito.verifyStatic(StringUtils.class);
ArgumentCaptor<String> argumentCaptor = ArgumentCaptor.forClass(String.class);
StringUtils.isEmpty(argumentCaptor.capture());
Assert.assertEquals("参数不相等", argumentCaptor.getValue(), expected);
}
}

其他

  • ReflectionTestUtils.setField(config, "id", id);
  • Mockito.reset(test);
  • @VisibleForTesting
  • Mocking a method in the same test class using - Mockito:https://towardsdatascience.com/mocking-a-method-in-the-same-test-class-using-mockito-b8f997916109
1
2
3
4
5
6
7
8
9
10
11
12
13
public class PersonTest{

@Test
public void playTest() {
Person person = new Person("name", 15, "23435678V");

Person person1 = Mockito.spy(person);

Mockito.doReturn(true).when(person1).runInGround("ground");

Assert.assertEquals(true, person1.isPlay());
}
}

一般不建议在同个类mock自己的方法,如果一定要,可以使用spy

  • @Captor注解在字段级别创建参数捕获器。但是,在测试方法启动前,必须调用MockitoAnnotations.openMocks(this)进行初始化。
  • @PowerMockIgnore注解
    为了解决使用PowerMock后,提示ClassLoader错误。

异常测试

1
2
3
4
5
6
7
8
   @Rule
public ExpectedException thrown = ExpectedException.none();

@Test
public void test() {
thrown.expect(new BizExceptionCodeMatches(ExceptionCodeEnum.FAIL_CODE.code()));
//do
}
1
@Test(expected = IndexOutOfBoundsException.class)

异步测试

  • 异步系统的两种测试方法:https://mp.weixin.qq.com/s/ft7LDsLmJByxunPuqGUOuQ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class ExampleTest {

private final Object lock = new Object();

@Before
public void init() {
new Thread(new Runnable() {
public void run() {
synchronized
(lock) {
//获得锁
monitorEvent();
//监听异步事件的到来
lock.notifyAll();
//事件到达,释放锁
}
}
}).start();
}

@Test
public void testAsynchronousMethod() {
callAsynchronousMethod();
//调用异步方法,需要较长一段时间才能执行完,并触发事件通知
/**
* 事件未到达时由于init已经获得了锁而阻塞,事件到达后因init中的锁释放而获得锁,
* 此时异步任务已执行完成,可以放心的执行断言验证结果了
*/
synchronized
(lock) {
assertTestResult();
}
}
}

基于Spock的数据驱动测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class SpockTest  extends Specification{

// 初始化
def setupSpec() {
println ">>>>>> setupSpec"
}
def setup() {
println ">>>>>> setup"
}
def cleanup() {
println ">>>>>> cleanup"
}
def cleanupSpec() {
println ">>>>>> cleanupSpec"
}

def void testAdd(int a, int b, int expect) {

expect:
assert expect == a + b

where:
a | b | expect
1 | 1 | 2
2 | 2 | 4
}

}

使用rest-assured进行接口层测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class RestAssuredMockMvcTest {

@Before
public void before(){
RestAssured.registerParser("text/plain", Parser.JSON);
}

///https://github.com/rest-assured/rest-assured/wiki/Usage#spring-mock-mvc-module
@Test
public void test(){

given().
standaloneSetup(new GreetingController()).
param("name", "Johan").
when().
get("/greeting").
then().
statusCode(200).
body("code", equalTo(0));
//body("data", equalTo("Hello, Johan!"));
}
}

事务测试

  • TODO

其他

  • 单元测试的运行速度重要吗?
    违背这个原则的典型反例,就是在单测中启动 Spring。
  • 数据驱动测试(Data Driven Test)
  • mockito只能用于纯逻辑的验证,涉及事务那些还是没办法。单个类的单元测试逻辑正确不代表逻辑就覆盖全了,所以针对重要的接口需要单独集成测试用例
  • ContiPerf:: 更为优雅和方便的单元压力测试工具。

扩展

  • [JDK11下Mock框架进化:从PowerMockito到Mockito Only]https://mp.weixin.qq.com/s/OsySrzocrMmJdk6C0_h60A

Reference

  • https://www.baeldung.com/mockito-void-methods
  • https://stackoverflow.com/questions/2276271/how-to-make-mock-to-void-methods-with-mockito
  • https://github.com/eugenp/tutorials/tree/master/testing-modules/mockito
  • </https://stackoverflow.com/questions/9585323/how-do-i-mock-a-static-method-that-returns-void-with-powermock>
  • https://www.cnblogs.com/Ming8006/p/6297333.html#c2.9
  • https://www.baeldung.com/mockito-annotations
  • https://javapointers.com/tutorial/difference-between-spy-and-mock-in-mockito/
  • https://github.com/eugenp/tutorials/tree/master/testing-modules/mockito
  • https://github.com/rest-assured/rest-assured/wiki/Usage#spring-mock-mvc-module
  • 单元测试,只是测试吗?
  • 单元测试难?来试试这些套路
  • Spock单元测试框架以及在美团优选的实践
  • [阿里开源的 Mock 工具:TestableMock]
  • 收藏!Java编程技巧之单元测试用例编写流程 – 这个很全了。。。
  • https://stackoverflow.com/questions/30890011/whats-the-difference-between-mockito-matchers-isa-any-eq-and-same
  • https://stackoverflow.com/questions/24295197/is-there-mockito-eq-matcher-for-varargs-array
  • https://stackoverflow.com/questions/5385161/powermock-testing-set-static-field-of-class
  • Java单元测试技巧之PowerMock:

  • 转:分享一个观点:区分集成测试和单元测试的最本质差别在于,单元测试没有不可控外部依赖,也就是不会因为外部的原因导致测试失败。其它差别都不是能有效区分单元测试和集成测试的。

《我的第一本金融入门书》-笔记

发表于 2020-11-03

银行的生财之道——存款与贷款

  • 银行的存款不可能全部贷放出去,按照法律规定,银行必须要留存一定比例的存款作为应对储户提款的准备金,并且贷款在很多时候存在不能及时收回的情况,甚至会出现坏账,这会对银行造成损失,此外,这笔钱还没有扣除税收等因素

货币创造的秘密——商业银行的派生存款

  • 我们时常听到财经新闻报道说,中央银行上调法定存款准备金率0.5个百分点,冻结了约3000万元的银行存款货币。在我国,这是当市场流动性泛滥、通货膨胀严重的时候政府所采用的紧缩货币供给的手段。银行存款也是货币,并且银行账户上的存款数量远远大于现金,那么这些多出来的没有现金支撑的存款货币是怎么创造出来的呢?为什么法定存款准备金率的上调就能冻结那么多的存款货币呢?
  • 我们知道,现在人们使用的货币都是信用货币,而信用货币最早的形式是商业银行的银行券。后来,银行券的发行逐渐集中到了一国的中央银行。也就是说,现在人们手中的所有现金货币都是由中央银行发出来的,而商业银行能够创造的只是存款货币。存款货币的创造与银行以支票存款为依据组织的转账结算有直接的联系。一般来说,人们把现金存入银行之后,并不一定再把现金全数提出;从银行取得贷款的客户通常也并不要求银行支付现金,而是要求把贷款记在自己的存款账户中。当客户的存款账户上存有款项时,既可以在必要的时候提取现金,又可以开出支票履行支付义务;当一位客户取得支票时,他往往也不是到付款银行提取现金,而是委托自己开有存款账户的往来银行代收并把收来的款项记入存款账户。对银行来说,客户开出支票,因此有应该付出的款项,同时客户交来支票委托收款,因此有应该收入的款项
  • 银行对现金货币的需要归结为两类:一是客户从存款中提取现金用于发放工资、小额零星支付等;二是结清支票结算中应收应付的差额。在长期经营中,银行发现,相对于存款,现金只是一小部分,而且两者的比例关系相对稳定。也就是说,只要按存款的一定百分比保持现金库存即可应对客户对于现金的需要。
  • 在这样的现代银行支付体系下,存款货币就产生出来,或者说是派生出来了。
  • 假如有一位客户甲,他持有10000元的现金,并将其存入开立了活期存款账户的A银行。从而,A银行负债业务中的存款一项就多了10000元。根据经验,保存相当于90%的现金就足以应对客户日常提取现金的需要,那么A银行可以把9000元贷出。假如这时候正好有一位客户乙要从A银行贷款9000元,那么此时A银行的资产账户上就多出来9000元的贷款,以及剩余的1000元用于应对取现的准备金,实际上,这1000元的准备金是按照法律要求要存到中央银行的账户上的。
  • 如果客户乙将这9000元的贷款用签发支票的方式支付给与自己有业务往来的客户丙,客户丙在B银行开立了账户,于是这9000元则由A银行转到了B银行,B银行的存款就多了9000元。同样的道理,B银行将这9000元中的10%留作准备金,将其余的90%(8100元)贷放出去。如果这时正好有客户丁要从B银行贷款8100元,那么B银行的资产账户就多出来8100元的贷款,同时B银行将900元的准备金上缴中央银行。
  • 以此类推,B银行的客户丁向C银行的客户戊用支票支付8100元的应付款,于是C银行就又多了8100元的存款,在这8100元中,90%用于C银行的贷款,10%用作准备金,无限地循环下去,最终我们可以得到一个总的存款数量和准备金数量。
  • 将A、B、C……银行的存款数量相加,得到10000+9000+8100+7290+…=100000(元),准备金数量为1000+900+810+729+…=10000(元),从中我们可以看出最初的10000元原始存款最终变成了100000元派生存款,扩大了10倍。经过简单归纳,我们发现这个倍数正好是准备金率的倒数,也就是1/10%=10。
  • 至此,我们就明白了商业银行派生货币的秘密,在银行之间的非现金转账制度和不完全准备金制度下,这样的存款货币派生机制就是有效的。在这一制度下,某单位的现金经过多次存款转账支付,可以创造出(1/准备金率)倍的存款。我们也能够回答中央银行调整存款准备金率对存款货币数量影响巨大的原因了。假如存款准备金率由10%调整到20%,那么存款的最终创造数量就变为1/20%,扩大了5倍,即10000元的现金只能创造出50000元的存款,这个影响相当巨大。
  • 在非现金转账制度和不完全准备金制度的条件下,商业银行可以创造出数倍于现金数量的存款,而这个倍数就是存款准备金率的倒数。

利率是怎样调整的——固定利率与浮动利率

  • 根据在借贷期内利率是否会发生变化,我们又可以将利率分为固定利率和浮动利率。
  • 浮动利率是一种在借贷期内可定期调整的利率。
  • 浮动利率尽管可以为债权人减少损失,但也因手续复杂、计算依据多样而增加费用开支。因此,浮动利率多用于3年以上的国际金融市场的借贷。
  • 固定利率和浮动利率是利率的两种设置方式。采用固定利率可以使计算简单方便,而采用浮动利率可以使长期借贷行为更加公平。

为什么会有负利率——名义利率与实际利率

  • 实际利率是指物价水平不变,从而货币购买力保持不变条件下得到的利息率。
  • 为了避免通货膨胀的损失,假设仍然要取得3%的利息,那么粗略地计算,乙必须把贷款利率提高到8%,这样才能保证收回的本金和利息之和与以前的物价水平相当,并且保证购买力不变。其中,这个8%就是名义利率。从这个例子我们可以看出,名义利率是包括物价水平变化情况的利率,实际利率和名义利率之间的关系可以大致表示为:名义利率=实际利率+通货膨胀率。市场上各种利率都是名义利率,实际利率却不容易观察到。而利用上述公式,就可以根据已知的名义利率和通货膨胀率推出实际利率。例如考察我国现在的银行存款,1年期定期储蓄存款的利率是3.25%,这是名义利率。而2011年3月的消费物价指数为4.9%,这可以代替通货膨胀率,这样我们可以算出1年期储蓄存款的实际利率为-1.65%,也就是说,我们年初把钱存进银行,年末取出后按购买力计算还亏了1.65%。所以,有些人不愿意把钱存进银行,而是去炒股或者买房,因为一般认为股市和房市是可以实现保值的市场。
  • 实际利率是物价水平不变条件下的利率,而名义利率是包括了物价变化水平的利率,两者的关系为:名义利率=实际利率+通货膨胀率

没有风险的利率——基准利率

  • 生活中,我们经常听到所谓基准利率的说法,比如,在国内,老百姓习惯上把银行的1年期定期储蓄存款的利率作为基准利率;而银行从业人员则把银行间隔夜拆借利率作为基准利率;在国外,基准利率又成了中央银行的再贴现率。那么,到底什么是基准利率呢?顾名思义,基准利率是指在多种利率并存的条件下起决定作用的利率,也就是说这种利率发生了变动,其他利率也会相应变动。因此,了解了这种关键性的利率水平的变化趋势,也就可以了解全部利率体系的变化趋势

什么是有价证券的价值——票面价值、内在价值、市场价格

  • 票面价值也称面值,是在有价证券票面上标明的金额数值
  • 内在价值就是一种有价证券未来收益的现值,它是对有价证券进行价值评估的核心。
  • 对于债券,其本身并没有对应的实物资产。股票,有其对应的实物资产,但具有同样估值金额的实物资产绝不等于对应的股票有同等的内在价值。
  • 市场价格也称市值,它的形成是以其内在价值为准的
  • 票面价值、内在价值、市场价格是三个不同的概念。票面价值是标明在有价证券票面上的金额数量,内在价值是有价证券未来收益的现值,而市场价格是围绕有价证券内在价值上下波动而形成的。

怎样评估有价证券的价值——净现值

  • 现值发行的债券票面价格为1000元,每年按8%付息,即利息为80元,每年付息一次,10年还本。如果市场利率达到9%,那么这张债券的现值,或者说内在价值实际只有935元,如果这张债券的定价超过935元,则将不具有投资价值,如果定价低于935元,则可以买入。
  • 净现值是将有价证券未来可预期到的收益折现到当前所得到的金额再减去投资的成本。

一个简单的股票价值评价指标——市盈率

  • 所谓市盈率,就是股票的市场价格与每股盈利的比值。
  • 市盈率=每股市场价格/每股盈利
  • 如果市盈率太高,则可能意味着股票的价格高于价值,在这种情况下,投资者需要卖出手中的股票或不购买这种股票。如果市盈率太低,则可能意味着股票的价值被低估,此时正是投资的好时机
  • 从理论上来说,股票的市盈率愈低,愈值得投资
  • 只有比较同类股票的市盈率才会有实用价值
  • 市盈率的高低也不能作为投资选择的绝对标准。因为高市盈率还可能意味着股票的收益有很大的增长潜力,投资者对股票特别看好;低市盈率可能是因为股票未来的收益前景不好,投资人对股票不那么看好导致的。
  • 证券业监管部门也十分关注市盈率的高低。如果市场的平均市盈率太高,那么,政府可能得出市场泡沫过大的判断,然后采取一定的措施帮助消除泡沫。
  • 市盈率是判断股票价值的一种十分简便的方法,它的数值等于股票的市场价格与每股盈利的比值,含义是,每得到1元的收益需要付出多少元的投资。

实际收益的损失——通货膨胀风险

  • 通货膨胀风险也称为购买力风险。它是由于通货膨胀、货币贬值给投资者带来的实际收益下降的风险
  • 因为通货膨胀的增长,居民的收入或工资可能会出现上涨。大部分民众对通货膨胀不敏感,但是对自己的工资上涨很敏感。人们的收入增加,大多数的人会选择增加消费,这种现象就是货币幻觉
  • 股票的通货膨胀风险相对较小。
  • 当一国的股票市场完全瘫痪,不可以保证人们财富的安全时,人们就会放弃股票和本国货币,大量持有有信用的外币和黄金等贵金属。

购买金融资产就是投资吗——投资与投机

  • 交易者根据交易手段、交易结果、交易目的的不同应该被分成两类:投资者与投机者。
  • 拥有安全性、盈利性、研究性特征的是投资者,反之则是投机者。
  • 投资者深入分析金融资产的价值,他们寻找价值被低估的金融产品并长期持有以获得稳定的收入。投机者关注行情指标,他们购买金融资产的目的就是为了在短期内获得暴利。

行为金融学典型现象——羊群效应

  • 羊群效应是行为金融学的经典内容。羊群是一种比较散乱的动物组织,如果有一只羊躁动起来,那么其余的羊会跟随着这只羊一起躁动起来。羊群效应说的是一种从众心理。
  • 羊群效应的教训告诫我们,做任何事都要有自己的判断。总是跟着别人投资,也不会得到多大的收益
  • 投资者应该相信自己的判断,千万不要盲目跟风投资。

股票的生财之道——分红与价差

  • 股票的获利方式有两种,一种是通过股价的变动获利,另一种是通过股票的分红获利。
  • 在长期中获得合理的收益,而不是短期内的一夜暴富,这是我们股票的一个重要原则。
  • 只有坚定这一个原则,我们在投资股票时,才能够以一个平和的心态去面对股市带来的波动与股价的涨涨落落。

未来标准化的商品买卖——期货合约

  • 之前讲到远期合约的优势是规避未来的风险,其实远期合约也存在一些不足,那就是远期合约往往是买卖双方商议好就可以达成的一种合约,而如果第三方甚至其他各方想要介入这份合约或者买卖双方想要将合约中的金融产品卖给其他人的话,则会比较麻烦。比如双方规定好要交的货是一等大豆1000吨,但是第三方只想要200吨,或者第三方只想要二等大豆等,这样就不能转卖这个远期合约。为了避免这种麻烦,使交易更为顺畅地进行,人们又发明了标准化的远期合约,即期货合约。
  • 期货是现在进行买卖,但是在将来进行交收或交割的标的物,这个标的物可以是某种商品(如黄金、原油、农产品),也可以是金融工具,还可以是金融指标。
  • 期货实际上是一种可以反复转让、反复买卖的标准化合同。
  • 交易这些“将运到”合约比交易谷物本身要更为有用
  • 期货市场的两类关键参与人就是套期保值者与投机者。期货市场建立的目的是出于对保值的需要。
  • 所谓套期保值,就是以现在的价格卖出未来生产的产品,或是以现在的价格买入未来所需要的原料。
  • 套期保值者一般是产品的生产商或者是需要原料的加工商。他们进入期货市场购买或卖出期货合约的目的是为了避免未来的损失。举例来说,棉花生产商为了避免在棉花收获时因棉花丰收而引起的价格下跌中受到损失,在收获期前三个月就在期货市场卖出期货合约,或者做空该种类的期货。而到了棉花收获期,他们再买入期货合约,也就是进行平仓。由于期货合约越到履约期越接近现货价格,如果三个月后棉花价格下跌,那么棉花生产商便在期货市场盈利,从而弥补在现货市场的亏损;如果三个月后棉花价格上涨,那么棉花生产商便在现货市场盈利,从而弥补在期货市场的亏损,即将利润在三个月前就锁定。
  • 而投机者在期货交易中则是扮演着“价格发现者”的角色。投机者进入期货市场购买或卖出期货合约的目的只有一个——就是获利。因此,在期货市场中时刻关注价格的波动与走向,如果发现期货市场价格与现货市场价格出现偏离,那么就会毫不犹豫地利用价差进行套利交易。但是同时,他们的不断套利也使得期货市场与现货市场的价格不会出现扭曲。
  • 期货采用保证金制度,即只需要付部分货款就可以订购全部的商品
  • 期货市场更适合专业的投资者进行投资。
  • 中国有上海期货交易所、郑州商品交易所、大连商品交易所、中国金融期货交易所四大交易所
  • 期货合约是将要买卖的东西的单位、品质、样式等在合约中事先规定好,在规定的日期进行交割的一种远期合约。期货的出现有助于保障原材料供应商与需求商的利益不会因原材料价格波动而受损。期货交易中使用保证金制度,属于杠杆交易,风险较大。

金融政策的神奇魔力

  • 政府调节经济的目的是为了预防由于单纯依靠市场力量所出现的种种市场失灵的情况。无论是由于人们的盲目乐观而产生的经济泡沫,还是由于人们信心不足而出现的经济衰退,政府都可以通过运用金融政策使经济重回正轨

市场失灵的历史表现——1929年美国经济危机回顾

  • 经济学家凯恩斯认为经济危机的根源在于有效需求不足。
  • 劳动者工资率的上升却慢于劳动生产率的增长。其结果是,生产商品的能力大大超过了购买力,“消费不足”带来了“生产过剩”的必然后果。

金融政策魔力的源泉——货币、利率与汇率

  • 一个国家的宏观金融政策应主要包括三大政策:货币政策、利率政策和汇率政策。
  • 政府的目标是确保三个市场的均衡,即保证国内的商品市场的均衡、货币市场的均衡以及国际收支的均衡。
  • 对于商品市场的均衡,从总体上说,就是国内商品的总供给与总需求的均衡,让所供给的商品满足所产生的需求,这样一方面可以不出现供过于求的局面,不至于产生商品积压,引起通货紧缩,使得经济发展陷入停滞的局面;另一方面可以防范供不应求的局面,不至于出现需求旺盛而供给不足,产生物价上涨,人民的需求难以得到有效保障的困境。政府可以运用财政政策对市场加以调整。在货币市场的均衡方面,就是要使一国对于货币的需求等于货币的供给。就货币的需求而言,根据凯恩斯的理论,人们有三种倾向去持有货币,即预防动机、交易动机与投机动机。这三方面的因素使得人们对货币有相应的需求。而中央银行作为货币的发行机构,可以通过控制货币的供给引导货币市场的均衡。在国际收支方面,如果一国的出口大于进口,则会出现顺差局面,会对本国的货币产生升值压力,进而影响本国的出口行业,并同时容易造成输入型通货膨胀,对国内经济产生影响;而如果进口大于出口,则会产生贸易赤字,使得本国货币不断走弱,进而影响本国的国际购买力水平与经济发展前景。因此,政府应该通过有效的汇率政策等调控方式引导国际收支平衡。

是痛苦的根源还是繁荣的表象——通货膨胀

  • 如果通货膨胀保持在一个比较低的水平,那么就说明市场上对于各种产品的需求比较旺盛,至少是供不应求。这样的话,整个经济的流转就会比较顺畅,企业生产的产品不会因为卖不出去而滞销;企业可以继续生产,生产过程中也会雇佣一定的劳动者,因此也不会有人失业。劳动者有了工作,也就有了收入,有了收入,也就有了消费的基础。
  • 一般而言,国际上通常认为通货膨胀率(一般用消费者物价指数CPI进行衡量)在2%以下是可以接受的范围,而在3%以上就认为已存在问题。如果通货膨胀率高于5%,则认为存在着比较严重的问题。而通货膨胀率如果达到20%,就认为这个国家面临着经济崩溃的风险。
  • 通货膨胀是指一个经济体在一段时间内货币数量增速大于实物数量增速,普遍物价水平上涨,单位货币的购买力下降的状况。较低水平的通货膨胀反映经济的蓬勃发展,但较高水平的通货膨胀就是经济痛苦的根源

一个艰难的抉择——失业与通货膨胀的跷跷板

  • 失业率的存在是让人苦恼的,通货膨胀的爆发是让人忧愁的,西方一些学者甚至将两者的数值进行加总,创造出一个“痛苦指数”来反映人们对于现行经济状况的担忧。而更让一国政府与中央银行郁闷的是,往往他们通过相应的金融政策调整,在解决其中一个问题的时候,会引发另一个问题的产生。就如同跷跷板一样,压下了这一头,那一头又升了起来。
  • 事实正是如此,当中央银行通过宽松的货币政策甚至是降息对不景气的经济进行刺激以消除失业压力的同时,会引发投资的加速,使得原材料的成本大幅度提高,进而引发一般商品价格上涨,并促使工资上涨,形成成本推动的通货膨胀。相反,当中央银行通过提高利率或收紧货币为一个过热的经济体降温时,常常会让投资者犹豫不决,导致投资不足,进而使企业不能进行有效的扩大再生产,并开始裁员,导致失业人数增加。
  • 上述这种情况正好反映了金融政策制定者所处的两难境地。金融政策的调整既要保证老百姓不会因为物价的快速上涨而怨声载道,又要避免老百姓因找不到工作或失业而痛苦不堪。所以说,对一国政府与中央银行而言,如何让通货膨胀与失业的跷跷板在一个能够接受的范围内达到平衡,是一个十分艰难的抉择。小贴士根据经济学家的研究,一国的失业率与通货膨胀率常常存在着此消彼长的替代关系。这种现象使得政府在制定相应的宏观经济政策时常常面临着两难的抉择,往往只能就重避轻,而难以全部满足。

来自全球化的挑战——输入型通货膨胀与汇率变动的压力

  • 当今世界市场的发展使得各国的联系更为紧密,因此单单考虑本国的情况显然难以完全把握经济的命脉。因此,政府在进行相应的金融政策调整时,还要考虑外部因素,也就是考虑国际市场带来的压力。而这种压力也来自两个相对应的方面,或者说,来自汇率的两种调整方式的压力。一种方式是实行固定汇率制,也就是保持汇率稳定。保持汇率稳定可以预防经济的波动,但是相应地,由于本国货币的升值或贬值压力,一国的中央银行必须动用其外汇储备进行汇率干预以保持汇率的稳定,这就对一国的外汇储备状况提出了很高的要求。如果一个国家的货币有贬值压力,那么中央银行就需要不断地用本国的外汇储备换回本国货币以保持市场上对本国货币的需求,从而稳定本国汇率。这样做对本国的外汇储备是极大的消耗。而另一方面,如果本国货币有升值压力,那么中央银行必须不断地在国际市场中供给本国货币以换回外汇储备,这样做的话会使本国的外汇储备不断膨胀,而当外汇储备贬值时,就会造成本国外汇储备的巨大损失。另一种方式是实行浮动汇率制,是指政府允许市场作为调节汇率的手段,允许汇率进行波动。这样,政府就可以不动用外汇储备对汇率市场进行干预,使汇率可以较为自由地变动。然而,在这种方式下,汇率的波动会对经济产生很大的影响。如果本国货币升值,那么会使本国的相关出口行业面临着巨大的汇率压力,因为出口企业在国内用本国货币购买原材料、雇佣劳动力,而出口到国外的产品却是用外币进行结算,本国货币升值会造成产品的成本上升,对利润产生冲击。如果本国货币贬值,那么会引发进口产品的价格上升,进而形成输入型通货膨胀。也就是说,我们购买同样的国外原材料或产品时,需要付出更多的本国货币,就如同通货膨胀一样,同样的产品需要更多的钱才能买到,表现为进口原材料与商品的价格普遍提高,对于对外依赖度很高的国家,这将会严重干扰该国的经济生产过程。因此,无论采用哪种汇率制度,汇率的波动或者汇率的变化压力都会对经济造成一定的冲击。而对汇率的调节机构来说,制定相应的汇率政策是一个取舍的过程。汇率政策对一国汇率的影响可以分为三种,即有计划的升值、保持汇率不变以及有计划的贬值。有计划的升值可以防止输入型通货膨胀,使本国的进口商品、原材料价格趋于稳定,进而使一般商品价格不至于出现较大的波动,但是,这是以牺牲出口行业的利益为代价的。而有计划的贬值则恰恰相反,一国政府为了推动出口企业的快速发展,通过实施一定程度的货币贬值手段对外贸行业进行刺激,使外贸企业由于能够更为便宜的使用国内资源而获得一定的成本优势。但是,贬值手段所带来的可能后果是输入型通货膨胀的压力,会对本国的一般性生产行业造成一定的冲击,同时也会影响本国的一般商品价格。保持汇率稳定不变则如前文所述,会促使中央银行动用大量的外汇储备进行干预,这样会对一国的外汇储备造成一定的压力。

对魔法的质疑——金融政策是否是万能的

  • 金融政策在实行中往往存在着滞后性与信息不完全的问题。
  • 金融政策的调整不是立竿见影的,而是有一定的滞后性。这就对金融政策的制定者提出了较高的要求,需要他们对未来的趋势有明确的判断,不能为了解决短期的问题而影响长期的发展。
  • 经济的波动往往呈现出复苏—繁荣—衰退—萧条—再复苏的周期性特征,这是因为,在繁荣阶段,企业往往增加投资并且成本较高,形成泡沫,在随后的衰退期中很可能会遭受损失。如果金融政策的制定者能够把握这种经济周期规律,合理运用金融政策,那么就可以获得良好的效果
  • 逆周期政策就是一种金融调控的合理手段。简单地说,逆周期政策就如同开车一般,在经济向着繁荣方向发展时就踩下经济的刹车,而在经济向着衰退方向发展时就踩下经济的油门。具体而言,当经济进入复苏阶段,企业重新开始走上扩张道路时,政府不应继续通过宽松的金融政策对其进行刺激,而是应该适当紧缩货币与信贷。这样,由于金融政策有滞后性,当经济走向高涨时期时,货币与信贷政策正好可以发挥作用,避免经济过热。而当经济增长比较平缓但并未出现下滑趋势时,政府就应对其进行刺激。逆周期政策可以避免由于滞后性造成的调控不当的问题,是金融政策制定中一个比较好的调控思路。

扩展

美国国债

  • 中国为什么要购买美国国债?https://www.zhihu.com/question/23117022
  • 根本原因是中国是实行外汇管制和人民币汇率控制。也就是说钞票进出中国都要经过一个机构,更换对应的货币,这个机构叫央行。
  • 中国是世界工厂,生产很多东西卖到全世界;同时中国的经济发展和巨大的市场规模,很多外国公司到中国投资。所以中国现在每年的贸易都是顺差(进来的钱多,出去的钱少),这样一年一年累计,中国银行就聚集了一大笔美元。

买美国国债

  1. 钱太多,花不掉。
    中国的外汇也不全是买美国国债,也有部分是做其他的,比如买黄金,投资等。但是这么一笔巨额的资金,只做这些是消化不了的。
  2. 选来选去,只有买美国国债比较合适:1,有保障(美国后台,安全),2,用的掉(美国国债发行量大),3,回报率尚可。
  3. 购买美国国债是稳赚不赔的吗?
    不是,比如美元贬值可能导致缩水
  4. 那么大笔的重要的资金,安全是第一位的;回报率是第二位的

中国的外汇储备并不都是中国人赚的

  • 中国的外汇储备并不全部都是中国人自己赚的,里面有一部分,而且是很大一部分,是外国投资引来的外汇,这笔美元只是暂存在央行,其所有权并不归属于中国。中国每年的巨额出口顺差,赚的钱也并不全是中国的,相当大一部分,都是属于外国资方的,但是我们不能因此排斥外国资方,相反还要拼命引资,没有这些外国投资,中国不可能发展到今天这个高度。
  • 所以,为了在外汇储备激增的前提下尽可能的保护中国的利益,买美国国债,是保护中国外汇储备最佳的选择。

《半小时漫画经济学》-笔记

发表于 2020-11-02

一、开篇:我们为什么要读经济学

  • 历史上没有一场仗是因为爱和正义打起来的,所有的流血背后,深层次的原因都是经济
  • 如果我们想搞明白人类是怎么活成这样的、世界是怎么运转起来的,就要先搞明白这个问题:经济是怎么来的?一、经济起源首先,经济是什么?简单地讲,就是资源最优化配置。比如一瓶水,只有到口渴的人手里,价值才能发挥到最大。
  • 这些余粮,就是大家的财产,我们也可以将之理解成经济学里的资源。
  • 农业是骨架,工业填充了血肉,其他行业是各种器官,它们一起组成了经济这副躯体。

二、货币的起源:钱打哪儿来的

  • 用金银当货币,就叫——金银本位。用羊当货币就是羊本位,用贝壳当货币就是贝本位,以此类推。
  • 经济越来越好,商品就越来越多,这就需要更多的钱去买。于是有了比金银更方便带、更方便造的纸币。到后来,人们觉得纸币也麻烦,于是有了方便付款的电子货币。
  • 货币随着经济增长一直在变多!
  • 钱源于债务!因为人类学家发现了很多证据,研究了半天,得出个结论:这种物物交换的社会是不存在的!
  • 二狗子拿着鸡要换三胖子的铁锤,三胖子虽然不想要鸡,但他还是把铁锤给了二狗子,大家都熟,打个欠条就行。比如欠条到了老四手里,就相当于二狗子欠老四债。后来,大家也都这么做,用着用着顺手了,欠条就在村里流通了起来,谁还在乎它原先是谁的?为什么大家相信这张欠条呢?原来,二狗子人品好,财产多,不管谁拿着欠条来换铁锤,他都给换。这种有借有还的品质,就是传说中的——信用。人们交换、欠债什么的,其实玩的都是信用,只要有人相信,贝壳、石头、金银,甚至纸,都能当钱花。这就是信用货币理论的一部分。

三、那些年缴过的“五险一金”到底是什么

  • 政府、工厂、工人各拿出点钱来存在一起,哪个工人出意外了,就拿钱出来,让他能维持生活,大概是这样:工人们没后顾之忧了,干起活儿来更带劲,德国经济一路雄起。现代社保制度从此诞生了!
  • 如果社保不够年限,那恐怕享受不到福利了。你自己缴的会退给你,不过公司帮你缴的钱,只能和它们说再见了。

四、养老金能养老吗

  • 计算养老金时,是以当时的社会平均工资为基础的,所以不用担心钱贬值的问题。
  • 真实情况是,人口正在老龄化,上班族越来越少,退休的大爷大妈越来越多。[插图]这样下去,总有一天,社保基金入不敷出,到时候嘛。。

五、医保到底保了个啥

六、一口气搞懂到底要缴哪些税

  • 从收入里拿出来的这部分钱就叫个人所得税。正确叫法应该是免征额
  • 在商品流通过程中缴的税,就叫流转税。
  • 无论占有哪种财富,都有可能要缴税,这叫财产和资源税。
  • 税很多事和特定行为有关,把它们打包一下收的税,就叫行为税

七、买房前必须知道的二三事

  • 宅基地所有权归集体,使用权归农民,不存在什么年限,住到宇宙爆炸都没问题。但宅基地的房子有个问题,没房产证,没产权,没土地使用证,俗称小产权房。这种房子自己住没问题,但要想卖给外村人或者城里人,就难喽!
  • 一般买这些地的,都是房地产公司,交的钱就叫土地出让金,但使用权是有期限的,比如70年,相当于租地。房地产公司怎么赚钱呢?很简单,盖楼卖给消费者,这就是我们熟悉的商品房。产权时间是从开发商拿地开始算的,以70年产权为例,实际你买的房子的产权到期时间可能要比70年短。
  • 国家收回土地土地产权到期了,如果国家想收回来用,那你的房子就要拆迁了,你的人生可能就不一样了。

九、借钱那些事儿:如何避开借贷中的那些坑

  • 个人、公司和政府,三者之间的不同组合,就对应了我们身边不同的金融产品。
  • 按传统经济学家的说法,人是理性的,大家都只会把钱借给靠谱的人,也只会借自己有能力还的钱。次贷危机首先得弄清楚什么叫次贷:[插图]一边美国银行敢给,另一边矮矬穷真敢借,也不想想自己能不能还上
  • 法律就说了,民间借贷的利率高过36%属于高利贷,是非法的。

十、消费心理学:消费者防剁手指南

  • 大家购物时,会先找差不多的货比一比,一般不会选高价和低价,而是选中间价。这就是托奥斯基的“价格锚点”理论。
  • 特沃斯基和卡尼曼的“损失规避”理论。简单来说就是比起得到,大家更怕失去。所以就算是一件好东西,一旦发现会给自己带来损失,很多人就宁愿不要。

十一、传销大起底:珍爱生命,远离传销

  • 整个过程都没有真正的产品,赚钱全靠拆东墙补西墙。这个简单的骗术,就是金融界大名鼎鼎的庞氏骗局。这个骗局的死穴就在于:需要不断有新钱注入,一旦没有蠢萌新人进来,大家全都要玩儿完。四个字形容它的特点,那就是:一旦停药,骗子就会赶紧拿钱跑路,大家的投资款就打水漂了。
  • 如果有一天,再也发展不到下线了,整个系统就崩溃了。也就是说,传销的本质就是:不要产品,只要人。

一、开篇:我们为什么要了解金融危机

  • 经济有个定义:价值的创造、转化和实现。简单地说,经济就是资源配置,但是人类总是在这条路上跑偏。
  • 博大精深的中华文化告诉我们,把“危机”拆开来看,就是:“危险”和“机会。
  • 二战之所以能打起来,部分原因是美国的国内危机,演变成了全球危机。
  • 金融危机不是绝对的坏事,也可能是社会的崩溃疗法

二、金融危机就要来了:明斯基时刻

  • 人类社会的经济是有规律的,每次繁荣后面,都跟着一波波衰退。人们把这种周期性经济衰退的现象叫:经济危机。而经济的衰退常常是由于金融危机导致的。
  • 经济是有周期的,金融危机经常发生,原因可能是金融不稳定。经济周期有大有小,很多小周期构成了一个大周期,明斯基时刻说的是大周期的转折点。
  • 借的这部分钱,就叫杠杆。借钱用来扩充资产,就是加杠杆。杠杆让你赚得多,但赔起来也多。
  • 杠杆越来越高,最后还不起了,可能就会引发危机。所以该咋做知道了吧?去杠杆啊!简单说就是还钱,一般政府都会做这些事:方法1:债务违约重组钱暂时还不上是吧?银行为了把钱要回来,只好想点别的办法,比如少还点,延长还款期限。方法2:财富再分配给土豪加税,把收来的钱用来发福利、创造就业岗位,这样失业的人就慢慢有了钱,就能把欠的还上。方法3:货币调节为了解决问题,政府会和中央银行商量,让中央银行多印一些钱,然后花到了大家身上。

三、一朵花酿成的惨案:郁金香泡沫(上)

  • 郁金香泡沫,堪称金融危机鼻祖。
  • 赚了钱就按比例分红,赔了钱就只承担有限责任,这就是世界上第一家股份有限公司

四、一朵花酿成的惨案:郁金香泡沫(下)

  • 合同到期了,就能拿到郁金香,反正不会亏,大家都乐意。这种玩法就是期货的雏形。荷兰人全国总动员炒郁金香,船不造了,生意黄了,钱投进去连个响都没有,经济急剧下滑。
  • 郁金香泡沫过去了,后来又有南海泡沫、密西西比泡沫,甚至次贷危机。人们好像从来不会吸取教训,直到现在咱们身边也还在发生这种事:所以看完这个故事,对咱们还是很有用的,起码你再看到有些东西很贵,但明显不值那个价,人们却一直往里砸钱投资时,就应该知道那很可能就是金融泡沫,要保持冷静。价格虚高的东西别碰,要投就投有价值的,这叫理性。

五、连牛顿也算不出的疯狂:南海泡沫

  • 对南海公司来说,“韭菜”就是一茬茬的钱,问题是怎么收割呢?南海贸易特许权本来就是个金字招牌,再加上政府的担保,就能让不明真相的老百姓信得不要不要的。“韭菜们”都看好南海公司,纷纷买入他们的股票。
  • 上到国王,下到家庭主妇,全在炒股,却不管不问公司经营啥情况,这就很容易——出大事啊!

六、股票连涨13个月会怎么样:密西西比泡沫

  • 他首先要做的不是跑业务,而是开始发股票,而且能用国债来换股票。大家都很看好公司,因此股票老值钱了。现在能用国债换,简直是睡觉都能笑醒!这样一换,国债被抵销,国家就不用还老百姓钱啦!法国经济繁荣靠股市,但股市其实很虚,因此所谓的经济繁荣就是个泡沫啊!最后路易十六被送上断头台,这就是著名的法国大革命。

七、房价是怎么被炒起来的:1837年美国大恐慌

  • 1837年美国大恐慌从本质上说就是一个美国人瞎炒房的故事。简单来说,贷款变难了,这叫信贷紧缩,缩得太紧,导致市场上没钱,美国经济就容易不景气。总结一下,美国跟英国房东打了两架,欠了外债,为了还钱开银行,薅了老百姓的黄金,最后把自己的经济搞坏了。
  • 很快国内经济一落千丈,纸币没人信,黄金又没有,股票暴跌,很多行业破产,大家都找不到工作。这就是美国第一次萧条,史称美国大恐慌。
  • 1848年,恐慌了11年的美国在加州的一个地方,发现了大金矿。本来缺黄金,一下子却变成了矿主,美国突然不慌了,这就是历史上著名的淘金热。
  • 简单来说,美国大恐慌其实就是一个炒房的故事,房子是用来住的,不是用来炒的,可惜美国人并不懂这个道理。

八、美联储的诞生:1907年美国银行危机

  • 南方要搞农业,北方要搞工业,一言不合就开撕,这就是咱们熟悉的南北战争。
  • 他来自传说中的摩根家族,据说当时身家达13亿美元。于是摩根撸起袖子,着手拯救美国:他先找了几家银行,成立了一个由富翁们组成,专门给发愁的信托和银行送钱的联盟——摩根干的其实是中央银行的活,所以美国觉得需要一家中央银行来统一管钱。成立新机构要取个新名字,叫啥好呢?美国老百姓已经听到银行就害怕了,所以不能有银行两个字。那就叫美国联邦储备局吧

九、实现美国梦的三大法宝:泰罗制、福特制和猪

十、1929—1933年美国大萧条

  • 自由放任主义。结果小公司一个个倒下,而强势的公司却越做越大,逐渐成了江湖的不败神话:垄断的意思大家都知道吧:一家公司赶走竞争者,占领市场一家独大。但垄断常常会产生不好的结果,少数人资源独占,而多数人吃糠咽菜,贫富差距很快就被拉大了。
  • 当时的资本家一心想赚钱,玩命搞生产,可问题是:多数人穷得连饭都吃不起,他们能买得起啥?所以当时的社会背景就是:资本家们一瞅,一个个的都不消费,多影响自己赚钱国家经济啊!
  • 消费力虚了,农业也跟着倒了血霉,农产品卖不出去,只能销毁。很多农民宁可把牛奶倒进河里,也不降价或者送给穷人。为什么呢?因为一旦牛奶降价,其他货为了卖出去,只能被迫降价,这样会导致卖家利润更低。另外,想卖货得先砸钱,比如投入运费,货倒了反而损失小。
  • 经济危机最严重的问题就是失业。
  • 由生产过剩引发的经济危机。

十一、拯救美国的两位“救市主”:胡佛和罗斯福

  • 胡佛是一个坚定的自由放任主义者,这种说法其实不太对,因为他在经济危机之后一直在积极干预经济。

  • 新上任的总统就是罗斯福。罗大爷觉得,政府应该对市场插手,不能只当看大门的保安,想解决危机,就要做连你家垃圾都要管的——这套想法其实就是凯恩斯主义,当然里面的内容还有很多,这里简单理解一下就好。既然胡佛搞砸了,那这一套还好使吗?咱看看罗斯福怎么做的就知道了。
    罗斯福放了个大招——废除金本位。金本位制度规定美元和黄金挂钩,两者价值要对等,不能瞎印美元。现在脱钩了,政府就能多印点美元给银行,而且美元一多就贬值。对其他国家来讲,美国商品便宜了,秒变抢手货。

  • 政府搞基建招工,然后给工人们发工资。

  • 胡佛和罗斯福的很多政策其实差不多,都整顿了银行,都不让降工资,但为啥一个失败而另一个却成功了呢?这里就要说一下经济周期了。啥叫经济周期?简单说就是:下滑期,再厉害也没用;上升期,躺赢不是梦。

重构-改善既有代码的设计-摘要

发表于 2020-11-01







《重新定义团队:谷歌如何工作》-笔记

发表于 2020-10-24

自序

  • 韦尔奇和康纳迪采用了20-70-10的绩效排名体系,在这种体系下,他们将通用电气的员工分为三类:最优秀的20%,中间层的70%,末尾的10%。最优秀的员工得到赞扬,作为奖励可以选择工作任务,参加领导力培训项目和享有优先认股权。末尾的10%会遭到解雇。

  • 清晰明了的“前20%”“中间70%”和“末尾10%”被更加委婉的描述方式取代:“顶尖人才”“极具价值”和“需要改进”。

  • 因为自由的状态是以自由表达为基础的,而自由表达又依靠对信息和真实情况的了解。

  • 在这里有时会精疲力竭,有时会倍感沮丧,但永远奋发进取,创造有目的性的、自由的和充满创造力的环境。

前言 为什么谷歌的原则也对你适用

  • 当员工信任领导层的时候,他们就会成为品牌的代言人,从而为其家庭、所处的群体和环境带来积极的改变。员工生产效率变高,企业发展增速,顾客购买热情高涨,商业投资回报也就自然而然地实现了。
  • 从管理的核心角度来讲,权力的动态方向恰与自由背道而驰。员工要依靠管理者,希望取悦他们。然而,注重取悦管理者意味着与其进行开诚布公的探讨是有风险的。如果你不取悦他,内心就可能惶恐不安或焦躁愤恨。同时他还要保证你实现某些工作成果。
  • 谷歌应对此类问题的方法是割开这个结。我们刻意剥夺了管理者对员工的控制权。下面一些例子是谷歌的管理者不能单方面做出的一些决定:•雇用谁•解雇谁•如何评估一个人的表现•给某个人加薪多少,给多少分红或分配多少股权•选谁来拿最佳管理奖•给谁升职•代码何时才算合格,可以纳入到公司的软件代码库中•一种产品的最终设计以及何时投放市场。
    上述决定都是由一组同事、一个委员会或一个特别任命的独立团队做出。
  • 升职的问题又摆到了眼前,这时他们又会惊愕地发现自己没有权力独自决定给他们认为团队里最优秀的员工升职。问题在于,你和我对“最优秀的员工”的认识有所不同。也有可能你的团队中最差的成员比我的团队最优秀的员工还要好,这种情况下,你的整个团队都应该升职,而我的团队成员都不应该升职。
  • 一名管理者到底该做些什么呢?只有一件事情可以做。按照我们的执行总裁埃里克·施密特的话说就是“管理者服务于团队”。和其他企业一样,我们当然也遭遇过意外和失败,但是在谷歌这种不干预的领导方式下,管理者的关注重点不是惩罚或奖励,而是清除路障,鼓励团队。
  • 只有公司采用了给员工充分授权的经营方式(比如,剥夺管理者的决定权,并将该权力分配给一些个体或团队),为员工提供工作之外的学习机会,提高团队信任度(给团队足够的自主权,允许员工自行组队),或是组合利用上述方法,这样业绩才能得到提升。
    简而言之,只有当企业着手给员工更多的自由时,业绩才能提升。
  • 我们所做的绝大多数事情的费用都极低。即便是只拿死工资的时候,也能把工作做得更好,使员工更有幸福感。其实,越是在经济状况不好的时候,善待员工越是重要。
  • 我们只需要坚信员工都是好的,再就是要有足够的勇气,把员工看成是企业的主人翁,而不是把他们当成机器。机器会完成工作;主人翁会竭尽所能帮助企业和团队获得成功。人的一生大部分时间都在工作,但是对多数人而言,工作是一件痛苦的事情,只是一种谋生的手段。可以不必如此的。
  • 如何更好地探寻和发展自由、富有创造力和宽松的环境,使员工在这种环境下工作。

第一章 成为一名创始人

  • 他们都希望创造出这样一家公司:工作有意义,员工可以尽情发挥自己的激情,他们和他们的家人都得到关怀。

  • 建立杰出的团队或机构的起点是有一位创始人。但是成为一名创始人并不意味着要建立一家新的公司。任何人都有能力成为一名创始人,也可以成为所在团队的文化创造者,不管你是一家公司的第一名雇员还是一家数十年历史的公司中的一员。

  • 我写作本书的愿望之一就是,希望阅读本书的人都能站在创始人的角度看待自己。或许不是一家公司的创始人,但是也可以成为一个团队、一个家庭或一种文化的创始人。谷歌的经历带来的最根本的一点经验就是你必须先决定自己想要成为一名创始人还是一名雇员。这个问题关乎的不是实际的所有权而是做事的态度。

  • 谷歌工作法则:成为一名创始人□把自己看成是一名创始人□像创始人一样行动

第二章 “文化可以把战略当早餐一样吃掉”

  • 如果你给员工以自由,他们将还你以惊喜

  • 公司文化的三个根本元素:使命、透明和发声的权利。

  • 我们的使命是“整合全球信息,使人人都能访问并从中受益”

  • 谷歌的使命与众不同,既在于其简洁明了,也在于其未曾言及的方面。没有言及利润或市场。没有言及顾客、股东或用户。没有言及为何选此作为公司使命,也未曾言及如何实现这些目标。相反,谷歌的使命整合全球信息,使人人都能访问并从中受益是一件不言自明的好事。这样的使命使个人的工作有了意义,因为它不是一种商业目标而是一种道德目标。史上最有影响力的运动都要有道德动机,或是追求独立,或是追求平等权利。

  • 归根结底,我们永远也无法达成我们的使命,因为总有更多的信息需要集成,总有更多的方式可以使人们从中受益。这样就给我们创造了动机,促使我们不断创新,探索新的领域。要成为“市场领导者”这种公司使命,一旦实现,就难以再带来更多的激励。

  • 我们在践行公司使命时,也带来了令人惊喜的实用价值。

  • 每一类职业的人里面都大约有三分之一将自己的工作看作一种使命。这样做的人不仅更快乐,而且也更健康。

  • 如果你相信员工,就不必害怕与他们分享信息透明是我们公司文化的第二块基石。“默认开放”(Default to open)是在开源社区中时常会听到的一个短语。

  • 如果你能给员工以自由,他们就会为你创造惊喜。他们有时也会令你失望,但是我们也都知道人无完人。这并非宣扬自由的檄文,只不过是权衡利弊后的选择。

  • 谷歌工作法则:打造了不起的文化□将工作看作是一种命运的召唤,而且工作要有富于意义的使命。□给人以稍多于你的舒适区的信任、自由和自主权。如果你没有感到紧张,那是因为你给的还不够。

第三章 只聘用比你更优秀的人

  • 如果我们前期选人的时候能做到更好,也就意味着聘用他们之后在这些人身上投入的精力就会减少。聘用水平超过90%应聘者的员工,最糟的情况他们也能有平均水平的表现。这些员工几乎不可能成为公司里表现最差的

  • 你如何能够判断自己到底有没有找到一名非凡的人才?我所遵循的首要原则——也是你在招聘时需要做出的第二个改变——是:“只聘用比你更优秀的人。”我所聘用的人都在某些特定的方面比我更优秀的。

  • 从《人才的谬见》一文中得到的教训不是“不要聘用聪明人”。而是“不要只聘用聪明人”。至理名言。出色的招聘工作不仅在于聘请到名头很大的人、顶尖的销售人员或最聪明的工程师,而且在于搜寻到在你所处组织的环境下能够成功的最优人才,在于找到能使周围每个人都更加成功的人才。

  • 谷歌工作法则:关于招聘□资源有限的情况下,将人力资源费用首先投入到招聘上。□慢慢来,聘用最优秀的人才,只聘用在某些特定的方面比你更优秀的人,不要让经理独自做团队人员聘用决策。

第四章 搜寻最优人才

  • 最优秀的人并不在寻找工作。表现极为优秀的人在现在的工作岗位上很开心,满足感很强。他们不会进入人们的推荐人名单中,因为人们会想为什么要推荐一些在现在岗位上很开心的人呢?而且他们肯定也不会考虑新的工作。

  • 谷歌工作法则:搜寻非凡的应聘者?□要详细说明寻找人才的标准,依此找到最优秀的被推荐人□使招聘成为每个人的工作□不要害怕尝试疯狂的事情,以此引起最优秀人才的注意

第五章 不要相信你的直觉

  • 根据头10秒钟的印象做出的预测是没有任何意义的。这头10秒钟的预测使我们在整个面试过程中都在试图证明我们对某个人的印象,而不是真正地去评估他们。心理学家将这种现象称作证实偏见(Confirmation Bias),“倾向于寻找、解释或优先考虑那些能够支持我们观点或假设的信息”。

  • 多数的面试都是在浪费时间,因为99.4%的时间都用在证实面试官最初10秒钟的印象,不论印象好坏。“请做一下自我介绍。”“你最大的缺点是什么?”“你最大的优势是什么?”毫无价值。

  • 你不仅要评估应聘者,还需要让他们喜欢上你。真的。你得让他们有一次非常棒的体验,处理好他们关心的问题,使他们感觉刚刚经历过一生中最快乐的一天。

  • 谷歌工作法则:筛选新雇员?□设定高质量标准□寻找自己的应聘者□客观评估应聘者□给应聘者一个加入的理由

第六章 打造最幸福的公司

  • 权力导致腐败,绝对的权力导致绝对的腐败

  • 如果要做假设,也应该认为掌握权力的人是恶的,权力越大,恶念越深……伟人多数是恶人,即便他们不滥用权力,而只是施加影响力;如果你再考虑到权力带来腐败的可能性或必然性,他们的恶会更甚。

  • 回想一下你参加过的会议。我敢打赌,级别最高的那个人总是坐在会议桌的上首。是因为他们匆匆地从一间办公室冲到另一间办公室,抢先来到会议室,才占到这个最好的位置吗?下次仔细观察一下。随着参会者陆续到场,他们会刻意将上首的座位空着。此种现象证明了我们一些不自觉的微妙举动都创造了等级制度。没有指示,没有讨论,甚至没有有意识的思考,我们就会为“上级”留出位置。

  • 我们最高层的一些领导对这种现象也非常熟悉,并尝试打破这种状态,选择坐到会议桌某一侧的中间。

  • 经理都倾向于累积和运用权力。员工都倾向于服从命令。

  • 不可否认的是,我们很多人都同时扮演着经理和员工两个角色。我们都遇到过控制欲很强的经理,也都遇到过不服从管理的员工,这样的挫败感我们每个人都曾有过。

  • 授权于群众的第一步就是要保证人们能够安全地发表意见。俗话说“枪打出头鸟”,就是警示人不要随便发表评论。正是因为这个原因我们才尽可能削弱经理的权力。他们拥有的正式授权越少,就越难利用萝卜加大棒的政策辖制团队,这个团队的创新范围便会越广。

  • 为了减轻人类内在寻求等级划分的倾向,我们尝试除去显示权力和地位的象征符号。

  • 谷歌坚持的核心准则中一直都有一条“不要耍政治手腕。用数据说话”

  • 经理们忽略的是,每次他们放弃一些控制权,就可以为团队创造一次提升的好机会,也给自己节省出更多时间应对新的挑战。找出某个令你的团队感到沮丧的领域,让他们改变现状。如果有限制,比如时间或资金有限制,就告诉他们。要对员工透明,在塑造团队或公司的过程中给他们发言权。你会惊讶于他们的成就。

  • 谷歌工作法则:授权于员工□消除地位象征□依靠数据而不是根据经理的想法做决定□探寻方法,让员工塑造自己的工作和公司□高期待

第七章 为什么每个人都讨厌绩效管理

  • 关注个人成长而不是评分和奖励,以此改善绩效

  • OKRs(Objectives and Key results,目标和主要结果)。目标必须具体、可度量、可检验;如果你达成所有结果,就能完成目标。

  • 正如普拉萨德·塞迪解释的:“传统的绩效管理体系犯了一个大错。他们将两件应该彻底分开的事情合到了一起:绩效评估和人员发展。评估有其必要性,可确定加薪或奖金等有限资源的分配。发展也同样很有必要性,可以促进员工的成长与提高。”如果你希望员工成长,不要同时进行这两项谈话。确保发展成为你与团队成员之间不断往返的一个过程,而不是年底的一次惊喜。

  • 为了确保员工与经理的交谈更有效,我们整理出一份一页的讲义,分发给他们,在绩效交谈的时候使用。做这一份讲义的目的还是为了使对话更具体,更切合实际。我们给员工分发这些讲义只是为了稳妥;我们希望经理能够覆盖恰当的话题,但是让员工准备好引导讨论也没有什么坏处。

  • 把奖励分配谈话与员工发展谈话分开。两项谈话混为一谈会扼杀学习的动力。不管公司规模多大,这一点都适用。

  • 谷歌工作法则:绩效管理□正确地设定目标□收集同事的反馈意见□通过校准流程确定考评结果□把奖励分配谈话与员工发展谈话分开

第八章 管理团队的两端——最优员工和最差员工

  • 从统计学上讲,这些现象更适合用“幂律分布”(power law distribution)解释。

  • 大多数公司在管理员工时都采用正态分布,大多数员工被列为平均水平,两端为表现差和表现优秀的员工。两端并不像身高分布那样对称,因为失败的员工都被解雇了,最差的应聘者根本就进不了公司,因此左侧的一段很短。但是很多公司认为员工的表现还会符合同样的正态分布。这样的认识是一个错误。事实上,组织中大多数的个人表现符合幂律分布。

  • 并非大批平均水平的员工通过数量优势做出主要贡献,而是由少数精英员工通过强大的表现做出主要贡献。

  • 采用一种不同的方式:我们的目标在于告诉底端5%的每一位员工,他们处于这样一个群体。这种对话不会是幽默风趣的。但是我们向这些员工传递出的信息使这项工作简单了一些:“你在整个谷歌处于底端的5%。我知道这样的感觉不好。我之所以要告诉你是因为我想要帮助你成长,变得更好。”换言之,这不是一次“要么好好干,要么走人”的谈话;这是一次感性的谈话,目的是帮助一个人发展。有一位同事曾经将其形容为“富有同情心的实用主义”。绩效表现糟糕极少是因为某人的能力不足或品性不佳。更多的是由于技能的缺陷(或许可以改进,或许不能)或意愿不足(员工没有做工作的动力)。在后一种情况下,可能是由于个人问题,也可能预示着团队中出现了某个更大的问题需要修正。

  • 通常,调岗之后这个人的绩效能够提升到平均水平。这听起来或许不算什么,但是反过来这样想想:100个人的团队中,吉姆是表现最差的5个人之一。经过这次干预之后,吉姆的绩效表现进入了前50位。

  • 余下的一些员工,有的选择主动离职,有的就只能解雇了。听起来很残酷,但是最后他们通常会更开心一些,因为我们表现出对他们状况的理解,并与他们一道投入了改进过程,而且我们给他们时间寻找一家能够发挥专长的公司。

  • 在分布底端投入时间精力的这个循环意味着你们的团队能够提升很多。员工或是得到大幅的提升,或是离职去别的地方寻找成功。

  • 对员工直接一些实际上是仁慈的表现

  • 我要强调一点,谷歌识别底端5%的员工并非“员工大排名”,不是要按照固定的分布将员工的绩效表现分类。那种考评方式下,员工为了不落在底端会激烈竞争,最终搅乱了公司文化。
    我采访过的每一位现在和过去的微软员工——每一位——都认为员工大排名是微软内部最有害的政策,在这种政策下,无数的员工被迫离职……“如果你的团队中有10个人,你开始工作的第一天就了解到,不管每一位员工多么优秀,都将有两个人获得好评,7个人获得中评,另外有一个人获得差评,”一位前微软软件开发工程师说,“这使员工的注意力都放在内部互相竞争上,而不是与其他公司竞争。”

  • 如果你相信员工本质都是好的,认为他们值得信任,那就必须对他们坦诚相待,保持透明度。这就包括让他们知道自己的绩效拖了后腿。但是在一家使命导向性、有目标的公司,处理人力问题时要有敏感性。多数表现不佳的员工能够认识自己的表现,想要变得更好。给他们改进的机会非常重要。

  • 顶端的员工生活在高产出、良好的反馈意见、更高的产出和更好的反馈意见这样一个良性循环中。他们每天都沐浴在爱的环境中,给他安排的额外工作也使他更加开心。更重要的是要从最优秀的员工身上学习。

  • 最优秀经理手下工作的谷歌人在十几项Googlegeist评估维度上要比最差经理手下工作的谷歌人高5%至18%。除此之外,他们在以下几方面的认可度明显更高:•职业决策更加公正。绩效评估公正,得到升职的都是实至名归的人选。•个人的职业目标能够达成,他们的经理是非常有帮助的支持者和引导者。•工作高效,决策迅速,资源分配合理,从多种视角考虑问题。•团队成员之间没有等级制度,互相尊重,决策依据数据做出而不是靠耍手段,团队内部各人的工作和信念都保持透明。•他们适当地参与到决策制定过程中,并且得到一定的授权去完成工作。•他们可以自由地平衡工作和私人生活。

  • 最优秀经理领导的团队绩效表现也更好,人员流动率更低。

  • 调查显示高分经理具备八种低分经理所不具备的共性:8个氧气项目特性1. 做一名好的导师。2. 给团队授权,不随便插手下属工作。3. 表达出对团队成员的成功和个人幸福的兴趣和关心。4. 高效/结果导向型。5. 善于沟通——聆听和分享信息。6. 在职业发展方面助力团队。7. 对团队有清晰的愿景和战略。8. 具备重要的技术技能,可为团队提供建议。

  • 我们发现在伟大的经理中间,技术专业性是8种特性里重要性最低的一项。不要误会,技术专业性非常关键。一名不会编代码的经理不可能在谷歌领导一个团队。但是在区分最优秀的经理行为时,技术能力在不同团队中是差异最小的。

  • 这是对经理–员工关系的一次华丽反转。想要提高,最好的方法是与那些提供反馈意见的员工进行交谈,询问他们希望自己做出哪些改变。

  • 让处于绩效分布底端的人了解真相,但是不要将绩效与薪酬或职业成果直接挂钩,尽可能用一种积极的方式警示并激励他们。数百名经理需要面对自己并非好经理的现实。

  • 谷歌工作法则:管理团队的两端—最优员工和最差员工?□助力有难处的员工□将最优秀的人放在显微镜下观察□利用调查和检查清单寻找真相,推动员工学习□与人分享员工对你的反馈意见,以身作则采取行动解决问题,身先示范

第九章 打造学习型组织

  • 在某一领域精熟的人,不管是小提琴家、外科医生、运动员[插图]还是拼字比赛冠军[插图],学习的方法都有异于常人。他们将活动分解成细小的动作,比如连续数小时在雨中练习同一种击球动作,不断重复。每一次,他们都会观察效果,做微小的——几乎难以觉察的——调整,逐步改进。埃里克森将这种方式称作刻意练习:有意重复类似的小任务,即时反馈、修正和实验。

  • 不过或许你不想要手下最优秀的销售人员去教学。毕竟,不应该让她全心全意做销售吗?我认为这是一种短视的想法,因为个人的绩效表现的提升是线性的,而培训授课则会带来几何级数的增长。

  • 学习型组织发端于一种认识,即我们所有人都渴望成长,也都希望帮助他人成长。然而,在很多组织中却是员工受教,专业人士负责教学。为什么不让员工同时做两件事情?

  • 谷歌工作法则:打造学习型组织□进行刻意练习:将课程分成易于消化的小块,给出明晰的反馈意见,并不断重复这个过程□请最优秀的员工教学□只在已经证明能够改变员工行为的课程上进行投入

培训的效果衡量四个层次,一是反应,课后调查反馈课堂效果及氛围。二是学习,调查学员学到了那些知识,学习效果如何。三是形行为,接受学员学以致用的反馈,是否有提升,同时调查团队周边或客户的客观评价。四是结果,最终是否导致了效率提升,绩效比变好等等

第十章 不公平薪酬

  • 总结下来共4条原则:1. 不公平薪酬。2. 以成就为荣,不以报酬为荣。3. 创造易于传播爱的环境。4. 精心筹划却遭受失败的要奖励。

  • 如何庆祝成功的同时不滋生嫉妒

  • 这样做带来了不良的后果,假如你是非常优秀的员工,将会得到几次大幅加薪,之后加薪的速度会越来越慢,直到最后你接近容许的薪酬范围上限,加薪也会随之停止。最优秀的队员除了需要高报酬之外,也能持续创造优异的成果。

  • 对于那些快速学习成长和表现最顶尖的人来说,确保你的薪水与所创造的价值相适应有一种方法,就是离开这种垄断的内部市场,进入自由市场。即寻找一份新工作,以你的真正价值为基础,协商薪酬,然后离开现在的公司。这也是你在人才市场上看到的真实情况。

  • 为什么公司不设计一种体系,避免最优秀和潜力最大的员工辞职呢?因为他们对公平有一种错误认识,没有勇气坦诚面对自己的员工。薪酬的公平并不是说所有在同级别岗位上的人都要拿同样的薪水或是上下差不到20%。薪酬与贡献相匹配才能算得上公平。因此,个人的薪酬应该有巨大的差异

  • 正态分布(又称高斯分布)与幂律分布最大的区别在于,某些现象中,正态分布严重低估了极端事件发生的概率。

  • 个人的表现符合幂律分布。事实上,大多数员工都在平均水平以下:•66%的研究员发表论文的数量低于平均水平。•84%艾美奖提名演员获得提名数低于总提名平均数。•68%的美国参议院议员的任职届数要低于平均数。•71%的NBA球员得分低于平均分。低于平均数并非坏事。这只不过是一种数学统计而已。数据显示,非凡贡献者的表现水平要远高于大多数人,他们可以拉动平均数远高于中位数。

  • “10%的产出来自最顶尖1%的员工,26%的产出来自最顶尖5%的员工。”换言之,他们发现最顶尖1%员工的产出是平均产出的10倍,最顶尖5%的员工的产出是平均产出的4倍多。当然,这种算法并非在所有地方都适用。恰如奥博伊尔和阿吉斯所指出的:“工业和以体力劳动工作为主的组织,技术能力有限,对最低和最高产量有严格的标准。”在这些地方的员工表现更接近于正态分布。在这种环境下,极少有机会能做出非凡的成就。但除此种情况之外,幂律分布都占据主导。

  • 那些拿到100万美元奖励的人一定、一定是狂喜的吧?他们确实很开心。我的意思是说,拿到这样的奖励非常激动人心。人生就此改变。之后,我们最优秀的、最有创造力的、又有洞察力的技术人员中有一些(虽然不是全部)——他们曾创造出谷歌历史上最具有影响力的一些产品——意识到自己不太可能通过同样的产品两次获得创始人奖,因此立刻会想要转移到新的产品领域。虽然并非本意,但是我们创造出的这种激励体系,使公司里几乎所有人都不如以前开心,即使有少数人开心了,但也动了念头,不愿继续从事为他们赢得奖励的关键的创新性工作!

  • 我们公开地进行体验奖励,私下里进行差异化奖金和股权奖励。这样的结果使谷歌人变得比以前更开心。

  • 在奖励员工的时候,一定不能只用现金奖励,还要考虑体验奖励。很少有人回顾人生时会只看到一张张薪水单。他们会记住一些谈话、一些午餐,与同事和朋友共度的一些事件。不要用金钱庆祝,要用行动庆祝。

  • 谷歌工作法则:不公平薪酬□控制情感,做到不公平薪酬。薪酬差异化要明显,应符合绩效表现的幂律分布□以成就为荣,不以报酬为荣□创造易于传播爱的环境□精心筹划却遭受失败的要奖励

来源微信读书
分章读书总结:
1、在绩效方面,更多要参考幂律分布而不是传统的正态分布,平均数不等于中位数,实际上组织内大部分员工处于平均值之下,组织10%绩效产出来自1%的员工,出于人才保留和激励,不公平薪酬完全是合理且必要的;
2、人对于公正性的感知非常强,会极大影响他对于自身价值的认识、工作满意度、上级信任度和组织忠诚度,所以极端奖励体系要同时满足分配公正和程序公正;
3、在奖励员工时,不能只考虑现金奖励,还要考虑体验奖励;金钱激励是即时性的,所以在金钱激励时要辅以绩效谈话或颁奖等仪式性行为,以加深记忆,拉长激励的保质期,但总体来讲,用行动庆祝比用金钱庆祝更有持续记忆力;
4、要在组织内创造易于传播爱的环境,公开的赞许是最有效的一种管理工具,相信员工能做正确的事,结果通常他们会去做正确的事;
5、奖励成功也要奖励失败,否则员工会失去创造方面的冒险性;

第十一章 世上最好的东西是免费的

  • 我们做的几乎所有事情都是免费或费用很低的。所有这些项目都是为了提升效率,创造社区意识或创新精神。

  • 我不能给出数据证明有多少经济价值是因为免费洗衣机创造的,因为我根本就不在乎这些。我还记得职业生涯早期的麻烦经历,从我公寓到地下室的公用洗衣机要经过堆满杂物的楼道和摆满清洁用品的楼梯,而后要困在家里好几个小时,生怕别人来偷走我的衬衫。超级烦人。我们为什么不在园区找一间空房间,放上几台洗衣机和一些清洁剂,让生活稍微愉悦一些呢?我们为什么不请一些演讲者来园区给我们做演讲呢?

  • 我们进行过离职调查,从来没有任何人说这些服务能够使他们留下,也没有人因为这些服务才加入谷歌。这其中并没有什么大秘密:我们所做的这些事情(大多数)仅是举手之劳,但却收获巨大,而且我们感觉这样做是应该的。

  • 微小的关怀和资源投入也能带来巨大的成果。

  • 谷歌工作法则:效率、社区意识和创新精神?□使员工的生活容易一些□想办法说可以□生命中的不幸罕有发生……一旦员工遭遇不幸,要伸出援手

第十二章 助推

  • 诺贝奖获得者、普林斯顿大学荣誉退休教授丹尼尔·卡尼曼在他的《思考,快与慢》(Thinking, Fast and Slow)一书中描述人类有两套思维系统。其中一套慢、有深度、有思索、以数据为导向,而另外一套快、依靠本能、属于直觉思维系统。多数时候我们会依赖第二套思维系统,因此即便我们认为自己理性的时候,其实恐怕也并非如此。

  • 在面对对自我观念和自我认同的威胁时,防御是一种自然的反应。

  • 只需简单地提供信息,然后依靠人的本性——好胜的本性和利他主义的本性——就能改变一个机能失调的团队,看到这种现象真是既有趣,又令人振奋。

  • 谷歌工作法则:助推走向健康、富有和快乐□区分“实是”和“应是”的不同□进行许多小的实验□助推,不要硬推

第十三章 谷歌的教训

  • 任何想法走了极端都会变得愚蠢可笑。

  • 因此外界批评从原则上来讲是对的,但在实践中却并非如此。每年我们都要遭受一次重大的信息泄露。每一次都要进行一次调查,而且每一次信息泄露不管是刻意而为还是意外事故,不管是出于善意还是恶意,当事人都会被解雇。我们不会宣布泄露信息的人是谁,但是我们会让公司里的所有人都知道泄露的信息是什么,以及后果怎样。很多人了解到很多信息,总不可避免地有几个人会搞砸。但这样是值得的,因为泄露信息造成的损失相比我们享受的开放性而言并不算重大。

  • 一次失败的绩效管理变革。每一次我们对谷歌的绩效管理体系做出改变的时候,都会遭遇两个不证自明的真理:1. 没人喜欢当下的体系。2. 没人喜欢改变当下体系的提议。

  • 谷歌工作法则:搞砸的时候□承认错误。坦诚面对错误□吸取各个方面的意见□不管什么坏掉了,修好□找出错误中的寓意,加以传播

第十四章 从明天起你可以做些什么

  • 问题并非管理体系需要如何改变人性,而是如何改变工作的性质。

  • 一家组织的经营方式可以遵循两种极端的模型。本书的核心在于我的信念,相信你可以选择出期望打造何种类型的组织,而我所做的只是展示一些实现目标的工具。“低自由度”的一端是指挥控制型组织,对员工的管理很严格,工作强度大,公司对员工弃之如敝屣。“高自由度”的一端以自由为基础,员工受到尊重,对公司如何发展有一定的话语权。

  • 如果你希望建立高度自由的环境,下面有10个步骤可以帮助你的团队和组织实现转型。1. 赋予工作意义2. 相信员工3. 只聘用比你更优秀的人4. 不要将职业发展与管理绩效混为一谈5. 关注团队的两端—最优员工和最差员工6. 既要节俭又要慷慨7. 不公平薪酬8. 助推9. 管理日益提升的期望10. 享受!然后回到第1条,再来一遍

  1. 赋予工作意义工作至少占据了我们生活三分之一的时间和清醒时的一半时间。工作可以——也应该——不仅仅是一种达成结果的手段。非营利组织从很久以前就已将工作的意义作为吸引和激励员工的方法。比如,帮助难民的非营利组织避难通道(Asylum Access)的创始人艾米丽·阿诺德–费尔南德斯建立起一个世界一流的全球团队,这个团队的建立完全基于成员的共同愿景,即帮助难民找到工作,送他们的孩子上学,帮助他们在新的国家中建立起新的家园。在很多环境下,工作仅仅是为了得到薪水,但是亚当·格兰特的研究成果证明,只需与那些因你的工作而受益的人建立起微小的联系,便能大幅提升生产效率,而且还能使人更开心。所有的人都希望自己的工作有一定的目的。将工作与一种超越日常但却能真实反映所做事情的理念或价值观联系在一起。谷歌立志整合全球信息,使人人都能访问并从中受益。任何在这里工作的人都要践行这项使命,不管职位多么低微。这种使命吸引来了人才,激励他们留下来,去冒险,以最高水平的表现去工作。如果你是一名鲑鱼切片工,你就是在养育他人;如果你是一名管道工,你就是在改善人们的生活质量,保持他们家园的清洁和健康;如果你在生产线上工作,不管生产的产品是什么都将为人所用,帮助到他们。不管你在做什么,都会对某人有重要的意义。而你所做的这项工作对你也应有重要的意义。作为一名经理,你的工作就是帮助员工发现这种意义。
  2. 相信员工如果你相信人本善,就应如此行动。要对员工保持透明和真诚,给他们话语权,决定如何行事。从小事做起也可以。真的,你之前表现出的信任越少,小的举动就会令人感到越重大的意义。对于一家传统上一直进行不透明管理的公司而言,一个意见箱,员工知道其中的意见真正地有人读过且有人处理,会令人有革命性的感觉。请团队成员问你是什么促使你做出最近的一些决定的。如果你拥有的是一家小商店,要经常询问员工他们认为做出哪些改变能使商店更好,或者问他们如果这是他们的公司,他们会怎么做。因为你希望他们能这样做。就好似这是他们的公司一样。要实现这种状态唯一的方法就是你放弃一小部分权力,给他们朝这个方向发展的空间。这听起来或许有些令人望而却步,但其实并不需要冒太大的风险。管理层随时都可以拿走意见箱,或告诉员工不再需要他们的意见,或者甚至可以解雇一些人。如果你担心这样做会有损你的权威,那么就告诉员工每一种改变都只是试行几个月。如果可行,就继续。如果不可行,就停下来。即便仅仅是尝试,你的员工也会心怀感激的。如果你是团队的成员,就向你的老板提出这样的请求:给我一个机会。帮助我理解你的目标是什么,让我理清如何达成这些目标。这样的小举动将创造通往主人翁文化的途径。
  3. 只聘用比你更优秀的人企业总会认为尽快填补一个空缺岗位比耐心寻找最适合一个岗位的人更重要。有销售人员对我说过:“宁滥毋缺”,意思是说他们宁愿由一名领域内中等水平的人完成70%的限定销售额,也不愿让一个岗位空缺。但是在招聘质量要求上的妥协就已经是一个错误了。聘用糟糕的员工就好似在锅里扔进了一颗老鼠屎,不仅自身的表现不佳,还会拖累周围人的表现、士气和精力。如果拒绝一个人意味着其他每个人在短期内都需要更努力地工作,只需要提醒他们回想一下与上一个浑蛋同事共事时的遭遇就好了。成立委员会完成招聘工作,预先设定客观的标准,永远不要妥协,定期查看新聘用的员工是否优于以往聘用的员工。能够证明你的招聘工作做得很好的是新聘用的员工中十有八九都比你更优秀。如果他们不及你优秀,暂时不要聘用,直到找到一个更优秀的人。短期内你们的工作会放缓,但最终你将建立一个更加强大的团队。
  4. 不要将职业发展与管理绩效混为一谈克里斯·阿基里斯向我们展示了,即便最成功的人也有学不会的时候。如果他们都无法学习,那么余下的我们又能有什么希望呢?面对自己的缺点时总是难以令人愉悦。如果你将后果与批评结合在一起,如果员工感觉犯了一个错误就意味着在职业或经济上受损,那么他们就会争辩而不是保持开放的态度去学习和成长。发展谈话要随时进行,确保平稳且富有成效,恰如我以前的经理在每次会后进行的谈话一样。开启一次发展谈话的时候永远要保持这样的态度:“我能做些什么帮你取得更大成功?”否则,员工的防御心理就会增强,学习将中断。在实现目标的道路上,要确保发展谈话的平稳进行。不管目标有没有实现,两种谈话都应在空间和时间上分开。一个绩效考评阶段结束之后,立刻直入主题就设定的目标进行讨论,探讨哪些目标已经实现,以及奖励如何与绩效挂钩。但是这一次交谈应该只针对成果,而不是过程。可能没有达成目标,可能完成了目标,也可能超额完成了目标,每一种结果都应该对应不同的奖励或鼓励。如果处理好这方面的工作,绩效讨论就不再会是突然袭击,因为在整个过程中你们都在进行沟通,员工也能感觉到你在每一步工作上对他们的支持。不管在什么情况下,都不要完全依赖经理确定员工表现的确切情况。为了团队的发展,恳请同事贡献意见,即使是简单地问询一些问题或发布一些简单问卷也可以。至于绩效考评,要求经理们坐在一起组成团队,共同校准考评结果,确保公正。
  5. 关注团队的两端——最优员工和最差员工将最优秀的人放在显微镜下观察。他们结合了环境和技能,精心打磨才理清了如何成就超常表现。不仅要识别出最佳全能员工,还要识别出特定方面最突出的员工。不要寻找最优秀的销售人员;寻找面向特定规模的新用户销售量最大的人。找到能在夜雨中练习高尔夫球那样的优秀人才。在专业方面分得越精细,就越利于研究你的明星员工,发现他们比其他人更成功的原因。然后不仅要让他们成为其他人的榜样,围绕他们所做之事制定检查清单,还要请他们做老师。教授一项技能是掌握它的最好方法之一。请明星员工做教员,即使是半小时的咖啡交谈时间,也要促使他们清楚地讲述自己是如何开展工作的,而这个过程也有助于他们的成长。如果你身边有这样的同事,要仔细观察他们,多向他们提问题,利用这个机会从他们身上获取知识。与此同时,对表现最糟糕的员工也要心怀怜悯。如果你的招聘工作没有犯错,那么大多数陷入困境的员工都是因为没有找到合适的岗位,而不是因为自身笨拙。帮助他们学习或找到新的角色。但是如果上述努力失败,立刻辞退他们。让他们留在公司里并非仁慈,在一个自己并非最差员工的环境中,他们会更加快乐。
  6. 既要节俭又要慷慨我们为员工做的大多数事情都不需要任何花费。请供应商来公司为员工服务或与当地三明治店协商为公司送午餐。TGIF和嘉宾演讲者需要的仅仅是一个房间和一支麦克风。然而却带来了无比丰富的财富:启发谷歌人开发出一种新的服务或引发讨论。省下钱来,在员工最需要的时候,在他们遇到灾难或大喜之时使用。当某人需要急诊医疗护理或迎接家庭新成员之时,你的慷慨会带来最大的影响力。关注人类最重大的一些时刻能够突出你们的组织关心每一个员工。了解到自己在人生低谷和顶峰之时背后都有整个机构的力量做后盾,每个人都会感到宽慰。这一点对很小的公司也同样适用。我的父亲成立过一家工程公司,他亲自领导了30年。他深切关怀每一位员工,不仅付给他们薪水,而且善意赞扬,为他们提建议,做引导。团队中任何一个人任职5年之后,他都会拉他们出来私聊一番。他告诉他们公司有一项退休金计划,5年时间的投入已经满额。除了员工自己存下来的积蓄,他还为他们每个人额外存了一笔钱。有些人欢呼雀跃,有些人感动流涕,有些人只是简单地谢过了他。他没有提早告诉员工这项计划,因为他不希望人们为了钱才留下来工作。他希望员工留下来是因为喜欢创造东西,是因为喜爱这个团队。关键时刻他很慷慨,因此也使结果大不相同。
  7. 不公平薪酬不管你们的人力资源部门是怎么对你说的,要记住大多数工作中的绩效表现都是符合幂律分布的。你们的团队中90%甚至更高的价值都是由顶尖的10%的人创造的。因此,最优秀的员工远比平均水平的员工更有价值。他们的价值或许比平均水平的员工高50%,或许高50倍,但是不管高多少肯定值得你为他们付出更多。一定要让他们感觉到这些。即使你没有足够的资金为他们提供超高额的薪水,但是更高一些的薪水也算一种心意表达。另外一位员工对这种奖励或许会有些不高兴,但是你可以坦诚相对,解决这个问题:向他们解释薪酬差异化的原因,以及他们怎么做才能改变现状。与此同时,在公众认可方面要慷慨投入。团队的成就要庆贺,虽然失败但却学到重要经验教训的时候也要鼓励。
  8. 助推本书中提到的各种想法中对你未来的人生能够带来最大切实改善的一种就是改变每笔收入中存下来的金额。如果比较30年里赚到的钱同样多的一些人,他们累积的财富却可能有3000%的差异,而这一切几乎完全取决于你存下了多少钱。存钱从来都不是一件简单的事情。除非你比克罗伊斯[插图]还要富有,节省下来的每一个美元都像是一种利弊权衡。我是要买品牌货还是一般产品?是买3美元的花生酱还是吃甜点?换一辆新车还是再凑合一年?我毕业后的第一年,当演员的同时还做服务生,经常光顾小镇附近的女主人廉价商店(Hostess Thrift shop),店里售卖一些马上就要过期的面包和点心。我有了零食蛋糕(有节制的!),而且还能每周多省下几美元。要记住,督促谷歌人提升不到3%的存钱比例,每位谷歌人的退休基金将增加262000美元。很多人听到下面的事情或许会觉得很疯狂。我认识一些人,将度假胜地汉普顿斯的10万美元夏日出租房看成生活必需品;我的一些银行家朋友虽然在2008年丢掉了工作,但还是能躲到海滨别墅里度假。我一直反复强调这一点,但人们还是不愿意改变存款比例。计算出当前你存下来的钱占收入的比例,从现在起再多存一些。不论何时这都不是一件容易的事情。但这样做肯定是值得的。上面是对你个人而言。现在环顾四周,看看你所处的环境是如何助推你与周围的人的。你能很容易地看到其他人,与他们建立联系吗?你们冰箱里最不健康的零食放在与人的视线平齐的位置上吗?你给同事和朋友发邮件或短信的时候是分享好消息还是抱怨发火?我们都时刻受到环境的助推,也时刻助推着周围的人。利用这一点,使你自己和你的团队更快乐、更高效。工作场所的空间布置要鼓励你所期望的行为:如果你需要员工协作,但却受困于工位是小隔间,那么就推倒隔断。向员工传递讯息的时候要深思熟虑。分享一些积极的数据,比如参加当地慈善活动志愿者的人数,鼓励其他人参与。你将惊异于同一个工作场所给人带来的感觉会有如此大的不同。
  9. 管理日益提升的期望有时你会犯错误,这时就需要倒退几步。要准备好吃下你们自己的枸杞派。明白了这一点之后,在开始实验之前,告诉周围的人你打算实验本书中的一些想法。这样做有助于促使他们从批判者转变为支持者,实验走上了弯路的时候,他们的质疑将给你带来更多的益处。
  10. 享受!然后回到第1条,再来一遍拉里和谢尔盖立志创立一个他们都希望为之工作的地方。你也可以做同样的事情。即使你刚毕业加入一家公司,还只是一名初级职员,或者是第1000006号职员,你也可以像一位创始人一样选择与周围人的沟通方式,选择如何设计自己的工作场所,选择如何领导。你这样做可以帮助创造一个能够吸引地球上最优秀人才的场所。
  • 这并非一劳永逸的努力。想要打造了不起的公司文化和环境要求我们不断地学习和革新。不要担心立刻尝试所有事情。实验本书中介绍的一种或多种想法,从实验中学习经验,对项目进行调整,然后再次尝试。这种方式的美妙之处在于,良好的环境可以自我强化:所有这些努力可以互相支持,共同创造出一个有创造力、有趣、努力且效率极高的组织。如果你相信人本善,那么就应在工作中践行自己的信念。谷歌已经30多次被卓越职场研究所评为最佳雇主,另外还获得数百种支持女性、非裔美国人、老兵等人群的组织和政府、社会机构颁发的荣誉。但是我们并非第一个“最佳雇主”,也不会是最后一个,甚至在今日也不是唯一一个。谷歌真正擅长的是大规模运营,建立起的体系服务20亿人也如服务10人一样周到可靠。员工的创新得益于一批有先见之明的创始人、狂热的企业文化捍卫者、周密的学术研究,以及具有创造力的公司和政府。数千名谷歌人共同塑造了我们运营的方式,推动我们找到最具有创造力和最公平的方式解决与人相关的问题,使我们肩负起了责任。我有幸与见解深刻、勇于担当、富于创造力的同事和人力运营团队共事,竭尽全力才跟得上他们的步伐。我每天都能从他们身上获得启发。每年有成千上万的人参观我们的园区,问我们:“为什么这里的人这么开心?”“谷歌的秘密是什么?”“我在我的组织里做些什么才能使其更具创新性?”答案就在你的手中。谷歌工作法则1. 赋予工作意义2. 相信员工3. 只聘用比你更优秀的人4. 不要将职业发展与管理绩效混为一谈5. 关注团队的两端——最优员工和最差员工6. 既要节俭又要慷慨7. 不公平薪酬8. 助推9. 管理日益提升的期望10. 享受!然后回到第1条,再来一遍

后记

  • 我们一直围绕着4条基本原则构建谷歌人力运营部:1. 为实现极乐天堂而奋斗。2. 利用数据预测和塑造未来。3. 不遗余力地提高生产力。4. 创建非传统型团队。
  • 我选择血细胞来图示“人力资源正在做的工作”旨在强调我们的项目就如人体的循环系统一样无处不在且同样可靠。
  • 大多数公司,包括几年前的谷歌都会向升职的人道贺,但却毫不关注那些没有得到升职机会的人。这是非常愚蠢的行为。只需要一两个小时的时间找出你认为可能会因此沮丧的人,告诉他们如何才能得到持续发展。人们都希望受到这样的待遇。这样做从程序上讲更公正,有助于员工认同流程的开放性和可靠性。这样远比导致某人辞职,失去他们带来的产值,再寻找新人替代,聘用新人,引领新人走上高速通道这个过程对公司更有利。而且,在某人职业生涯非常脆弱的时刻,你这样做是帮助他们理解了发生的事情,利用一个消极事件激发他的动力。要构建这种能力需要花费一些时间,但是不管你所在组织的规模大小,开始这项工作都不是难事。
  • 她完全不知道电子数据表有一项功能可以做计算。我们需要留心所属专业的两端,并针对他们采取相应的行动。她的例子解释了为什么越来越多的公司将非人力资源从业人员安排为人力资源部的主管。
  • 情商高的人通常有更明晰的自我认知,因此也不会那么傲慢。这也使他们更容易转移到新的领域。
  • 教会整个团队一些传统人力资源团队不会学到的技巧,比如使用sQL或r等编程语言,或将员工面试中搜集的定性数据进行编码的方法。
  • 通过三分招聘模型的使用,我们招聘到具备各种能力的人:人力资源专业人员教会我们如何对员工和组织施加影响,识别不同的形态模式;咨询师可以提升我们对商业的理解力以及我们解决问题的水平;分析人员能提高我们所做各项工作的质量。
  • 在人力运营部中将我们所有人团结在一起的最重要因素在于我们共同的愿景,认为工作不必令人痛苦。工作可以令人更高雅、更有活力、更兴奋。这是推动我们努力的原因。

总结

  • 自由的状态是以自由表达为基础的,而自由表达又依靠对信息和真实情况的了解。(自由需要控制在一定的合理范围内?)
  • 从管理的核心角度来讲,权力的动态方向恰与自由背道而驰。
  • 剥夺管理者对员工的控制权。由一组同事、一个委员会或一个特别任命的独立团队做出。
  • 管理者服务于团队。管理者的关注重点不是惩罚或奖励,而是清除路障,鼓励团队。
  • 只有当企业着手给员工更多的自由时,业绩才能提升。
  • 坚信员工都是好的,再就是要有足够的勇气,把员工看成是企业的主人翁,而不是把他们当成机器。
  • 让处于绩效分布底端的人了解真相,但是不要将绩效与薪酬或职业成果直接挂钩,尽可能用一种积极的方式警示并激励他们。数百名经理需要面对自己并非好经理的现实。
  • 有效的管理不是简单粗暴地不允许你犯错,而是预见到可能发生的错误,提供一个合理的标准和方法,在错误发生之前就避免它。
  • 情商高的人通常有更明晰的自我认知,因此也不会那么傲慢。这也使他们更容易转移到新的领域。

管理借鉴

  1. 尽量创造自由的环境;(高自由度)
  2. 不能把员工当作机器;
  3. 会议时不坐在上首座位,避免潜在的等级关系;(除去显示权力和地位的象征符号)
  4. 尽量保持透明;
  5. 放弃一些控制权,就可以为团队创造一次提升的好机会,也给自己节省出更多时间应对新的挑战;
  6. 关注个人成长而不是评分和奖励,以此改善绩效;
  7. 把奖励分配谈话与员工发展谈话分开;
  8. 绩效交谈前发讲义,覆盖恰当的话题,引导讨论;
  9. 打造学习型组织;
最优秀经理手下工作的谷歌人在十几项Googlegeist评估维度上要比最差经理手下工作的谷歌人高5%至18%。除此之外,他们在以下几方面的认可度明显更高:
•职业决策更加公正。绩效评估公正,得到升职的都是实至名归的人选。
•个人的职业目标能够达成,他们的经理是非常有帮助的支持者和引导者。
•工作高效,决策迅速,资源分配合理,从多种视角考虑问题。
•团队成员之间没有等级制度,互相尊重,决策依据数据做出而不是靠耍手段,团队内部各人的工作和信念都保持透明。
•他们适当地参与到决策制定过程中,并且得到一定的授权去完成工作。
•他们可以自由地平衡工作和私人生活。

调查显示高分经理具备八种低分经理所不具备的共性:8个氧气项目特性
1.  做一名好的导师。
2.  给团队授权,不随便插手下属工作。
3.  表达出对团队成员的成功和个人幸福的兴趣和关心。
4.   高效/结果导向型。
5.   善于沟通——聆听和分享信息。
6.   在职业发展方面助力团队。
7.   对团队有清晰的愿景和战略。
8.   具备重要的技术技能,可为团队提供建议。

招聘借鉴

  1. 只聘用比你更优秀的人(至少某些特定的方面比你更优秀的。)
  2. 最优秀的人通常并不在寻找工作;
  3. 让面试者开心;
  4. 谷歌工作法则:筛选新雇员?□设定高质量标准□寻找自己的应聘者□客观评估应聘者□给应聘者一个加入的理由

【计算机科学速成课】笔记

发表于 2020-10-18
  • 【计算机科学速成课】[40集全/精校] - Crash Course Computer Science

Redis笔记

发表于 2020-10-11

脑图

命令

  • info memory 可查看Redis使用了jemalloc
  • Redis Config Get 命令 - 获取指定配置参数的值:https://www.redis.net.cn/order/3667.html
  • cluster nodes

Redis 客户端

  • https://redis.io/clients#java
  • Jedis
  • lettuce
  • Redisson

Jedis

连接方式

  1. Jedis直连
    1
    Jedis jedis = new Jedis("127.0.0.1",6379);
  2. Jedis连接池
    1
    2
    GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
    JedisPool jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379);
  • Jedis是redis的java客户端,JedisCluster则是Jedis根据Redis集群的特性提供的集群客户端。

Jedis客户端如何定位到对应的主节点

  • 你不知道的Redis:RedisCluster与JedisCluster
  • JedisClusterInfoCache:关于缓存数据的获取及更新实际是由JedisClusterInfoCache的discoverClusterNodesAndSlots方法实现, 主要是通过cluster slots 命令获取集群内的槽位分布数据,然后解析该命令的返回结果,为每个主节点初始化一个连接池,然后将节点与连接池、节点负责的所有槽位与连接池的映射关系缓存
  • 初始化主要分为一下几部分:
    1. 连接一个节点执行cluster slots命令,获取槽位分布以及集群节点信息;
    2. 为每一个节点都初始化一个连接池,并跟节点建立映射关系缓存;
    3. 将每个主节点负责的槽位一一与主节点连接池建立映射缓存。
    4. 初始化工作中缓存的映射信息,在JedisCluster的使用过程中起到了至关重要的作用。但也正是因为JedisCluster在本地内存中缓存节点数据并且为每个节点维护一个连接池,在使用节点特别多的庞大集群时,客户端也会消耗更多内存。

Redis基本数据类型原理

  1. 字符串:redis没有直接使用C语言传统的字符串表示,而是自己实现的叫做简单动态字符串SDS的抽象类型。C语言的字符串不记录自身的长度信息,而SDS则保存了长度信息,这样将获取字符串长度的时间由O(N)降低到了O(1),同时可以避免缓冲区溢出和减少修改字符串长度时所需的内存重分配次数。
  2. 链表linkedlist:redis链表是一个双向无环链表结构,很多发布订阅、慢查询、监视器功能都是使用到了链表来实现,每个链表的节点由一个listNode结构来表示,每个节点都有指向前置节点和后置节点的指针,同时表头节点的前置和后置节点都指向NULL。
  3. 字典hashtable:用于保存键值对的抽象数据结构。redis使用hash表作为底层实现,每个字典带有两个hash表,供平时使用和rehash时使用,hash表使用链地址法来解决键冲突,被分配到同一个索引位置的多个键值对会形成一个单向链表,在对hash表进行扩容或者缩容的时候,为了服务的可用性,rehash的过程不是一次性完成的,而是渐进式的。
  4. 跳跃表skiplist:跳跃表是有序集合的底层实现之一,redis中在实现有序集合键和集群节点的内部结构中都是用到了跳跃表。redis跳跃表由zskiplist和zskiplistNode组成,zskiplist用于保存跳跃表信息(表头、表尾节点、长度等),zskiplistNode用于表示表跳跃节点,每个跳跃表的层高都是1-32的随机数,在同一个跳跃表中,多个节点可以包含相同的分值,但是每个节点的成员对象必须是唯一的,节点按照分值大小排序,如果分值相同,则按照成员对象的大小排序。
  5. 整数集合intset:用于保存整数值的集合抽象数据结构,不会出现重复元素,底层实现为数组。
  6. 压缩列表ziplist:压缩列表是为节约内存而开发的顺序性数据结构,他可以包含多个节点,每个节点可以保存一个字节数组或者整数值。

基于这些基础的数据结构,redis封装了自己的对象系统,包含字符串对象string、列表对象list、哈希对象hash、集合对象set、有序集合对象zset,每种对象都用到了至少一种基础的数据结构。

redis通过encoding属性设置对象的编码形式来提升灵活性和效率,基于不同的场景redis会自动做出优化。不同对象的编码如下:

  1. 字符串对象string:int整数、embstr编码的简单动态字符串、raw简单动态字符串
  2. 列表对象list:ziplist、linkedlist
  3. 哈希对象hash:ziplist、hashtable
  4. 集合对象set:intset、hashtable
  5. 有序集合对象zset:ziplist、skiplist

Redis为什么快

redis的速度非常的快,单机的redis就可以支撑每秒10几万的并发,相对于mysql来说,性能是mysql的几十倍。速度快的原因主要有几点:

  1. 完全基于内存操作
  2. C语言实现,优化过的数据结构,基于几种基础的数据结构,redis做了大量的优化,性能极高
  3. 使用单线程,无上下文的切换成本
  4. 基于非阻塞的IO多路复用机制

Redis6.0之后改用多线程

  • redis使用多线程并非是完全摒弃单线程,redis还是使用单线程模型来处理客户端的请求,只是使用多线程来处理数据的读写和协议解析,执行命令还是使用单线程。
  • 这样做的目的是因为redis的性能瓶颈在于网络IO而非CPU,使用多线程能提升IO读写的效率,从而整体提高redis的性能。

主从同步的原理

  1. slave发送sync命令到master
  2. master收到sync之后,执行bgsave,生成RDB全量文件
  3. master把slave的写命令记录到缓存
  4. bgsave执行完毕之后,发送RDB文件到slave,slave执行
  5. master发送缓存中的写命令到slave,slave执行

配置参数

cluster-require-full-coverage

cluster-require-full-coverage= yes

  1. 任一master宕机 集群可用
  2. 同一组master和slave宕机 集群不可用
  3. 半数及以上master宕机 集群不可用

cluster-require-full-coverage= no

  1. 同一组master和slave宕机 集群可用
  2. 半数及以上master宕机 集群不可用

源码系列

  • 全面阐释Redis常见对象类型的底层数据结构

字符串SDS

  • 扩容策略:长度小于1M时每次扩容加倍;长度大于1M后每次扩容加1M

字典

  • 由两个hashtable组成,通常情况下只有一个有值。扩容时搬迁过程中两个都有值;
  • hashtable第一维是数组,第二维是链表;
  • 渐进式rehash:在后续的hset,hdel等指令逐步搬迁,同时有定时任务进行搬迁(单线程,一次性搬迁太耗时)

压缩列表(ziplist)

  • zset和hash在元素较少时使用ziplist存储
  • ziplist是连续的内存空间,元素紧凑存储,支持双向遍历

快速列表(quicklist)

跳跃列表(skiplist)

紧凑列表(listpack)

  • Redis 5.0

基数树(rax)

LFU

  • Least Frequently Used, 按最近的访问频率进行淘汰,比LRU更精准表示一个key的访问热度

工具

  • Redis Memory Analyzer:https://scalegrid.io/blog/the-top-6-free-redis-memory-analysis-tools/, https://stackoverflow.com/questions/49388547/get-the-redis-key-value-size-in-memory

Reference

  • [redis深度历险:核心原理与应用实践]
  • 关于Redis,你扛得住这夺命连环11问吗?
<1…101112…16>

154 日志
165 标签
RSS
© 2025 Kingson Wu
由 Hexo 强力驱动
|
主题 — NexT.Pisces v5.1.4