Eingehende Betrachtung einiger grundlegender Fragen des Schemamanagements

Der Autor dieses Artikels: Wang Dalong, leitender Ingenieur im Bereich Datenanalyse, Meister aller Datenstürme in Guanyuan-Produkten, spiritueller Führer der Metadatenwelt und ewiger Wächter der Datenverwaltung.

Vorwort

Ich habe festgestellt, dass der beste Weg, ein bestimmtes „Ding“ zu verstehen, darin besteht, zunächst das „Paradigma“ dahinter zu verstehen. Ein Paradigma ist eine bestimmte Vorgehensweise in einem Bereich, eine bestimmte Methodik oder eine Zusammenfassung einer Fallstrickerfahrung. In einem Bereich kann es mehrere Paradigmen geben. In verschiedenen Szenarien gibt es Vor- und Nachteile zwischen den Paradigmen. Ein Paradigma kann populär werden und schließlich zu einem „Verhaltensstandard“ werden, was normalerweise bedeutet, dass es vor dem aktuellen „Ära-Hintergrund“ akzeptiert wird. . Bei vielen Überprüfungen wird das ursprüngliche, populäre Paradigma natürlich im Laufe der Zeit (möglicherweise aufgrund einiger Infrastrukturentwicklungen) obsolet (vom frühen ETL bis zum aktuellen ELT ist ein typisches Beispiel).

Bei Programmiersprachen gibt es mehrere grundlegende Probleme , denen man sich nicht entziehen kann :

  • Typmodell

  • Programmierparadigma (prozedural, funktional, objektorientiert usw.)

  • Wie man mit Sprache interagiert

  • Die Urteilsstruktur und die Kerndatenstruktur der Sprache

  • Markante Kernfunktionen

Die Vertrautheit mit diesen Themen erfasst gewissermaßen auch die Essenz von Programmiersprachen. Wenn Sie jetzt den Entwurf einer neuen Sprache leiten, wissen Sie zumindest, welche grundlegenden Fragen zu berücksichtigen sind. Auf dieser Grundlage können Sie das Zielszenario festlegen eine wettbewerbsfähigere Umsetzung.

Als halber Mönch im Bereich BI habe ich in meiner täglichen Arbeit oft das Gefühl, „die Bäume, aber nicht den Wald zu sehen“. Wenn ich beispielsweise für „eine bestimmte Art von häufigen Problemen“ optimieren möchte, Ich finde, dass meine Denkweise „Mangel an System“ ist. Was mich verwirrt, ist Folgendes:

  • Zuerst wusste ich nicht, dass das Problem, auf das ich oft stoße, tatsächlich ein domänenspezifisches Problem ist und dass es nicht einfach ist

  • Welches Feld entspricht „einer bestimmten Art von Problem“? Ist das gesamte Team auf derselben Seite?

  • Welche grundlegenden Fragen sind in diesem Bereich zu berücksichtigen ?

Am besten ist es, einen erfahrenen „Vorgänger“ zu haben, der diese Fragen gut schildern kann, sonst geht die tägliche Arbeit schnell am Thema vorbei. Vor einiger Zeit empfahlen Kollegen um mich herum nacheinander „Designing Clould Data Platforms“ . Nachdem ich einige Kapitel gelesen hatte, hatte ich das Gefühl, einen Schritt gefunden zu haben, um loszulegen.

Meiner Meinung nach beschreibt dieses Buch das Designparadigma der Cloud-native-Architektur. Die im Leseprozess erwähnten Konzepte sind mir nicht fremd, aber es verbindet auch alle Probleme, denen ich täglich begegne. Der Autor forderte uns beispielsweise auf, eine Cloud-native Datenanalyseplattform zu entwerfen. Welche grundlegenden Probleme müssen berücksichtigt werden?

  • Aufnahme von Daten aus RDBMS, Dateien, SAAS ...

  • Daten organisieren und verarbeiten

  • Architektur der Metadatenschicht

  • Schemaverwaltung

  • Datenzugriff und Sicherheit

  • Beobachtbarkeit

  • ......

