Ein gewisses Verständnis von Softwaresystemen

Einführung: Fassen Sie Ihr persönliches Verständnis des Softwareerstellungsprozesses zusammen

Vorwort

Dieser Artikel soll etwas von meinem Verständnis der Systemsoftware ausdrücken.Der Stil ist nicht derselbe wie die vorherigen, und das Ganze ist "Rückzug". Tatsächlich bin ich selbst nicht sehr gut im "Rückzug" und sogar ein wenig abstoßend. Im Vergleich zum Lesen von Aufsätzen lese ich lieber Code.Natürlich lese ich auch Aufsätze, aber relativ weniger.

Ich arbeite seit meinem Abschluss im Bereich Datenbank-Storage-Engines, in den vergangenen fünf Jahren lag mein Hauptaugenmerk auf der Forschung und Entwicklung von X-Engine, der selbst entwickelten LSM-Tree-Storage-Engine von Alibaba zwei Jahren haben wir das Upgrade der Cloud-nativen Architektur und Kommerzialisierung von X-Engine abgeschlossen, die eine bestimmte Anzahl von Kunden in die Public Cloud integriert und stabil läuft, sollte auch die erste TP-Speicher-Engine in der Branche sein, die Cloud-native Fähigkeiten realisiert basierend auf der LSM-Tree-Architektur. Ich habe den gesamten Zyklus der Architekturplanung, des Designs und der Entwicklung, der Implementierung und des stabilen Betriebs und der Wartung einer TP-Speicher-Engine vollständig erlebt und von der exzellenten Technik des hochrangigen Teams, des Technologieführers und der gesamten Teammitglieder profitiert, die ich erlebt habe Seit ich in den Datenbankbereich eingetreten bin, haben die Fähigkeiten und die technische Vision sowie einige meiner eigenen Überlegungen in diesem Prozess einige meiner eigenen Erfahrungen und Erfahrungen in Stufen geformt.

Darüber hinaus habe ich mich mit einigen hervorragenden Architekten und Ingenieuren der Branche ausgetauscht und festgestellt, dass das Verständnis von System Engineering auf große Resonanz stößt, und ich habe auch viel wertvollen Input erhalten.Natürlich gibt es auch einige unterschiedliche Ansichten . Dies ist auch der Hauptgrund, der mich dazu veranlasst hat, diesen Artikel zu schreiben. Ich hoffe, einige meiner eigenen Auffassungen klar zum Ausdruck zu bringen. Diese Ansichten sind keine Mode, geschweige denn Innovation, sondern eher mein eigenes Denken und Erleben.

Perspektiven auf Systemsoftware

Standpunkt 1: Das Wesen von Software ist der Verbrauch von Hardware-Ressourcen. Der Unterschied zwischen unterschiedlicher Software liegt darin, welche Probleme durch Hardware-Ressourcen zur Lösung verbraucht werden und wie der Verbrauch von Hardware-Ressourcen zugeteilt wird. „Abstrakt“ und „Kompromiss“ werden oft beim Design von Softwarearchitekturen erwähnt. Die Essenz der Abstraktion ist „welches Problem zu lösen ist“, und „Kompromiss“ ist eigentlich „wie man Hardware-Ressourcen zuweist“.

Zum Beispiel können die TP-Speicher-Engine und die AP-Speicher-Engine viele verschiedene Orte in Bezug auf die Implementierung auflisten, Zeilenspeicher vs. Spaltenspeicher, sekundärer Index vs. ZoneMap-Index, starke Transaktion vs. schwache Transaktion und so weiter. Diese Unterschiede sind eigentlich Ergebnisse, und die Hauptursachen dafür sind:

1) Die Probleme, die von den beiden gelöst werden, sind unterschiedlich. TP-Szenarien sind hauptsächlich Online-Echtzeitdienste. Die Merkmale dieser Dienste sind, dass die Gesamtdatenmenge relativ gering ist (die Daten, die wirklich online verarbeitet werden müssen, können a viele historische Daten), die Anforderung ist kurz und reibungslos, und die Datenlokalität ist offensichtlich und hoch, Parallelität und geringe Latenz usw., während das gesamte AP-Szenario einen großen Datenumfang, eine hohe Rechendichte und einen hohen Durchsatz aufweist. (welches Problem soll gelöst werden)

2) Die vollständige Transaktionsunterstützung der TP-Engine vereinfacht die Gleichzeitigkeitskontrolle des Geschäfts. Tatsächlich tut die TP-Engine das, was das Geschäftssystem ursprünglich tun muss. Natürlich bedeutet dies, dass die TP-Engine einige Hardwareressourcen dafür verbrauchen muss Dies. Um die Geschwindigkeit der Datenspeicherung zu beschleunigen, verfügt die AP-Engine über eine relativ schwache Transaktionsunterstützung, und dieser Teil der Arbeit wird immer noch vom Geschäftssystem (z. B. ETL) erledigt, sodass hierfür keine Hardwareressourcen verbraucht werden müssen . (So ​​weisen Sie Hardwareressourcen zu)

Standpunkt 2: Die großen Änderungen in der Systemsoftware werden im Wesentlichen durch die Hardwareentwicklung getrieben. Dies entspricht Punkt 1.) Bevor die Theorie auf dem Gebiet der Systemsoftware ins 21. Jahrhundert einzog, wurde in akademischen Kreisen ausgiebig und tiefgehend geforscht. Von den ersten Computern über Mainframes und Minicomputer bis hin zu Heim-PCs und billigen Allzweckservern und jetzt zu Cloud-Computing-IAAS-Diensten folgt die Entwicklung von Systemsoftware im Wesentlichen dieser Richtung. Die erneute Popularität von Systemsoftware wird wesentlich durch die „neue Hardware“ von IAAS getrieben. Die On-Demand-Akquisition des gesamten IAAS hat viele frühere Designs von Systemsoftware im Zusammenhang mit begrenzten physischen Ressourcen gebrochen, weshalb Cloud-native Systemsoftware neue Möglichkeiten eröffnen wird.

Meinung 3: Es gibt kaum eine Systemarchitektur, die eine andere übertrifft. Dies spiegelt die Standpunkte 1) und 2) wider: Hinter verschiedenen Architekturentscheidungen stehen unterschiedliche Kompromisse. Ich höre oft einige Sprüche, Sie lesen dieses Papier und diesen Artikel, ihre Architektur hat kein Problem, und unsere Architektur hat dieses Problem. Meine erste Reaktion, als ich diese Ansichten hörte, war Zweifel, und zwar aus drei Hauptgründen: 1) Die experimentellen Ergebnisse vieler Arbeiten und Artikel können nicht reproduziert werden, was bedeutet, dass seine Schlussfolgerungen sehr wahrscheinlich Probleme haben; 2) In In vielen Fällen wird nur der „Gewinn“-Teil betont und der „Geben“-Teil wird nicht erwähnt. 3) Wie groß die Auswirkungen der Probleme in unserem System sind und ob sie gelöst werden können, um diese zu bestimmen, sind quantitative Daten erforderlich. Leicht beeinflussbar durch die Schlussfolgerungen verschiedener Abhandlungen und Artikel, ist es durchaus möglich, ein unscheinbares System zu erstellen. So wie Menschen, die Kampfkünste praktizieren, die Kampfkünste aller Sekten lernen, ist es leicht, am Ende besessen zu werden.

观点4:条条大路通罗马,最终系统对外呈现的区别,更多的是工程实现的原因,而非架构的原因。不同的系统架构需要解决的大部分问题本质上其实是一样的,并且组成一个系统的零部件都差不多,只是根据需要选择哪些零部件来构建系统。只有躬身入局,真正地去面对问题、分析问题、解决问题,才能认清楚其中的本质,否则很容易变成纸上谈兵。

