Categories
程式開發

架构的百年大计


本文最初发表于 Increment,经原作者 Vicki Boykis 授权,InfoQ 中文站翻译并分享。

2012 年,当我第一次加入数据分析团队时,我所在的公司正准备实施 Hadoop,并将所有的数据从 Oracle 移出。团队一名成员将我们召集到会议室,花了一个小时来传授他在为期四天的 Cloudrea 训练营得到的心得经验。

他说:“它叫做 MapReduce,这是一种能够同时处理大量文件的方法。它有一个名称节点和工作节点,这些节点都以并行的方式进行工作。它比那些关系数据库要强大得多了。”

这种由一台中央机器控制的多台机器共同工作的想法,似乎很重要,也很新颖。但在我当时的工作中并没有任何关于 Hadoop 的背景,我的团队成员都和我一样,对分布式计算这块还很陌生。于是,我们花了几个月的时间重新创建了星型模式,这是我们在 Oracle 中费尽心思拼凑起来的,并花了无数个小时试图让 HDFS、Hive 和 Pig 来完成我们之前在关系数据库环境中所做的工作。作为一名分析师,我独自一人,运行 Hive 查询并在 HDFS 中寻找新数据。几个月以来,我一直努力去理解这个新的架构,和我一起同行的只有 Stack Overflow 和 Apache 邮件列表上的那些同样困惑的提问者。

我们并没有有效地利用 Hadoop 的力量。MapReduce 范例的好处是,你可以快速进行大量的计算,但是你需要有少量的大文件,否则,你的并行进程在分布式系统上就会变得效率低下。当你从关系数据库迁移数据时,还会失去一些强大的能力:快速查询以获得答案的能力。优化 Hive 查询(实际上是转换为 Java 方法的 SQL 语法)需要与传统关系 SQL 查询不同的编程范式。但是,要了解这一点,你必须了解 Hadoop 运行的假设条件,以及与之前的关系数据库有何不同。你还需要了解每种方法的用例。

我的团队没有这样的背景。我们努力启动并运行项目,曾经有那么一段时间,我们既没有从 Oracle 那里得到任何好的数据(因为我们专注于将所有数据转移到 Hadoop),也没有从 Hadoop 那里获得任何好的数据(因为彼时我们还没有完全理解它是如何工作的)。但我们并不孤单:在早期,有许多公司,比如 Blackboard,被 Hadoop 的力量所吸引,结果却发现自己仍然原地打转,因为他们无法获取数据,或有效地处理和查询平台上已有的数据。

当时我的团队有两个问题:不仅是我这种新来的技术人员陷入了困境;也有许多公司(包括我所在的公司)正如此迅速地采用 Hadoop,但他们并没有聘请或咨询有相关架构经验的人。

去年,当我和丈夫在无畏号航舰博物馆(Intrepid Museum)参加海军服役仪式时,我还在思考可重复使用的软件模式和师徒制。无畏号航舰博物馆是一个由航空母舰改造的展览场所,停靠在纽约市。在最后一幕中,新晋升的军官向曾协助执行晋升军官候选程序的现役军官回敬了他们的第一个军礼,这些现役军官往往扮演着师傅的角色,协助培训新晋升的军官。

这一行为给我留下了深刻的印象。在科技行业中并没有类似的传统,即在开发人员首次部署到生产环境之前与一名团队高级成员结对。事实上,技术人员在进入这个行业时,并不知道这项工作以前是如何完成的,日新月异的技术变革并没有帮助解决这一痛点。早期工程师可能需要对 Redshift、S3、HDFS 和 Postgres 进行评估,以“疾跑”周期的节奏存储数据,不过,他们并没有意识到,尽管 Redshift 和 Postgres 都有自己特定的用例,但它们都是由相同的关系层次结构驱动的,S3 和 HDFS 共享类似的文件夹式架构,从长远来看,大多数人选择转向关系模型。(尽管这并不能阻止他们遇到长期存在的时态数据库(Temporal database)问题。)

因为很少有人拥有设计和实现有效的软件架构所需的历史和背景知识,所以开发人员必须主动收集这些信息。将架构模式(及其背景)的旋转列表(rotating list)牢记于心,可以避免出现需要返工的情况,并防止出现架构不匹配的现象。例如,MapReduce 架构是通过一种简单的模式运行数据来工作。初始文档被分成多个部分;然后在每个部分上运行一个函数并汇总字数。然后在所有文档中将这些字数加在一起。MapReduce 的开发人员没有发现这种模式;相反,他们关注的是函数式编程中已经可用的 map 和 reduce 方法。2004 年 MapReduce 最初的论文中列出的引文清楚地表明,该模型并非新模型,它是由许多不同平台上数百名学者的工作产生的。(例如,其中引文之一来自 1980 年发表的一篇关于《并行前缀计算》(“parallel prefix computation”)的论文。)map 和 reduce 模式在 Scala、Scheme 和 Clojure 等语言中也以函数的形式存在。2011 年,Hadley Wickham 将其称为“拆分 – 应用 – 合并”(split-apply-combine)模式。Dask 和 Spark 也遵循同样的原则,只是实现方式略有不同。

你甚至可以在不使用分布式系统的情况下应用这种模式。(之前我曾写过关于如何在笔记本电脑上运行 map 和 reduce 模式在编程上同样有效的文章。)例如,当我和以前的团队在努力理解 Hadoop 的时候,我们花费了大量时间,通过实现字数统计(即大数据领域中的经典“Hello,world!”)来了解系统是如何工作的。直到后来,当我阅读 John Bentley 著的《编程珠玑》(Programming Pearls)一书时,我才发现作者早已用 C++ 实现了单词计数程序。单词计数的程序早就存在,并早已使用 FORTRAN 这样古老的语言实现了,但不知怎么的,我却从来没有在 Hadoop 之外考虑过它们。