Und was sind die grundlegenden Aspekte, die bei der Schemaverwaltung, die ich Ihnen als Nächstes vorstellen möchte – einer wichtigen Unterdomäne der Datenanalyseplattform – berücksichtigt werden müssen? (Gleichzeitig wird dringend empfohlen , über „Sprechen Sie über die Cloud Native Data Platform“ zu sprechen . Dies kann Ihnen beim Erstellen eines vollständigen Entwurfs helfen.)

Warum müssen Sie Schema verwalten?

Schema bezieht sich hier auf Feldmetainformationen, hauptsächlich einschließlich Feldname, Feldtyp, Feldreihenfolge, Feldkommentar usw.

In einem unabhängigen System gibt es kein Schemaverwaltungsproblem. In einem allgemeinen Websystem ist es beispielsweise eine Frage des Systems selbst, egal wie sich die Schemainformationen ändern.

Und wenn es eine „wiederholte Datenmigration“-Beziehung zwischen Upstream- und Downstream-Systemen gibt und die Downstream-Systeme empfindlich auf Schemata reagieren, treten Probleme bei der Schemaverwaltung auf. Was ist die Bedeutung? Beginnen wir mit einem Beispiel für „Data Warehouse (im Folgenden als DW bezeichnet) beim Laden von Dateidaten“.

Wenn DW Daten lädt, werden die Daten immer zuerst in die Landing-Tabelle von DW geladen.

Die Rolle der Landing-Tabelle in DW besteht darin, neue aus der Datenquelle extrahierte Daten zu speichern, und ihre Schemainformationen imitieren direkt die Schemainformationen der Datenquelle.

当DW完成第一次加载时,两边的schema信息将保持一致。此时如果修改数据源transaction_amount字段为transaction_total,就像这样:

那么数据加载就会失败,数据工程师此时就要开始介入并维护landing table了。这种工作模式看起来很低效对不对?

后来,随着Hadoop兴起,开始出现“schema on read”的概念。相对于DW的“schema on write”模式,Hadoop所基于的文件系统HDFS在数据写入阶段并不关心其schema信息。

schema-on-write:需要先明确schema信息,创建表,才能开始写入数据。典型代表Mysql,DW等。
schema-on-read:数据写入阶段无需关注schema信息,它就是数据拷贝的过程,只有在读取数据的时候才会开始关注schema信息。典型代表HDFS。

现在假设我们用HDFS来替换DW,看看情况有没有变好?

这次,我们把下游的ETL逻辑也加进来。ETL pipeline在运行时本质上是生成一段sql(本书所描述的clould data platform底层基于spark,所以生成的是sparkSQL),sql会引用具体的字段名称。

同样的,我们去修改数据源的字段名称,你会发现,HDFS这一层在加载新数据时并不会出错,但是最终ETL运行出错了,原因是transaction_amount字段不存在。

现在看来,具备“schema on read”机制的存储的确可以减轻数据工程师的部分工作(至少不用维护data landing的过程),但并没有真正解决schema变更所导致的问题,只不过把问题往后推了一步。

事实上,在实际应用中不同公司面对这种情况处理的方式不一样。有些大公司会在schema改变发生的当下主动提交“change request”,目的是尽可能避免或者减轻下游系统的错误,整个过程会谨慎规划,花几个星期甚至几个月的时间来完成这件事情一点也不奇怪。而在一些体量比较小的公司,他们有另外一套策略,那就是啥也不干,直到下游ETL出错,然后让数据工程师自行修改,这当然会导致很不好的用户体验。

不管怎么样,我们需要意识到,schema的变更管理在数据分析领域是不可忽视的问题。并且以上所述都是一种「手动管理」的方式,我们接下来要开始探索更聪明的做法。

Schema的管理思路

我的理解,schema的管理思路可以简单概括为「共享」和「拷贝」两种。

「共享」这种方式,也就是作者所说的schema as a contract,是一种中心化管理思路。

