元数据系统设计理论与实践

本文首发微信公众号:码上观世界

在大数据架构中,从数据生产、加工到数据消费,每个环节都涉及到元数据的共享和交换,比如数据库,数据表,表结构、存储格式、ETL任务配置、运行记录、操作日志等,由于这些信息分布在不同的系统,既有OLTP系统,又有OLAP系统,这些系统之间并没有统一的企业标准,导致IT在进行系统集成时面临较大挑战,甚至用户自己也无法寻找需要的数据,并对数据的来源、含义、质量、可信度等给出解释。

3c9fdeeb81089cc0705b128a3355b5c5.png

为此,需要一套公共的元数据平台,提供统一的视角进行元数据管理。目前元数据的架构,一般分为集中式架构和分散式架构。集中式的架构,指的是采集多种数据源的元数据到公共元数据系统中存储。分散式的架构,没有自己的元数据存储,而是在使用的时候,即时查询其他数据源的元数据。

这两种架构各有利弊:

  • 集中式的架构,可以快速的检索元数据,抽取的时候,可以自由的转换,自定义补充,提升了元数据的质量;同时也有缺点,需要保证自身存储和其他源数据的一致性,增加了流程复杂度和工作量。

  • 分散式架构的优点是,元数据总能够保持最新,查询更加的简单;缺点也很明显,无法自定义或修改元数据项,查询也受源系统可用性的影响。

元数据系统设计涉及到元数据建模、元数据存储、元数据应用等方面,本文关注最底层核心的部分-元模型设计,同时介绍元数据获取经历的几个阶段所采用的的实现方式。

01

元模型理论

如果采用集中式架构,则首先解决元数据建模问题。元数据建模通过统一的标准和规范解决通用性和扩展性问题,其理论来源于OMG(Object Management Group )定义的元模型规范,OMG将元数据从数据对象开始逐层抽象建模,依次为:

  • 数据(Data):初始数据、物理世界中存在的原始数据,记为M0;

  • 元数据(Metadata):定义数据的数据,也称为Model,记为M1;

  • 元-元数据(Meta-metadata ):定义元数据的数据,也称为Metamodel,记为M2;

  • 元-元-元数据(Meta-metametadata ):定义元模型数据,也称为Meta-Metamodel,记为M3。

各层次的示例用表格列举下:

Meta-Level

Modeling Level

Examples

M3

Meta-Metamodel/Meta-metametadata

MOF Class, Attribute, Association,Package, Operation

M2

Metamodel/Meta-metadata

UML Class, Attribute

CWM Table, Column

M1

Model/Metadata

Product : TableProduct

Type : Column

M0

Data/Object

"Toaster""Television""Stereo"

其中M3、M2属于语言规范层,M3层的元模型是为定义下层的模型,该层只有一种元模型,即MOF。MOF(Meta Object Facility,元对象设施)标准是一个模型驱动的分布式对象框架,是OMG关于元模型和元数据库的标准;MOF为构建模型和元模型提供了可扩展的框架,并提供了存取元数据的程序接口。由于M3层具有自描述性,可以递归定义自身,所以往上就没有必要再加层次了。

M2层相对M3更为具体,但仍然属于抽象层,用于定义模型层,该层包含多个元模型:UML、CWM(Common Warehouse Metamodel)。CWM规范基于UML(Unified Modeling Language)、MOF(Meta Object Facility)和XMI(XML Metadata Interchange) 这三个核心标准实现了对元数据的定义(设计)、交换(交 互)、存储、存取(操作)等,为元数据管理提供全面能力支持。

M1层属于用户规范层,用于定义数据层。数据层的数据可通过M1层动态构建。上述各模型组成了如下的层次结构:

97e7d4c5f0da5530f44bc902875e17a1.png

上述模型架构理解起来非常困难,需要从底层数据逐层抽象,相当于在脑海中逐层挤压多余的水分,层次越高,水分越少,最上层都是干货,从上层到下层理解的过程相当于不断加水稀释。在实践中,这篇文章中,我也不打算详细介绍,感兴趣的同学可以参考以下内容:

MOF规范、案例的详细参考:

