最近花了两个多月的时间,终于把《数据密集型应用系统设计》(Designing Data-Intensive Applications,简称DDIA)这本书读完了。这本书是后端开发和分布式系统领域的经典之作,被很多人称为"后端必读书",我早就听说过它的大名,但是一直因为它太厚、太硬核,没有勇气开始读。这次终于下定决心,花了两个多月,每天读一点,终于把它读完了。读完之后,最大的感受就是:这本书真的太值了,它彻底改变了我对分布式系统和数据存储的认识,很多之前一知半解的概念,读完之后都豁然开朗了。今天就来写写这本书的读书笔记,聊聊我读完之后的收获,以及我对分布式系统的新的认识。

一、这本书讲了什么

在说收获之前,先简单介绍一下这本书,让没读过的朋友有个大概的了解。

《数据密集型应用系统设计》的作者是Martin Kleppmann,他是分布式系统领域的专家,曾经在LinkedIn和Confluent工作过,参与过Apache Samza等开源项目,有丰富的分布式系统实战经验。这本书是他多年经验和思考的总结,2017年出版,一出版就引起了轰动,被翻译成多种语言,成为了后端开发和分布式系统领域的经典教材。

这本书的核心主题是"数据密集型应用",也就是那些数据是主要挑战的应用,数据量大、数据复杂、数据变化快,比如社交网络、电商平台、搜索引擎、数据分析系统等等。这本书从最基础的数据模型和查询语言开始,一直讲到分布式系统的一致性、共识、事务等高级话题,内容非常全面,也非常深入。

全书分为三个部分:

第一部分:数据系统的基础。这一部分讲的是单机数据系统的基础,包括数据模型和查询语言(关系模型、文档模型、图模型)、存储和检索(哈希索引、LSM树、B树、列存储)、编码和演化(JSON、XML、二进制编码、模式演化)。这一部分是基础,讲的是数据在单机上是怎么存储和查询的,很多数据库的底层原理都在这里。

第二部分:分布式数据。这一部分是全书的重点,讲的是分布式系统的核心问题,包括复制(主从复制、多主复制、无主复制、复制延迟、一致性)、分区(分片、再平衡、路由)、事务(ACID、隔离级别、分布式事务、共识算法)、分布式系统的麻烦(网络延迟、时钟、进程暂停、拜占庭将军问题)。这一部分讲的是数据在多台机器上怎么分布、怎么同步、怎么保证一致性,是分布式系统最核心也最难的部分。

第三部分:衍生数据。这一部分讲的是从原始数据衍生出其他数据的系统,包括批处理(MapReduce、Hadoop、数据仓库)、流处理(消息队列、事件流、流处理框架)、批处理和流处理的统一(Lambda架构、Kappa架构、未来的发展方向)。这一部分讲的是数据处理的两种模式,以及怎么把它们结合起来,构建完整的数据系统。

这本书的特点是,不局限于某一个具体的技术或者产品,而是讲底层的原理和思想,讲各种技术的优缺点和适用场景,让你知其然更知其所以然。而且书中举了很多实际的例子,对比了很多主流的数据库和系统,比如MySQL、PostgreSQL、MongoDB、Cassandra、Redis、Kafka、Hadoop等等,让你能把理论和实际结合起来。

二、我最大的收获:对分布式系统的新认识

读完这本书,我最大的收获就是,对分布式系统有了全新的认识,很多之前一知半解甚至误解的概念,读完之后都豁然开朗了。这里分享几个对我触动最大的点。

1. 没有完美的分布式系统,只有权衡和取舍

之前我总觉得,分布式系统应该是完美的,数据应该是强一致的,系统应该是高可用的,性能应该是很高的,最好什么都能做到最好。但是读完这本书我才明白,分布式系统里没有完美的方案,所有的设计都是权衡和取舍。

