坦白说也是机缘巧合,在硕士生阶段进入分布式系统领域学习。无论是大规模存储或计算,其核心也是运用分布式技术利用并行性来解决数据密集型应用的需求。最近开始在啃这本《Designing Data-Intensive Applications》大部头,作者Martin Kleppmann在分布式数据系统领域有着很深的功底,并在这本书中完整的梳理各类纷繁复杂设计背后的技术逻辑,不同架构之间的妥协与超越,很值得开发人员与架构设计者阅读。 很可惜的是国内目前并没有对应的中文版本,这个系列算是一个读书感悟,同时也夹带私货,阐述一些自己的理解与看法,抛砖引玉,希望大家多交流学习。这本书共有12个章节,接下来我会一个章节更新一篇读书笔记。(囧rz,感觉自己又开了一个坑)同时也希望国内的出版社可以尽快引入版权,我也想要参与翻译工作啊(,,• ₃ •,,) !!
作为一个开发者来说,目前绝大多数应用程序都是数据密集型的,而不是计算密集型的。CPU的计算能力不再成为这些应用程序的限制因素,而更加亟待解决的问题是海量的数据、数据结构之间的复杂性,应用的性能。
先看看我们经常打交道的数据系统:
而很多时候,我们所谓应用程序的绝大工作就是将这些数据系统进行组合,然后添加我们的运行逻辑,但是如何更加合理的整合这些数据系统,对我们来说仍然是一个值得学习和思考的问题。而数据系统也在慢慢变得越来越相似,不同的数据系统也在各自学习彼此的优点。如Redis这样的缓存系统可以支持数据落地,很多时候的应用场合我们可以替代传统的RDBMS。而Kafka这样的数据队列也可以支持数据落地来存储消息。更加深刻的理解这些数据系统,来更好的权衡架构设计,是一门很精深的课题。
结合多个数据系统的应用
上图是一个典型的由多种数据系统构成的应用程序,随着数据量和数据逻辑的复杂,就成为了一个数据密集型的应用。
显然,这三个原则不单单是数据密集型应用应当遵循的原则,在绝大多数软件系统中同样是很重要的问题,接下来我们一一梳理一下。
即使一个系统今天工作可靠,但这并不意味着它将来一定会可靠地工作。一个常见原因是负载增加:也许系统已经从10000个并发用户发展到100000个并发用户,或者从100万个增加到1000万个。
“如果系统以特定的方式增长,我们应对增长的选择是什么?” “我们怎样才能增加计算资源来处理额外的负载?”
所以我们需要有描述性能的尺子:
负载情况与性能情况是很重要的,有时系统的瓶颈是由少数极端情况引起的。作者举了一个Twitter的例子,我觉得很好,这里详细分享一下这个例子:
Twitter在2012年11月16日公布的数据。 Twitter的两个主要操作是:
Twitter在扩展性的挑战主要不是由于Tweet的数量,而主要是在每个用户都有很多订阅者,每个用户也有很多关注者。执行这两种操作大致是两种方法:
SELECT tweets.*, users.* FROM tweets
JOIN users ON tweets.sender_id = users.id JOIN follows ON follows.followee_id = users.id
WHERE follows.follower_id = current_user
如下图所示:
关系型数据库的实现格式
Twitter的数据管道,用于发送消息给订阅者
如上图所示的结构显然更合适Tweet的发布,因为发布的Tweet的写操作几乎比读的操作低两个数量级,所以在这种情况下,最好是在写时做更多的工作,而不是在读时做更多的工作。但是方法2并不适用于有大量关注者的账号,假设某人有3000W粉丝,一次发布Tweet产生的写操作可能是巨大的。所以目前在Twitter的Tweet系统中,Twitter将这两种方法混合。大多数用户的推文在发布时仍然会被扩展到Tweet缓存之中,但只有少数用户拥有大量的关注者(即名人)。用户可以跟踪的任何名人的Tweet,并单独读取并与用户的Tweet缓存中进行合并。这种混合方法能够始终如一地提供良好的性能。
(这个例子很精炼的描述了架构设计的妥协与精妙,依据业务特点,最大化的优化了数据系统的性能。很佩服Twitter的工程师在架构设计上的功力。同时也很好奇如微博,微信是不是也是采用类似的架构进行设计。)
这部分教导了一些构建可维护系统的方法。软件的大部分成本不是在最初的开发中,而是在持续的维护中修复bug、保持系统运行、使其适应新业务、添加新特性。
(维护别人留下的烂摊子真的是很痛苦的事情,文档,注释真的是重中之重!!!)