https://essay.utwente.nl/57286/1/scriptie_Overbeek.pdf

CWM 缘由、规范、设计基础参考:

https://cwmforum.org/cwm.pdf

元模型理论给实践提供了参考来源,实践中也不一定完全照搬实现,接下来我从几个实践案例来介绍相关的元模型设计。


02

Hive Metastore 元模型设计

Hive Metastore 大家比较熟悉,它通过规范化的关系对象定义元数据的存储方式,比如库、表、列、参数、序列化等信息,在物理层模型中,我们可以很直观的看到这些信息,比如下面列举的一些信息:

表名称

表描述

CTLGS

Catalog数据表

DBS

DataBase数据表

TBLS

Table数据表

COLUMNS_V2

存储字段信息,通过CD_ID与其他表关联

DATABASE_PARAMS

DataBase参数数据表

TABLE_PARAMS

Table参数数据表

SDS

存储输入输出format等信息,包括表的format和分区的format。关联字段CD_ID,SERDE_ID

SD_PARAMS

SDS参数数据表

SERDES

存储序列化反序列化使用的类

SERDE_PARAMS

序列化反序列化相关信息,通过SERDE_ID关联

这些表通过ER图,也能够直观的看到它们的引用关联关系,比如以HMS2.x版本为例来看:

d9b9fca1d3edb327be87f93e39815a20.png

上述信息大致对应了元模型理论中的M2元模型在物理层的实现。

03

Atlas 元模型设计

类型系统是Atlas中的核心系统之一,是一个提供给用户定义和管理类型与实体的组件。所有Atlas中可管理的、立即可用的元数据对象都是由特定类型模型下的实体开描述的。

在Atlas中“类型”是对一个特定类别元数据对象如何存储和访问的定义。一个类型描述了一项属性或一组属性集合,而这些属性定义了元数据对象所包含的内容。有开发背景的用户可将类型类比理解为面向对象编程语言中的类,或者是关系型数据库中的表模式(table schema)。

下面是在Atlas中定义的一个名为Hive表的类型,该类型包含了以下的属性:

Name:         hive_table
TypeCategory: Entity
SuperTypes:   DataSet
Attributes:
    name:             string
    db:               hive_db
    owner:            string
    createTime:       date
    lastAccessTime:   date
    comment:          string
    retention:        int
    sd:               hive_storagedesc
    partitionKeys:    array<hive_column>
    aliases:          array<string>
    columns:          array<hive_column>
    parameters:       map<string,string>
    viewOriginalText: string
    viewExpandedText: string
    tableType:        string
    temporary:        boolean

上述示例的关键点:

  • Atlas中每一个类型都通过唯一的名字来标识。

  • 每一个类型都有一个元类型(metatype)。Atlas有如下的元类型:

    1.基本元类型:boolean, byte, short, int, long, float, double, biginteger,     bigdecimal, string, date

      2.枚举元类型(Enum metatypes)

      3.集合元类型:array, map

      4.复合元类型:Entity, Struct, Classification, Relationship

  • Entity & Classification可以继承其他类型,被继承的类型称为supertype。这样做的好处是,类型可以继承获得超类型的属性。建模者可以将一些公共属性定义在超类型中。比如示例中hive表就是继承自DataSet。

  • 具有‘Entity’, ‘Struct’, ‘Classification’ or 'Relationship'元类型的类型可拥有一个属性集合。其中每一项属性都有一个名词及与之对应的取值。属性可通过表达式type_name.attribute_name来引用。比如hive_table.name是String,hive_table.aliases是String数组,hive_table.db引用了hive_db类型的一个实例 

Atlas中的一个“实体”是一个实体类型的特殊值或实例,也表征了真实世界中的元数据对象。类比面向对象编程语言,一个实例是特定类的一个对象。

一个Hive表的实例就是一个实体。假设在“default”数据库中有个名为“customers”的hive表。该表就是hive表类型的一个实体。