最经典的就是CAP定理,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance),三者不可兼得,在分布式系统中,分区容错是必须的,所以只能在一致性和可用性之间做选择。你要强一致性,就要牺牲一部分可用性,网络分区的时候系统可能不可用;你要高可用性,就要牺牲一部分一致性,数据可能会有短暂的不一致。没有哪个系统能同时做到完美的一致性和完美的可用性,只能根据业务场景选择合适的平衡点。

除了CAP,还有很多其他的权衡,比如延迟和吞吐量的权衡,存储成本和查询性能的权衡,复杂度和可维护性的权衡等等。分布式系统的设计,本质上就是在各种矛盾之间做权衡,找到最适合业务场景的平衡点,而不是追求什么都最好。

这个认识对我影响很大,之前做技术选型的时候,总想着找一个"最好的"技术,什么都能满足,现在明白了,没有最好的技术,只有最合适的技术,关键是看你的业务场景更看重什么,然后做合理的取舍。

2. 一致性是一个很复杂的概念,不是非黑即白的

之前我对一致性的理解很简单,觉得一致性就是数据在任何时候都是一样的,要么一致,要么不一致,是非黑即白的。但是读完这本书我才明白,一致性是一个非常复杂的概念,有很多不同的级别,不是非黑即白的。

书里讲了很多一致性模型,比如强一致性、线性一致性、顺序一致性、因果一致性、最终一致性、读己之写、单调读、一致前缀读等等,每一种一致性模型都有不同的保证,适用于不同的场景。强一致性是最强的,但是成本最高,可用性最差;最终一致性是最弱的,但是成本最低,可用性最高。中间还有很多中间状态,比如因果一致性,保证有因果关系的操作是有序的,没有因果关系的操作可以乱序,这是一个很实用的中间状态。

而且,即使是同一个系统,不同的操作也可以有不同的一致性级别,比如重要的操作用强一致性,不重要的操作用最终一致性,这样既能保证关键数据的一致性,又能兼顾性能和可用性。很多系统都是这么做的,比如Amazon的Dynamo,就是典型的最终一致性系统,但是它也支持强一致性的读,你可以根据需要选择。

这个认识让我明白,不要一谈分布式系统就说"数据不一致怎么办",一致性是可以分级的,要根据业务场景选择合适的一致性级别,不是所有数据都需要强一致性,也不是所有数据都能接受最终一致性。理解了一致性的各种级别,才能做出合理的设计。

3. 时钟是分布式系统里最大的坑之一

之前我对分布式系统里的时钟问题没有太在意,觉得服务器的时间都是同步的,应该没什么问题。但是读完这本书我才明白,时钟是分布式系统里最大的坑之一,很多分布式系统的难题,本质上都是时钟的问题。

首先,物理时钟是不可靠的。每台服务器都有自己的物理时钟,虽然有NTP时间同步,但是NTP同步是有误差的,网络延迟、时钟漂移、闰秒等等,都会导致不同服务器的时间不一致,可能差几毫秒,甚至差几秒。如果你在分布式系统里依赖物理时钟来判断事件的先后顺序,那就会出问题,因为你以为A事件比B事件早,但是实际上B事件可能更早,只是那台服务器的时钟慢了。

然后,逻辑时钟虽然能解决事件排序的问题,但是它不能反映真实的时间,也不能用来做超时、过期等需要真实时间的判断。而且逻辑时钟的实现也很复杂,需要在所有节点之间传递时钟信息,增加了系统的复杂度。

书里讲了很多因为时钟问题导致的分布式系统难题,比如最后写入获胜(LWW)的冲突解决策略,因为时钟不同步,可能会导致旧数据覆盖新数据;比如分布式锁,如果你用物理时钟来判断锁是否过期,时钟不同步可能会导致两个客户端同时持有锁;比如全局快照,因为时钟不同步,很难拿到一个全局一致的快照。

这个认识让我明白,在分布式系统里,不要轻易依赖物理时钟,凡是需要判断事件先后顺序的地方,都要小心时钟不同步的问题,尽量用逻辑时钟或者版本号来代替物理时钟。如果必须用物理时钟,一定要考虑时钟误差,留足够的余量,并且做好容错。