举个例子:经常有人问我LSM-Tree架构中持续写入数据时,compaction问题对性能影响很大。这个问题我是这么看的,首先LSM-Tree架构上写入吞入优势的其中一个原因是,相比于innodb这种磁盘B+ Tree在写入的时候直接sort on write(page内有序,全局有序),LSM-Tree架构选择将一部分sort转移到sort on compaction、sort on read,本质上是将写入时排序的资源消耗,转移到了compaction或read。刷脏其实是包含两个动作:生成脏页,将脏页刷盘。innodb相当于是在写入的时候生成脏页,在刷脏的时候就是单纯的io操作。而compaction其实是同时做了生成“脏页”和“脏页”刷盘。innodb如果持续写入的话,也会有刷脏来不及时导致影响写入性能的问题。因为innodb刷脏和compaction之所以成为问题,本质上都是因为内存和磁盘写入速度的差异,导致生产者消费者模型失衡。所以innodb的刷脏和LSM-Tree的compaction本质上是相同的问题,只是通过不同的方法来将这个过程对系统的影响降到最低。

系统软件构建的七个面向

接下来的内容,主要是在进行详细设计的时候我认为比较重要的原则。这些原则的道理其实很容易理解,并且“软件工程”这门学科已经研究的很充分,但是实际操作的时候其实是蛮困难的,可能是历史包袱的原因,也有可能是外界环境的原因,需要根据实际情况做出不同的trade-off。值得注意的是,我们做出的trade-off一定是要经过仔细考虑的,而不是草率的,否则很容易出现“有舍没有得”。另外遵守这些原则设计实现出来的系统和不完全遵守这些原则设计实现出来的系统,结果其实是“好和更好的区别”,但是“好多少”这个量在系统做出来之前,其实很难衡量。这七个原则不是独立存在的,而是相辅相成的。

面向场景: 首先我们需要明确要解决什么问题,这是整个系统构建的出发点。one size fit all的系统在过去是不存在的,在未来也不一定存在。系统的完善,必然是要靠不断的迭代来完成的,那么如何迭代本质上就是我们在那些阶段解决哪些问题。一个系统可以有远大的目标去解决很多问题,但是所有问题的路标需要有相对清晰的规划,以达到既可以快速满足需求,同时保留向未来演进和扩展的基础。

实际研发过程中,可能发生的两类错误是:1)想采用敏捷开发的方式来进行工程管理,以满足整个迭代的需求。敏捷开发本质上先定义最小功能集,也就是首先想清楚解决什么问题,然后快速的迭代扩充功能,有点像小步快走。在实操上,很容易把敏捷开发搞成了"快、糙、猛",有点大干30天赶英超美的味道。2)问题定义不清楚,系统的“不变式”设置就容易草率。每个系统都有一些“不变式”,随后很多设计都是基于这些不变式进行展开的,比如在LSM-Tree系统中一个常见的“不变式”是更新版本的数据在更低的层次,同一行的数据的多个版本如果同时在memtable、level0、level1中存在,那么必然memtable中对应的版本是最新的,level0中的版本也比level1中的更新。如果在迭代的过程中发现之前设置的“不变式”不合理的,那么进行改动的代价是非常之大的。

面向解耦:无论是自上而下的去设计系统,还是自下而上的去设计系统,很重要的一个思考逻辑就是将各个模块间的耦合度降到最低。解耦做地比较好的系统,往往意味着:1)每个模块的功能是考虑的比较清楚,方案的完整度是比较高的;2)有利于专注的将某个模块实现的更加高效,避免其他模块的影响;3)有利于之后的迭代,影响面可控;4)出了问题好排查,单个模块的问题是比较好排查,真正那些难搞的问题往往是问题在各个模块间传导后才暴露出来,比如A模块出问题,经过模块B、C、D,最后在模块E暴露出来。