我们想象有这么一个Schema Registry仓库(里面存储了所有数据源的schema信息),上游数据源在每次schema变更时,都主动推送到Schema Registry,而下游数据消费者每次需要的时候来Schema Registry引用最新版本的schema。

这种做法有一些好处,比如:

  • 上下游职责边界清晰

  • 比较容易扩展新的数据消费者

  • 字段只需要维护一份就好(但这只能向后兼容数据,关于兼容问题下文会继续说明)

但想实现这种思路,有一个前提,就是数据源和数据消费者,两拨研发团队需要高度协同,简直就要像一个团队一样开发。这是一个几乎无法实现的方案,除非涉及的所有数据源都是公司内部自研系统。

剩下就是基于「拷贝」的方式了,即数据在上下游系统转移的过程中,schema信息是不断被复制的,比如数据从数据源到DW过程中,schema信息就在DW的landing table中复制了一份。

和「共享」方式相比,最大的区别在于「管理schema的职责」完全转移到了数据分析平台,而数据生产者,也就是使用数据分析平台的用户,不需要去关心这些细节。这也是接下来Schema-management实现思路的基调。

实现Schema-management module

概要

在这一小节中,我将和大家分享数据分析平台需要关注的几个基本问题

  • 数据进入平台时如何获取数据源的schema信息?(主要包括字段名称,字段类型)

字段名称容易获取,但对于像CSV,JSON这种格式的数据,我们怎么拿到数据类型?

  • 基于「拷贝」的Schema Registry的设计问题

Schema Registry是schema的仓库,需要存哪些信息?大致需要哪些接口?

  • 数据源schema变更时,如何保证平台common transformation过程中的兼容问题

关于common transformation下文会讨论

  • 数据源schema变更时,如何自动管理下游数以千计的custom transformation

关于custom transformation下文会讨论

  • 数据源schema变更时,如何自动级联变更下游其他存储的schema

数据源的数据经过ETL的处理之后,最终又被存到DW供数据消费者分析,而DW的schema如何级联变更呢?

  • 数据源schema变更时,有哪些问题是必须要用户参与手动维护的

程序不是银弹,我们需要理解哪些情况是无法被自动化的,然后思考方案如何用最优雅的方式让客户参与维护?

一个现代的云原生数据分析平台,肯定不会如此简单。

给大家展示一个被简化了的云原生数据分析平台架构,一起看下它的大致流程是怎样的?

当数据进入平台时大体上会经过三个步骤的处理:

  • 第一步,数据抽取以及data landing的过程

  • 第二步,common data transformation

  • 第三步,custom data transformation

custom data transformation指的是诸如ETL,reports等等pipeline

我们进一步细化上述第二步,什么是common data transformation?它大概负责哪些工作?

数据源数据在平台landing之后,需要做通用的转换处理:

  • Data format conversion module

数据源的格式各种各样,比如CSV,JSON,XML,甚至还有二进制数据,一个很直接的问题是后续的analytics pipelines该怎么基于这些格式构建呢?这中间需要做一层抽象和解耦,该模块的工作就是统一数据格式。而在实际应用中,我们会结合使用avro和parquet两种格式。

  • Deduplication module

这是一个比较大的话题,本书主要指重复数据清理,感兴趣的还可以了解一下MDM tools

  • Data quality checks module

按照用户的规则对数据源数据质量做检查,保证拿到的是“干净”的数据

现在,我们要新加入一个环节:Schema-management module,它需要做的事情是,检查数据源的schema信息是否已经存在Schema Registry中:

  • 如果不存在:

  • 推测新数据的schema信息

  • 将该schema信息注册到Schema Registry中,并将版本号设置为1

  • 如果存在:

  • 获取Registry中的schema信息

  • 推测新数据的schema信息

  • 对比上述两个schema信息,并以「向后兼容」的方式做combine操作(关于兼容问题下文会讨论)

  • 将最终结果以一个新的版本注册到Schema Registry中

值得注意的是,第二步和第三步都会和Schema Registry有交互,这也就意味着schema变更会影响到这两个步骤,在后面会逐步展开讨论。

