数据结构而非算法是编程的核心 - theartofmachinery


这是2006年Linus Torvalds的一句话

我是围绕数据设计代码的一个巨大的支持者,我认为这是git相当成功的原因之一......事实上,我会声称一个糟糕的程序员和一个好的程序员区别:他是否认为他的代码或他的数据结构更重要。糟糕的程序员担心代码。优秀的程序员担心数据结构及其关系。

这听起来很像埃里克雷蒙德2003年的“代表规则”

将知识折叠成数据,因此程序逻辑可以是愚蠢和健壮的。

这就是他对1989年Rob Pike这个想法的总结:

数据占主导地位。如果您选择了正确的数据结构并组织好了,那么算法几乎总是不言自明的。数据结构而非算法是编程的核心。

从1975年开始引用弗雷德布鲁克斯

表达(Representation )是编程的本质
有时,战略突破将是一种新的算法,例如Cooley-Tukey快速傅里叶变换成对n 2组的n log n排序。
更常见的是,战略突破将来自重做数据或数据表的表示。这是您的计划的核心所在。告诉我你的流程图并隐藏你的数据表,我将继续被神秘化误导。告诉我你的数据表,我通常不需要你的流程图; 因为他们会很明显。

因此,聪明的人在近半个世纪里一次又一次地这样说:首先关注数据。但有时感觉就像每个人忘记的最着名的智能编程建议。
让我举一些实例:

无法实现的高度可扩展系统
有一个系统从一开始就设计为处理CPU密集型负载,具有令人难以置信的可扩展性。没有什么是同步的。一切都是通过回调,任务队列和工作池来完成的。
但是存在两个问题:第一个问题是“CPU密集型负载”却不是真正CPU密集型的 : 一个任务在最坏的情况下耗费了几毫秒。所以大部分架构都弊大于利。
第二个问题是虽然它听起来像一个高度可扩展的分布式系统,但它不是一个 - 它只能在一台机器上运行。为什么?因为异步组件之间的所有通信都是使用本地文件系统上的文件完成的,这现在是任何扩展的瓶颈。
除了以“简单”的名义提倡本地文件之外,原始设计根本没有说明数据。大部分文档都是关于处理负载“CPU密集度”“显然”所需的所有额外架构。

面向服务的体系结构仍然是面向数据的
这种系统遵循微服务设计,由具有REST风格API的单用途应用程序组成。一个组件是存储文档的数据库(基本上是对标准表单和其他电子文书工作的响应)。当然它暴露了用于基本存储和检索的API,但很快就需要更复杂的搜索功能。设计人员认为将此搜索功能添加到现有文档API将违背微服务设计的原则。他们可以将“搜索”称为“获取/放置”的不同类型的服务,架构上不应该将它们结合在一起。此外,他们计划用于搜索索引的工具与数据库本身是分开的,因此创建新服务也对实现有意义。
最后,创建了一个包含搜索索引的搜索API,该索引基本上是主数据库中数据的副本。此数据正在动态更新,因此通过主数据库API改变文档数据的任何组件都必须更新搜索API。没有竞争条件的REST API不可能做到这一点,所以无论如何,这两组数据时不时地不同步。
尽管承诺了架构图,但两个API通过其数据依赖性紧密耦合。后来人们认识到搜索索引应该是统一文档服务的实现细节,这使得系统更易于维护。在数据层面“做一件事”,而不只是在动词层面。(banq注:如何在数据层面实现唯一职责?通过DDD等其他方法都是可借鉴的方式,DDD模型是一种对数据的组织处理方式)

奇妙的模块化和可配置的泥球
这种系统是一种自动化部署管道。原始设计人员希望制作一个足够灵活的工具来解决整个公司的部署问题。它被编写为一组可插拔组件,配置文件系统不仅配置了组件,还充当了DSL,用于编程组件如何适应管道。
快进几年,它变成了“那..那个程序”了:有一长串已知的错误,没有人曾经修复过。因为害怕破坏那个程序的一切而不敢接触那个程序的代码。没有人使用DSL的任何灵活性。使用该程序的每个人都复制粘贴了其他人使用的相同的已知工作配置。
出了什么问题?尽管原始设计文档使用了诸如“模块化”,“解耦”,“可扩展”和“可配置”之类的词语,但它从未说过任何有关数据的内容。因此,组件之间的数据依赖性最终使用全局共享的JSON blob以临时方式处理。随着时间的推移,组件对JSON blob中或不存在的内容做出了越来越多的无证假设。当然,DSL允许将组件重新排列成任何顺序,但大多数配置都不起作用。

教训
我选择了这三个例子,因为它们很容易解释。
显然很多人仍然认为我写这篇文章是为了取笑他人。实际上和我一起工作的人会知道我对我正在修理的东西更感兴趣,而不是责怪那些完成大部分工作的人,但是,好吧,这就是我对工程师的看法。

“是否谈论数据产生的问题?”对于良好的系统设计来说,这是一个非常有用的试金石。

它对于检测虚假专家建议也非常方便。困难,凌乱的系统设计问题是数据问题,所以错误的专家喜欢忽略它们。他们会向你展示一个非常漂亮的架构,但不谈论它适合哪种数据,以及(至关重要的)它不是什么类型的数据。

例如,假专家可能会告诉您应该使用pub / sub系统,因为pub / sub系统是松散耦合的,而松散耦合的组件更易于维护。这听起来不错,会产生漂亮的图表,但它却是倒退的想法。发布/订阅不会使您的组件松散耦合; 发布/订阅 的松散耦合,这可能会或可能不会匹配您的数据需求。

另一方面,精心设计的面向数据的架构有很长的路要走。函数式编程,服务网格,RPC,设计模式,事件循环等等都有它们的优点,但我个人看到像枯燥的旧数据库这样的工具 负责更成功的软件运送。

(banq注:作者没有看到函数式编程 设计模式等都是处理数据的方式,数据很重要,我们不能忽视,一旦我们重视了,就需要有一套解决复杂数据的方式,这是作者没有谈到的,会让人产生数据表设计很重要错觉,其实数据表很重要,但是处理设计出数据表的方式更重要,这也是DDD等模式范式的根本!)