guid:     "9ba387dd-fa76-429c-b791-ffc338d3c91f"
typeName: "hive_table"
status:   "ACTIVE"
values:
    name:             “customers”
    db:               { "guid": "b42c6cfc-c1e7-42fd-a9e6-890e0adf33bc", "typeName": "hive_db" }
    owner:            “admin”
    createTime:       1490761686029
    updateTime:       1516298102877
    comment:          null
    retention:        0
    sd:               { "guid": "ff58025f-6854-4195-9f75-3a3058dd8dcf", "typeName": "hive_storagedesc" }
    partitionKeys:    null
    aliases:          null
    columns:          [ { "guid": ""65e2204f-6a23-4130-934a-9679af6a211f", "typeName": "hive_column" }, { "guid": ""d726de70-faca-46fb-9c99-cf04f6b579a6", "typeName": "hive_column" }, ...]
    parameters:       { "transient_lastDdlTime": "1466403208"}
    viewOriginalText: null
    viewExpandedText: null
    tableType:        “MANAGED_TABLE”
    temporary:        false

以上示例中的要点:

  • 每一个实体类型的实例都有一个唯一标识,即GUID。该GUID是在(元数据)对象定义时由Atlas服务生成,并且在该对象的生命周期内以常量方式保存。任何时刻,我们都能用GUID来访问这个对象。

  • 一个实体实例的values是一个map,该map的key为对应类型中定义属性的名称,value为属性的取值。

  • 属性的取值必须与类型中定义的属性类型保持一致。实体类型(Entity-type)的属性拥有一个AtlasObjectId类型的取值。

实体(Entity)与结构(Struct)元类型都是由其他类型属性组合而成。然而,实例类型的实例拥有一个标识(GUID值),可以被其他实例引用(比如hive_db实体引用hive_table实体)。结构类型没有标识。结构类型的值是所有属性的集合。

而属性拥有以下的内容:

name:        string,
    typeName:    string,
    isOptional:  boolean,
    isIndexable: boolean,
    isUnique:    boolean,
    cardinality: enum
  • name - 属性的名称。

  • typeName - 属性的元类型名称。

  • isComposite - 这个标识了建模的考虑。如果一个属性被标记为复合的(composite),那么就表明这个属性不能脱离包含它的实体而存在。比如一个hive表的字段不能脱离表存在。一个复合属性必须在Atlas与包含的实体一起创建。

  • isIndexable - 标识是否在该属性上建立了索引

  • isUnique - 是否时唯一索引。任何标记设置为真的属性,都可以作为区分实体的主键。

  • multiplicity - 值是该属性是必须的,可选的,多值的其中之一。

如果isOptional=true,那么创建表实体时必须可以引用到db实体

db:
    "name":        "db",
    "typeName":    "hive_db",
    "isOptional":  false,
    "isIndexable": true,
    "isUnique":    false,
    "cardinality": "SINGLE"

注意到列属性定义中的ownedRef约束。这使得我们在定义列实体必须与对应的表实体绑定起来。

columns:
    "name":        "columns",
    "typeName":    "array<hive_column>",
    "isOptional":  optional,
    "isIndexable": true,
    “isUnique":    false,
    "constraints": [ { "type": "ownedRef" } ]

上面通过示例介绍了Entity类型,除此之外,Atlas还有以下重要的类型,包括:

  • Referenceable:该类型表示,它的所有实体都可以通过叫做qualifiedName的唯一属性查询到。

  • Asset: 该类型扩展自Referenceable,并增加了诸如name,description和owner等属性。name是必选属性,其他为可选属性。提供Referenceable和Asset的目的是,让建模者在定义和查询他们自定义类型时能具备强一致性。具有这些固定的属性集,能让应用和用户接口开发时有这样的属性假设,即默认这些属性已经才能在。

  • Infrastructure:该类型扩展自Asset,一般可作为基础上设施元数据对象(如集群,主机等)的公用超类型。

  • DataSet:该类型扩展自Referenceable。概念上,它用于描述一种存储数据的类型。在Atlas中,hive表,hbase表等都可以从DataSet扩展而来。从DataSet类型扩展而来的类型都将拥有一个模式(schema),即有一个属性定义了该数据集的列(字段或属性),比如hive表中columes字段。所有DataSet扩展类型的实体都会参与数据转换(transformation),而这种转换会通过Atlas的血缘图记录下来。

  • Process:该类型扩展自Asset。概念上,它用于描述任何数据转换的操作。例如,一个ETL处理过程可以是Process扩展类型的实例,而该ETL处理过程可将原始数据转换成存储到一个存放聚合数据的hive表中。Process类型有两个特别的属性,输入和输出。输入和输出都是DataSet实例数组类型的。因此,一个Process类型的实例可用输入和输出属性来展示数据的血缘关系。