Schema信息获取——字段推测

在Schema-management module中第一个基本问题是「需要有方案知道数据源的schema信息(主要包括名称和类型)」。对于RDBMS类型的数据源,schema信息是很容易获取的。但对于像CSV或者JSON这样的数据源,则需要通过「字段推测」的方式来获取。

怎么做字段推测呢?幸运的是,咱们的平台底层使用Spark作为计算框架来处理各种数据转换,spark自带一个强大的功能叫schema inference。它的大概原理是读取数据的前1000行,然后自动解析出字段的类型信息,这在解析CSV文件或者高度嵌套的JSON有非常好的表现。我们通过一个JSON生成工具得到以下数据:

使用spark shell可以快速验证「字段推测」功能。

值得注意的是,以上spark的推测结果,其实使用的是spark内部自带的类型系统,我们当然可以把这当成最终结果,但考虑到我们设计的是一个能广泛兼容的数据平台,所以我们会考虑将spark推测出来的schema信息转换成Avro Schema再存入我们的Registry当中。

上文提到在Common data transformation中有一个环节是Data format conversion,其目的是要统一数据源的数据格式,这可以给下游的Custom data transformation提供一个统一的抽象层,使得代码耦合度大幅降低。同样的,我们也希望在「数据类型」这件事情上能做到统一,广泛兼容。而Avro Schema是非常合适的选择,它支持非常多通用的原生数据类型:strings,integers, float,null等等,同时也支持复杂类型,比如records,arrays,enums等等。

下面简单展示Spark schema和Avro schema的转换方式。

Schema Registry的设计

拿到了schema信息之后,我们需要考虑的第二个基本问题是Schema Registry应该怎么设计?包括需要存哪些信息?需要提供什么接口?相信这个难不倒大家。

基本结构就是DB+API layer,我们先看下Schema Registry和其他模块的交互大概是怎样的?

  • 在数据landing的过程中,Ingestion pipelines会往Registry中增加或者更新数据

  • 而下游的transformation pipelines(如ETL)在构建过程中首先需要读取schema信息,其次,它最终的输出也是一个新的数据源,自然也会往Registry中增加数据

  • 监控工具也会周期性的检查schema的version,并给用户以提醒

此处监控schema变更的目的是什么?后面会详细讨论。

梳理清楚需求之后,大致也知道需要哪些API:

  • 根据数据源获取当前的schema版本

  • 增加新版本的schema数据

  • 更新schema基本信息

而表字段的设计可以像这样(帮助大家理解,并不一定是最终实现):

  • ID

  • Version

  • Schema

  • Created Timestamp

  • Last Updated Timestamp

值得一提的是,为什么我们要为schema记录历史版本呢?有一个直接好处是我们知道一个数据源的schema信息的历史变化情况,这对debug以及troubleshooting是有非常大的好处的。而在我们即将要讨论的兼容问题中,你会发现版本信息的另一个好处。

Schema变更场景

我们已经知道怎么获取schema信息,也知道怎么存储这些信息。是时候开始讨论schema的变更场景了。

首先考虑一个简单的问题,数据源schema可能有哪些关键变化呢?

  • 增加一个字段

  • 删除一个字段

  • 重命名字段

  • 修改字段类型

其次回忆一下,数据源schema的变更对哪些环节有影响?

我们拆解一下流程,重新理解各个环节所做的工作以及和schema的基本关系。

第一步,数据源的数据经过Ingestion layer不断写入平台,就像这样:

数据源schema的变化,对Ingestion layer并没有什么影响,它总是以最新的版本(也就是数据源当前的schema)写入数据。所以随着时间的推移,对于同一个数据源,在平台中可能存在部分老数据是用schema V1写的,部分新数据是用schema V2写的。

第二步,Common transformation pipelines,该环节需要做几个工作(除Schema management以外):

  • Data format conversion module

  • Deduplication module

  • Data quality checks module

简单理解,它需要对Ingestion layer写入的数据进行二次处理。