有些质疑的观点会说,面向解耦的思路去设计,有可能会牺牲系统的整体性能。其实这个跟不要一开始就为性能做过度的设计是一样的道理,真到了某些解耦的设计影响了性能,那么该耦合的就去耦合。把两个模块耦合在一起的难度往往是低于把耦合在一起的两个模块拆开。

面向防御:这个就是防御性编程的逻辑,要假设调用的函数都是有可能出错的, ,比如内存分配可能出错,io可能出错,基础库的调用可能出错等等,基于此来考虑如果出错,系统的行为是什么。有一个非常简单的原则就是"fail stop", 如果没有完整的防御,那么即使fail了也很难立即stop,最终造成一些很奇怪的表象。

通常的质疑是:1)你看这个函数的逻辑肯定不会失败的。也许从当前来看这个函数确实不会失败,但是很难保证随着迭代增加逻辑,之后没有失败的可能性。2)加了这么多防御,防御代码比实际逻辑的代码还多,会影响性能。首先,现在cpu的分支预测能力,基本上可以做到绝大部分情况下防御代码不会影响性能。另外跟对于面向耦合的质疑一样,真到某些防御代码成为了性能瓶颈,该优化就优化。优化一个防御,总比去解决一个因为没有防御而导致的问题代价更低吧。

面向测试:在测试阶段修复问题的代价是远低于在生产环境修复问题的代价,因此让系统变得可测试是非常重要的。系统可测试的标准就是,能方便的进行单元测试、集成测试,并覆盖绝大部分的代码路径。可测试的系统,随着不断的迭代,会累积越来越多的测试case,不断的夯实稳定性基础。面向测试跟面向解耦、面向防御是相辅相成的。只有模块间耦合度足够的低,才有可能做更多的测试,否则做一个模块的测试需要mock很多乱七八糟的东西。面向防御会使得测试的行为可以更好的预期,不然输入了一个异常的参数,具体怎么失败是不确定的,那测试case就很难写了。

面向运维:bug是一定会有的,对于复杂的系统,不管前期做多少准备都很难避免生产环境中遇到未知的问题。面向运维的主要目的是,遇到问题的时候,能用代价最低的手段去及时止损。遇到线上问题,动态调参数就能解决比需要重启才能解决的代价更低,重启能解决比需要发版才能解决的代价更低。面向运维不仅仅是加几个参数,加几个开关那么简单,而是需要把“面向运维”作为设计方案的重要组成部分来考虑,保证出了问题有运维手段,有运维手段敢用,用了以后有效果。

面向问题本质:当去解决一个问题的时候,一定要多思考这个问题的本质原因是什么,简单的问题复杂化和复杂的问题简单化,都是因为没有抓住本质。如果能思考清楚其背后的本质原因,从源头避免掉是更加彻底的解决方式,否则很容易陷入不断打补丁的状态,我一直有个观点:“没有抓住问题本质去解决问题,结果往往是在制造问题”。另外一个经验是,如果一个模块连续出了好几次问题,那么就要想想是不是在最开始的设计上就有需要改进的地方。

面向可视化:可视化的目标主要是以更加直观的形式,来展现系统运行状况,这对于系统调优和诊断是非常重要的。当系统异常时,可视化的方式可以帮助快速定位到系统哪里出了问题。另外一方面是,可以提供接口给监控系统做历史状态的追踪。比如oracle的诊断监控就是一个非常优秀的案例,而SnowFlake对于内部状态的打点监控也是近乎疯狂。

总结

说了这么多,最终系统还是靠一行行的code实现出来的,保持匠心、严谨、较真的态度去打造系统是非常朴素正确,但又很难做到的事情,共勉!

原文链接:click.aliyun.com/m/100034986…

本文为阿里云原创内容,未经允许不得转载。

Guess you like

Origin juejin.im/post/7123114349681442830