4. 事务不是银弹,分布式事务更难

之前我对事务的理解也很简单,觉得事务就是ACID,能保证数据的一致性,有了事务就不用担心数据不一致的问题了。但是读完这本书我才明白,事务不是银弹,单机事务已经很复杂了,分布式事务更是难上加难。

首先,单机事务的ACID也不是那么简单的,特别是隔离级别,读已提交、可重复读、串行化,每个隔离级别都有不同的问题,比如脏读、不可重复读、幻读,很多人对这些概念都一知半解。而且即使是最高的串行化隔离级别,也不能解决所有的并发问题,比如写偏斜(write skew),串行化隔离级别才能解决,但是很多数据库的默认隔离级别都不是串行化。

然后,分布式事务就更难了。分布式事务需要在多台机器上保证原子性,要么都成功,要么都失败,这就需要用到两阶段提交(2PC)、三阶段提交(3PC)、或者Paxos、Raft等共识算法。但是2PC有阻塞问题,协调者挂了可能会导致所有参与者都阻塞;3PC解决了一部分阻塞问题,但是更复杂,而且在网络分区的情况下还是可能有问题;Paxos和Raft能解决共识问题,但是实现非常复杂,性能也不高。

而且,分布式事务的性能很差,因为需要多轮网络交互,还要等待所有节点都确认,吞吐量和延迟都不如单机事务。所以很多分布式系统都尽量避免分布式事务,用最终一致性、事件溯源、Saga模式等方式来代替,虽然不能保证强一致性,但是性能更好,可用性更高。

这个认识让我明白,不要一谈数据一致性就说"用事务啊",事务不是万能的,特别是分布式事务,成本很高,能不用就不用,尽量用其他方式来保证业务上的一致性。如果必须用分布式事务,一定要清楚它的代价和局限性。

5. 批处理和流处理正在融合,未来是统一的

之前我对批处理和流处理的理解是,它们是两种完全不同的处理模式,批处理是处理历史数据的,流处理是处理实时数据的,各有各的场景,各有各的技术栈。但是读完这本书我才明白,批处理和流处理本质上是一样的,只是数据的边界不同,它们正在融合,未来会是统一的。

批处理的本质是,处理有界的数据集,数据是有限的,有开始有结束,处理完就结束了。流处理的本质是,处理无界的数据流,数据是无限的,一直在来,处理永远不会结束。但是从另一个角度看,批处理其实是流处理的一个特例,就是数据流在某个时间点结束了,所以批处理可以用流处理的方式来做。反过来,流处理也可以看成是一系列连续的小批处理,每次处理一小批数据。

所以,批处理和流处理是可以统一的,用同一套技术栈、同一套API,既能处理有界的批数据,也能处理无界的流数据。这就是现在很火的流批一体的概念,比如Apache Flink就是流批一体的计算引擎,既能做流处理,也能做批处理,用的是同一套API。

书里还讲了Lambda架构和Kappa架构,Lambda架构是批处理和流处理各做一套,批处理层处理历史数据保证准确性,速度层处理实时数据保证低延迟,然后合并结果;Kappa架构是只用流处理,把历史数据也当成流来重放,用一套系统搞定。Lambda架构复杂,但是更可靠;Kappa架构简单,但是对流处理系统的要求更高。现在随着流处理技术的发展,Kappa架构越来越流行,流批一体也成为了趋势。

这个认识让我明白,不要把批处理和流处理割裂开来,它们本质上是相通的,未来会越来越融合。学习数据处理技术的时候,不要只学一种,要理解它们的本质,这样才能跟上技术的发展。

三、这本书对我的影响和改变

读完这本书,不仅仅是学到了很多知识,更重要的是,它改变了我的思维方式,改变了我看待系统和技术的角度。