第三步,Custom transformation pipelines,该环节用户会自定义ETL数据处理逻辑,而ETL最终会输出一个新的数据源

有没有发现,第二步和第三步都涉及到对已有数据的读操作。既然如此,我们就不难想到会出现以下几种情况:

  • 用新版本Schema,读取新数据(肯定不会有问题)

  • 用新版本Schema,读取老数据(会出问题吗?)

  • 用老版本Schema,读取新数据(会出问题吗?)

  • 用老版本Schema,读取老数据(肯定不会有问题)

对于第一种和第四种,肯定不会有问题,那么对于中间两种呢?这里需要引入两个概念:向后兼容与向前兼容

当我们说某schema变更「向后兼容」时,它指的是,data transformation pipelines(不管是Common还是Custom)用最新版本的schema可以正常读取老数据(用老版本的schema写入的数据)。

当我们说某schema变更「向前兼容」时,它指的是,data transformation pipelines用老版本的schema可以正常读取新数据(用新版本的schema写入的数据)。

铺垫的差不多了,最后,当我们考虑「schema变更」所产生的影响时,一定要牢记一个蓝图,即在schema变更时,我们的终极目标不仅仅要保证Common transformation环节能正常读取数据,还要保证下游成百上千的ETL pipelines以及reports(仪表板),能跟着一起变更并且正常运行(下游的Custom transformation同样会依赖数据源的字段),这样可以极大的提高用户的使用体验以及效率。

Schema变更对Common Transformation Pipelines的影响

我们从Common transformation开始谈起,讨论一下「用新版本Schema,读取老数据」以及「用老版本Schema,读取新数据」分别会发生什么?

在下面的例子中,有一个单一数据源已经完成了一轮数据抽取,使用schema V1往平台写入了数据。此时数据源增加了一个字段column_3,并且通过Ingestion layer写入了新的数据。

如果Common transformation pipelines用schema V2去读取老数据会怎么样?

Avro格式定义了几种处理规则,使得schema变更可以向后兼容。在这个例子中,Avro使用schema V2读取老数据时会自动为column_3字段设置一个默认值,通常默认值是一个empty或者“null”值,当然也可以设置和字段类型相匹配的默认值,所以「增加一个字段」对Avro来说是向后兼容的

我们继续,现在假设Common transformation pipelines因为某种原因,没有立马切换新版本,而是用schema V1去读取新数据,会发生什么?

Avro会直接忽略新加的字段,当前的Common transformation piplines不会有任何问题,piplines可以在晚些时候再切换到新版本的schema。所以「删除一个字段」对Avro来说是向前兼容的。

虽然咱们的pipleline可以允许schema版本延迟切换,但我们并不建议这么干,因为用户大概率是希望能尽快看到新的字段。及时同步数据源schema变更总是好的,这会让用户感觉到咱们的数据平台是非常在意这件事情的。这也是后面我们要讨论的「监控Schema变更」的原因之一。

我们已经讨论了增加列和删除列的兼容性,那么重命名兼容性呢?相信你也想到了,其实重命名就等于删除列+增加列,对Avro来说,如果该列有默认值,那么重命名操作是前后兼容的,否则,就是前后都不兼容。

最后一种操作是修改字段类型。Avro支持promoting字段类型,保证数据不会丢失。比如Avro可以把int扩展成long,float,和double类型。你可以在该文档中了解到更多promote信息。

Schema变更对Custom Transformation Pipelines的影响

custom transformation和common transformation最大的区别在于前者开始加入了业务逻辑,而且数量上会变的很多(对于一个中大型的客户来说ETL或者reports数量往往是数以千记的),进而还会引出更多管理问题。

但在兼容性问题上两者没有本质的区别。我们还是用类似的例子说明,数据源删除了column_2,增加了column_3(也可以说是column_2重命名成了column_3)。

同样的,我们考虑几种情况,custom transformation使用老版本schema能读取新数据吗?这取决于当初创建column_2的时候有没有设置默认值,如果有,那么没问题。使用新版本schema能读取老数据吗?这同样取决于创建column_3时有设置默认值吗?如果有,那么也没问题。