关于Atlas的更多内容参考:

https://atlas.apache.org/#/TypeSystem

04

DataHub 元模型设计

DataHub 元模型基于以下几个抽象来实现:

Entities:实体。比如用户、图表Chart、Dataset、Dashboard、DataJob等,实体有自己的唯一标识符,实体通过唯一标识符关联一系列属性信息;

Aspects:描述实体的部分属性。切面可以独立更新,它是DataHub写入的最小原子单位。切面可以跨多个实体存在,比如在多个有”ownership“属性的实体上定义名叫 Ownership的切面,那么该切面就可以被这多个实体重用。

Relationships:实体跟实体之间的关系。类似数据库的外键引用。关系允许双向遍历。

Identifiers(Keys & Urns):查询实体的标识符。其中Keys是一种特殊的切面,Keys可以序列化到字符串形式的Urns,Urns 也可以转换回Keys,而Urns是一种对用户查询实体更友好的方式。

通过一个示例来看DataHub 元模型,比如下图中包含 3 个实体:CorpUser、Char、Dashboard, 2 个关系:OwnedBy、Contains,3 个元数据切面:Ownership、ChartInfo、DashboardInfo。

aa59c607fd96cea2f281ca82d9a0c196.png

关于DataHub的更多内容可参考:

https://datahubproject.io/docs/metadata-modeling/metadata-model/

05

元数据采集

元数据的采集方式,从发展过程划分为3代。

1. G1 基于Pull的方式。

此时的元数据服务很简单,只需要一个存储元数据的数据库以及搜素引擎即可。其元数据来源于ETL过程,ETL从数据源,如关系数据库,非结构存储的文件等中获取元数据。

1fef6c90784a182c3c35ddef8e7b4043.png

这种方式实现起来简洁直观,不需要多太多事情。但是缺点很明显:

  • 容易影响在线系统;

  • 外部数据源的响应能力与可靠性无法保证;

  • 元数据新鲜度和系统负载不好权衡。

2. G2基于Push的方式

为避免G1中Pull的方式的缺点,G2在原来存储的基础上,增加元服务,提供元数据存取的API,外部数据源通过Push的方式上传元数据。

f2bf1aa64bd00ea7aed1f71a042ca0b5.png

相比G1,G2可以随时响应Push请求,很好的平衡了元数据的采集及时性和系统负载。但G2也存在较大问题:

  • 不能订阅元数据变更日志;

  • 单体元数据服务过于集中,所有对元数据的存取需求都集中到该服务,无法支撑跨团队开发。

3. G3 基于事件的方式

G3的出现主要解决G2中存在的元数据服务过于集中,无法及时响应多场景应用开发的情形,引入事件机制,将元数据的获取和应用解耦:

b05c8feb86f7c9d173959751093c3d81.png

比如基于Kafka 等系统 暂存 Metadata log,下游使用方都可以同时订阅和处理元数据变更日志,比如下游应用方可以将变更日志解析后存储到在线 K-V系统,或者导入在线搜索系统或者图引擎或者流式API以及同步到数据湖中以便后续进一步分析处理。

关于元数据采集的更多信息可以参考:

https://engineering.linkedin.com/blog/2020/datahub-popular-metadata-architectures-explained

它详细介绍了元数据系统在LinkIn的发展和设计思考,它有助于理解Datahub背后的设计。

06

尾声

由于篇幅及水平等原因,本文暂写这么多,更多的内容可以参考文章链接,虽然本文主要谈了元数据模型设计和元数据采集,但是基于元数据模型的驱动设计、基于元数据的数据治理以及基于AI的元数据发现等都是很庞大的内容,需要进一步学习和分享。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值