首先,我变得更有"原理思维"了。之前学习技术,更多的是学习怎么用,怎么调API,怎么配置,但是读完这本书之后,我更关注技术背后的原理,为什么这么设计,解决了什么问题,有什么优缺点,和其他技术有什么区别。理解了原理之后,就不会被具体的技术绑定,不管技术怎么变,原理都是相通的,能快速掌握新技术。

其次,我变得更有"权衡思维"了。之前做技术选型和系统设计,总想着找最优解,什么都想要最好的,但是读完这本书之后,我明白了没有完美的方案,所有的设计都是权衡和取舍,关键是看业务场景更看重什么,然后做出合理的选择。这种思维方式让我在做设计的时候更理性,不会盲目追求新技术,也不会人云亦云。

第三,我对分布式系统不再那么畏惧了。之前总觉得分布式系统很高深,很复杂,不敢碰,但是读完这本书之后,我发现分布式系统虽然复杂,但是它的核心问题是清晰的,就是复制、分区、一致性、事务这几个核心问题,每个问题都有成熟的解决方案和理论基础。理解了这些核心问题,分布式系统就不再那么神秘和可怕了,虽然实现起来还是很难,但是至少知道该怎么思考,怎么入手了。

第四,我意识到了基础的重要性。这本书里讲的很多东西,都是很基础的,比如数据结构(B树、LSM树)、算法(共识算法)、网络(TCP、延迟)、操作系统(进程、线程、文件系统),这些基础的东西,决定了上层系统的设计和性能。之前我总觉得这些基础东西没用,想直接学高大上的技术,但是读完这本书之后,我明白了基础才是最重要的,基础打牢了,上层的技术才能学得深、用得好。

四、给想读这本书的朋友的建议

最后,给想读这本书的朋友几点建议:

第一,不要被厚度吓到。这本书确实很厚,有六百多页,而且很硬核,但是它写得很好,逻辑清晰,循序渐进,只要你有一定的后端基础,慢慢读,是能读懂的。不要追求快,每天读一点,花两三个月读完,很正常,重要的是读懂,而不是读快。

第二,要有一定的基础再读。这本书不是入门书,需要你有一定的后端开发基础,了解基本的数据库、网络、操作系统概念,不然读起来会很吃力。如果你是刚入门的后端,建议先看看其他入门书,有了一定基础之后再来读这本,收获会更大。

第三,不要纠结于具体的技术细节。这本书里提到了很多数据库和系统,每个都有各自的细节,不用去记每个系统的具体实现,重点是理解背后的原理和思想,理解各种技术的优缺点和适用场景。具体的技术会过时,但是原理和思想是不会过时的。

第四,读完之后要结合实际思考。这本书讲的是原理和思想,读完之后,要结合你自己的工作和项目去思考,你现在用的技术是怎么实现的,有什么权衡,遇到的问题可以用书中的什么思路来解决。这样才能把书里的知识变成自己的,真正消化吸收。

五、写在最后

《数据密集型应用系统设计》读完,我对分布式系统有了新的认识。

总的来说,《数据密集型应用系统设计》是一本非常值得读的书,它是我这几年读过的最好的技术书之一,读完之后收获非常大,不仅学到了很多知识,更重要的是改变了我的思维方式,让我对分布式系统和数据存储有了全新的认识。

如果你是做后端开发的,或者对分布式系统、数据存储感兴趣,我强烈推荐你读一读这本书,它可能不会让你马上涨工资,也不会让你马上成为技术大牛,但是它会让你对整个后端技术体系有一个更清晰、更深刻的认识,会让你在技术的道路上走得更远。

技术是学无止境的,特别是在这个技术快速发展的时代,新的技术、新的框架层出不穷,但是万变不离其宗,底层的原理和思想是不会变的。把基础打牢,理解了原理,就能以不变应万变,不管技术怎么发展,都能快速掌握,从容应对。

最后用一句话结尾:"读书是最好的投资,特别是读经典的好书。"愿我们都能多读书,读好书,不断学习,不断进步,在技术的道路上越走越远。