我们发现,使用avro的一个最佳实践是「设置合适的默认值」,这样会最大程度上保证数据的兼容性。下面的表格详细地说明了schema变更和兼容性的关系。

下游存储的Schema级联变更

custom transformation pipeline的输出,比如ETL,可能会作为一个新的数据源,写入到DW,所以上游schema变更的时候,DW的schema怎么修改?

这个过程就需要我们自己写一些代码了,基本的思路是基于scehma management模块,根据schema的历史变更,生成对应的Alter table的sql语句。

比如我们删除字段column_2,增加了字段column_3,我们最终会生成一个类似Alter table some_table add column column_3这样的sql去DW中执行。那为什么仅增加了column_3,没有删除column_2的操作呢?因为DW包含很多有价值的历史数据,通常我们不会做删除操作。

当然还有一种更粗暴的方式,就是每次schema变更的时候,直接重建DW的表,即删除原表,然后根据新的schema重新加载所有历史数据,但这仅适用于数据量比较小的场景。

需要注意的是,很多DW在修改schema的过程中是无法查询的,所以我们要权衡修改schema的时间,否则对基于DW的报表服务将产生很大影响。

监控Schema变更

终于,我们把schema变更对common transformation,custom transformation以及下游DW的影响和对应的解决方案都讨论了一遍,我们尽全力降低了因为schema变更给用户带来的影响——保持各种data transformation pipeline正常运行状态。但我们仍然需要有一个通知机制去告诉用户字段的修改情况,这不仅仅是因为我们无法100%规避因数据不兼容导致的pipeline运行报错(比如找不到某字段),哪怕pipeline本身没报错,其最终计算的结果也可能是错的。

还是这个熟悉的例子,对于这样一个数据源,我们删除了daily_sales字段,增加了total_day_sales字段。因为daily_sales的默认值是null(设置默认值是一个很好的习惯),那么当前的pipeline是向后兼容的,它能正常运行,但结果呢?这显然不是客户想看到的数据。

对于这种问题没有更好的自动化解决方案,我们需要思考的是,怎么优雅的通知客户哪些报表可能已经出错,并让客户以最方便的方式去review和调整各种pipeline逻辑。

现有的catalog项目实现

到目前为止,我们算是对Schema management这一领域问题有了整体的了解,而在该领域有哪些现成的产品呢?

  • aws clue data catalog

  • azure data catalog

  • google clould data catalog

我给大家列了一些,之所以没有在数据分析平台直接使用这些产品,是因为它们有这样那样不符合预期的地方。但作为开拓视野还是值得大家去了解的,尤其是最后一款产品,它的功能和我们本文描述的schema management非常接近,如果我们要进一步深入学习,可以看看它是怎么做的。

写在最后

假如这篇文章可以给大家带来一些价值,我希望它能帮助大家意识到该领域问题的存在,并构建对它的整体认知。日后工作中遇到该领域的问题时,眼光不再局限在一个个点状的jira task,而是能清晰的知道该问题发生在什么环节,能在一个领域体系内思考问题的原因,以及优化方案,甚至还能触类旁通,找到该领域内其他优秀产品扩展自己的思路。

在写这篇文章的过程中,原书《Designing Clould Data Platforms》的schema management章节(包括相关联的章节)已经被我反复读过很多遍。和原文相比,我几乎重新组织编排了内容,用我所理解的“循序渐进”的方式重新表达,这并不是说原文“逻辑混乱”,恰恰相反,哪怕像我这样的英文渣渣也毫无阅读障碍,只不过这是“充分理解”过程中不得不做的事情。另外,为了让大家在阅读过程中避免不必要的认知负载,我适当地做了一些知识屏蔽,如果对于一些概念仍然有疑惑,还是建议大家亲自看看这本书。

最后,如果本文有任何错误的观点都与作者无关,请在后台留言告诉我,大家一起成长。

Supongo que te gusta

Origin blog.csdn.net/GUANDATA_/article/details/128665102
Recomendado
Clasificación