通常,软件开发人员与系统的第一次接触是非常实际的,有些知识欠缺,需要充电。当当前状态是构建失败,且业务需要新功能时,是没有时间从过去中汲取教训的。 因此,在 2010 年代早期,许多人最终从零开始解决 Hadoop 中的问题,而这些问题在之前的分布式系统的迭代以及 map 和 reduce 的范式中已经得到解决了。

这种机构的经验传承缺失的现象,也导致了 SQL 的反复无常的起伏不止。关系数据库从 1970 年代就已经存在了,但在 2000 年代末 Hadoop 发布的同时,公司就开始尝试 NoSQL 存储和处理的模式。由此产生的产品包括 MongoDB、HDFS 和 Cassandra 等非关系型存储。在这些新的范例中,你可以在没有任何实体映射的情况下存储数据。不需要任何关系或索引,解决方案以快速写入而引以为傲。

在分析师和数据科学家需要统计数据之前,这一切都是不可思议的。想通过编写 MapReduce 脚本来对文件夹和分类日志的文件夹与 SQL 和索引相同的效率进行查询?这几乎是不可能的。在基于日志的文件陷入 HDFS“炼狱”之后,业界再次构建了基于 SQL 的检索引擎:Hadoop 的 Presto、Kafla Stream 的 KSQL 聚合,以及 Cassandra 的 CQL。

在极少数情况下,历史记忆确实会传承下去,其带来的好处是显而易见的。看看前 Python 的终身仁慈独裁者(Benevolent Dictator for Life,BDFL)Guido van Rossum 的经历吧,他在 2012 年接受了 Dropbox 的一份工作。作为 Python 的最初设计者和开发者,van Rossum 不仅可以说是该语言最有经验的人,而且对为什么要进行设计更改也有机构记忆(institutional memory)。在 Dropbox,他鼓励 mypy 的开发,这是一个用于 Python 的类型检查库。

译注:

终身仁慈独裁者(Python Benevolent Dictator for Life,BDFL),是少数开源软件开发者所拥有的头衔。他们通常是某一项目的创始人,并在该项目社区出现争议时拥有最终的决定权。这一称号诞生于 1995 年,最初用来指 Python 语言的创始人 Guido van Rossum。当时在他刚加入美国国家创新研究机构 (Corporation for National Research Initiatives,CNRI)后,Ken Manheimer 在一封邮件中试图创立一个监管 Python 语言开发的半正式团体。他在该邮件中首次使用了 BDFL 这一称呼,并戏称 van Rossum 为 “首任临时 BDFL”。2018 年 7 月 12 日,van Rossum 宣布不再出任 Python 语言的 BDFL。

机构记忆(institutional memory),是“知识管理”领域中的概念,指整个机构内所有员工的总体经验或记忆精华,轻易解雇老员工就会丧失“机构记忆”,因为他们是仅有的知道如何修理旧设备、读懂已经不使用的计算机语言、甚至驾驶飞机(在全球飞行员短缺的情况下)的人。

由于 van Rossum 在 Python 代码库和社区方面的经验,他意识到了大型公司在不进行类型检查的情况下运行 Python 的局限性,因此认识到类型提示是开发人员的需求。于是他聘请了 Jukka Lehtosalo 来承担这一项目,并鼓励构建这个架构使用类型检查,同时也促进了它在 Dropbox 的各个团队中的传播。在 van Rossum 的指导下,Dropbox 已经迁移到 Python 3,这是 Python 语言的最新版本,许多其他大公司都在努力实现迁移到 Python3。(Python 2 已于 2020 年 1 月 1 日停更。)

跨越时间和地点,哪怕是一点点的机构知识也可以为公司节省数百个小时的时间。例如,当一位经验丰富的 Unix 管理员加入我们的团队后,团队学习 Hadoop 的速度大大加快了。师徒制还可以帮助人们成为更有生产力的开发人员,否则他们可能已经离开公司,或在自己的岗位上停滞不前。一些组织开始提供正式的师徒项目,包括 Airbnb、Pinterest 和 Adobe。

在 Hadoop 流行几年后,随着 Spark(同样也基于 MapReduce)和基于云的 ETL 解决方案取而代之,该平台逐渐失去了市场份额。(即使是现在,仍然有一些人希望 Dask 能够支持 Spark。)我们在软件工程领域中也看到了类似的趋势:公司在整体服务和微服务之间,或者在单一的内部部署服务和云端之间穿梭。迭代和增量给劲是技术的自然特性,每个人都希望设备变得更小,代码运行更快。然而,这些技术迭代部分原因也是由于正式的指导实践提供的历史连续性的缺乏。大多人时候,没有人会站出来说:“等等,我们五年前就是这么做的。”如果他们真的这样做了,也许我们会看到技术栈以更合理的速度发生变化,并最终与更稳定的架构一起工作。

作者介绍:

Vicki Boykis,在费城担任顾问,从事端到端机器学习项目。她曾为多家媒体撰稿,并出版了简报《Normcore Tech》。她还是两个孩子的母亲。

关于 Increment:

Increment 是一本关于团队如何大规模构建和操作软件系统的杂志,该杂志拥有印刷版和数字版。

原文链接:

https://increment.com/software-architecture/architecture-for-generations/