Revelar el secreto | Echemos un vistazo al diseño y la implementación del esquema de linaje de activos dentro de Kangaroo Cloud Data Stack

Cuando los activos de datos deben conectarse a aplicaciones relacionadas dentro de la pila de datos, los tipos de relaciones consanguíneas admitidas aumentan de tablas y tareas fuera de línea a tablas requeridas, tareas fuera de línea, tareas en tiempo real, tareas de API, indicadores, etiquetas, etc., y todas las tareas de aplicaciones existentes de la pila de datos deben ser compatibles y, finalmente, se realiza un enlace de aplicación completo para ver tareas en la plataforma de activos de datos .

Aunque se agregan diferentes tareas, la línea de sangre de la realización de activos en esta etapa generalmente puede satisfacer las necesidades, pero también ocurrirán problemas, por lo que se requiere innovación tecnológica. Este artículo se centrará en la implementación del linaje de activos y presentará los desafíos y las implementaciones técnicas que encontró Kangaroo Cloud Data Stack en el proceso de construcción del linaje de datos.

Problemas actuales con el linaje de activos

En esta etapa, el contenido de visualización de la relación de sangre es muy repetitivo

En el enlace de linaje de la tarea A del activo , los nodos posteriores de la tarea C/D son todos la tarea E. Cuando abrimos los nodos posteriores de la tarea C/D al mismo tiempo, encontraremos una gran cantidad de nodos duplicados, pero en esencia son exactamente los mismos nodos de tareas, como se muestra en la figura a continuación.

archivo

Cuando hay más tareas en una relación de sangre, la probabilidad de los problemas anteriores aumenta, lo que hará que el contenido del lienzo muestre nodos duplicados y la eficiencia de ver la relación de sangre sea baja.

Se espera que el mismo nodo se pueda mostrar solo una vez en el lienzo. Cuando este nodo exista en el lienzo, se compartirá con el mismo nodo en el futuro, como se muestra en la figura a continuación.

archivo

Visualización de líneas de sangre inversas

Primero, permítanme presentarles brevemente, ¿qué es la relación de sangre inversa ? Usando la tarea API como ejemplo:

archivo

En la plataforma API , use dos tablas para crear una API a través del modo DQL e ingrese la relación de sangre con la tarea API en la plataforma de activos, y se mostrará la relación de sangre que se muestra en la figura a continuación. Si la Tabla A se ha sincronizado con la plataforma de activos, use la Tabla A para ingresar la relación consanguínea y espere mostrar la relación consanguínea como se muestra en la figura a continuación.

Dado que la tabla A/B genera conjuntamente la API, es necesario mostrar un signo más en el lado izquierdo de la API para cargar la tabla B. Esta es la relación de sangre inversa y una parte importante de la implementación del enlace completo.

archivo

fuentes de datos similares

Cuando la misma fuente de datos está conectada por diferentes motores, se generará una fuente de datos diferente A o una fuente de datos A1, pero en realidad la capa inferior es la misma fuente de datos, a la que llamamos fuente de datos similar .

archivo

En este momento, la tarea 1 y la tarea 2 no están relacionadas, pero de hecho, ambas usan la misma tabla subyacente y la visualización no cumple con las expectativas. Se espera que puedan tratarse como la misma tabla en la visualización de relaciones consanguíneas, y las tareas relacionadas también pueden mostrarse en asociación.

archivo

Los problemas anteriores se convierten en la razón de la solución de relación de sangre actual que debe implementarse u optimizarse para realizar la relación de sangre de enlace completo de los activos. Las soluciones a los problemas correspondientes se darán a continuación.

Solución de realización de linaje de activos

Comprensión previa al contenido

● Estructura de datos de linaje de tareas

interface ITaskKinShip {
  metaId:  number;                  // 元Id
  metaType: number;                 // 元类型
  metaDataInfo: object;             // 表、api、任务、标签等的元数据信息的元数据信息
  lineageTableId:  string;          // 血缘Id
  tableKey: string;                 // 表key source.db.table
  sonIds: number[];                 // 子节点血缘定位Id list
  fatherIds: number[];              // 父节点血缘定位Id list
  sonLineage: ITaskKinShip[];       // 子表级血缘
  fatherLineage: ITaskKinShip[];    // 父表级血缘
  isOpen: boolean;                  // 是否存在逆向血缘
}

● Estructura de datos de linaje de campo

interface IColumn {
  columnId: string;             // 字段Id
  columnName: string;           // 字段类型
  columnType:  string;          // 字段
  lineageColumnId: string;      // 字段血缘定位Id
  withManual: boolean;          // 是否手动维护
  withMasked: boolean;          // 是否脱敏字段
  sonIds: number[];             // 子ID
  fatherIds: number[];          // 父ID
  columnKey: string;            // 字段key source.db.table.column
}

interface IColumnKinShip extends ITaskKinShip {
  columns: IColumn[];           // 字段血缘
}

● diagrama de relación de sangre

3 capas se muestran de forma predeterminada durante la inicialización: es decir, solo el nodo de la tabla de datos se muestra como el centro, y los nodos aguas arriba y aguas abajo se muestran respectivamente, un total de 3 capas

Los nodos se pueden expandir: haga clic en el botón "+" para mostrar 3 nodos más delante o detrás del nodo; haga clic en el botón "-" para desconectar la conexión correspondiente al botón "-"

· Clic derecho: en un nodo no central, agregue un menú contextual "Ver el linaje de este nodo"; haga clic derecho para ver el linaje del nodo actual y el linaje centrado en este nodo

toda la idea

● Darse cuenta de compartir nodos

Si queremos lograr compartir nodos , juzgaremos si el nodo actual ya existe en el gráfico. Si existe, ya no representaremos el nodo, de lo contrario, representaremos el nodo, de modo que el nodo solo se pueda representar una vez.

Para cada información de nodo, tableKey es único, por lo que usamos tableKey como identificador único de cada vértice y pasamos tableKey como identificador único al crear un vértice.

createVertex = (treeData) => {
    const { graph, Mx } = this.GraphEditor;
    const rootCell = graph.getDefaultParent();
    const style = this.GraphEditor.getStyles(treeData);
    const doc = Mx.mxUtils.createXmlDocument();
    const tableInfo = doc.createElement('table');
    const { vertex, fatherLineage, sonLineage, ...newData } = treeData;
    tableInfo.setAttribute('data', JSON.stringify(newData));
    // 通过 tableKey 在当前 graph 查找 vertex
    const cell = graph.getModel().getCell(treeData.tableKey);
    // 如果能够找到就不创建新的 vertex,否则创建
    const newVertex =
        cell ||
        graph.insertVertex(
            rootCell,
            treeData.tableKey,
            tableInfo,
            20,
            20,
            VertexSize.width,
            VertexSize.height,
            style
        );
    return { rootCell, style, vertex: newVertex };
}

Si adoptamos la idea anterior, entonces este nodo puede convertirse en el nodo aguas arriba y el nodo subordinado del nodo central al mismo tiempo, o incluso en el nodo central. Por lo tanto, necesitamos cambiar la estructura de nuestros nodos de almacenamiento, y necesitamos almacenar la información del nodo como el nodo ascendente y la información del nodo inferior.

Una vez que se solicitan los datos desde el backend, debemos ordenar los datos y no almacenarlos más en el estado, sino almacenarlos en un objeto Map llamado vertexMap con tableKey como clave. Los objetos de renderizado subsiguientes también se renderizan de acuerdo con vertexMap.

La definición de vertexMap es la siguiente, que se utiliza principalmente para almacenar información de nodos cuya tableKey es el valor clave.

vertexMap = new Map<
    string,
    { 
        rootData?: ITaskKinShip;      // 节点作为根节点存储的数据
        parentData?: ITaskKinShip;    // 节点作为上游节点存储的数据
        childData?: ITaskKinShip;     // 节点作为下游节点存储的数据
        canDeleteData?: any;          // 能够被删除的数据,用于删除的时候判断
    }
>()

Utilice el método checkData para procesar los datos proporcionados por el backend en vertexMap. El punto clave es integrar los datos que son nodos ascendentes o descendentes.

checkData = (treeData: ITaskKinShip) => {
    const {
        tableKey,
        isRoot = false,
        isParent,
        sonIds,
        fatherIds,
        sonLineage,
        fatherLineage,
    } = treeData;
    const mapData = this.vertexMap.get(tableKey);
    // 标识为根结点
    if (mapData?.rootData) return true;
    // 判断是上游节点还是下游节点
    const newKey = isRoot ? 'rootData' : isParent ? 'parentData' : 'childData';
    // 如果不存在上游节点/下有节点的数据直接赋值
    if (!mapData?.[newKey])
        return this.vertexMap.set(tableKey, { ...mapData, [newKey]: treeData });
    // 否则,需要整合相同节点的 sonIds/fatherIds sonLineage/fatherLineage,为后续的判断提供依据
    const nodeData = mapData[newKey];
    const {
        sonIds: exitSonIds,
        fatherIds: exitFatherIds,
        fatherLineage: exitFatherLineage,
        sonLineage: exitSonLineage,
    } = nodeDat
    nodeData.sonIds = [...new Set(sonIds.concat(exitSonIds).filter((id) => id !== 'exist'))];
    nodeData.fatherIds = [
        ...new Set(fatherIds.concat(exitFatherIds).filter((id) => id !== 'exist')),
    ];
    nodeData.sonLineage = [...new Set(sonLineage.concat(exitSonLineage))];
    nodeData.fatherLineage = [...new Set(fatherLineage.concat(exitFatherLineage))];
    nodeData.isChildShow = nodeData.isChildShow || treeData.isChildShow;
    nodeData.isParentShow = nodeData.isParentShow || treeData.isParentShow;
    this.vertexMap.set(tableKey, { ...mapData, [newKey]: nodeData });
};

archivo

Lo anterior es el procesamiento de datos de parentesco consanguíneo, y el nodo del gráfico de parentesco consanguíneo completo es el siguiente:

archivo

● Cómo controlar la expansión de nodos

Cuando se representa cada uno de nuestros nodos, puede haber botones para expandir más nodos en las capas superior e inferior. Debido al uso compartido de nodos y la necesidad de admitir el linaje inverso, el estado del botón cambiará:

· El nodo central no tendrá botón

Cuando el nodo aguas abajo del nodo central tiene una relación de sangre aguas abajo, hay un botón derecho; cuando hay una relación de sangre inversa, hay un botón izquierdo

Cuando el nodo aguas arriba del nodo central tiene relación de sangre aguas arriba, hay un botón izquierdo; cuando hay una relación de sangre inversa, hay un botón derecho

Preste atención a si existe una relación de sangre inversa y júzguelo a través del indicador isOpen de la interfaz de back-end .

archivo

Para el signo + ordinario, al hacer clic en el signo +, se solicitarán tres capas de datos a la vez; para el signo + de linaje inverso, se solicitará una capa de datos a la vez; para el signo -, se recopilarán todos los nodos posteriores.

Para cada nodo, agregaremos isParentShow/isChildShow para indicar si se expanden el flujo ascendente y descendente del nodo actual.

· Operación de contracción: si el botón en el que se hizo clic está en estado expandido, el nodo asociado se contraerá. Al hacer clic en el botón derecho, establezca isChildShow = false para el nodo actual; al hacer clic en el botón izquierdo, establezca isParentShow = false para el nodo actual.

· Operación de expansión: si el botón en el que se hizo clic está contraído, el nodo asociado se expandirá. Al hacer clic en el botón de la derecha, si no hay sonLineage , significa que la información del nodo de sangre no se ha obtenido y se realiza una solicitud al backend; de lo contrario, establezca isChildShow = true para el nodo actual. Lo mismo es cierto para el botón izquierdo.

El proceso de juicio pertinente es el siguiente:

archivo

● Cómo manejar la adición o eliminación de nodos

Podemos usar el botón derecho para insertar una tabla de parentesco sanguíneo o insertar una tabla de impacto. Cuando realizamos la operación de inserción de la relación de sangre, también hay dos situaciones. Se juzga si el nodo actual ha cargado los nodos ascendentes y descendentes. Si se ha cargado, los nuevos datos de la relación de sangre se agregan directamente al vertexMap; de lo contrario, se realiza una solicitud al backend para obtener los datos de la relación de sangre del nodo actual.

archivo

Para eliminar un nodo, ya no es posible encontrar su nodo principal y eliminar el nodo correspondiente antes de volver a renderizar el lienzo. Para compartir nodos, esto causará problemas. En la plataforma de activos , los nodos de relaciones consanguíneas se dividen en dos tipos, uno lo analiza sqlParser y no se puede eliminar manualmente; el otro se agrega manualmente haciendo clic con el botón derecho y se puede eliminar.

Cuando nos encontramos con el intercambio de nodos agregados y analizados manualmente, necesitamos un procesamiento especial para eliminarlos y conservar la información relevante de los nodos analizados.

Cuando insertamos un nodo, sabremos si se analiza o agrega manualmente de acuerdo con el indicador de back-end withManual, y usaremos canDeleteData para mantener la información principal del nodo agregado manualmente .

if (withManual) {
    canDeleteData.push({
        lineageTableId: obj.lineageTableId,
        parentTableKey: obj.tableKey,
    });
}

Cuando implementamos el uso compartido de nodos al principio, integramos sonIds/sonLineage y otra información del mismo nodo a través del método checkData Al eliminar, debemos cooperar con canDeleteData para limpiar los datos correspondientes.

El flujo general de la operación de eliminación es el siguiente:

archivo

● Manejar tipos especiales de nodos

Debido a la visualización de la relación de sangre de varias aplicaciones, hay algunos nodos especiales. La solución convencional no puede cumplir con los requisitos de visualización de nodos, por lo que se requiere un procesamiento especial.

Para FlinkSQL en tiempo real, se requiere un procesamiento especial. El cuadro de línea de puntos es el contenido relevante de FlinkSQL, y los nodos conectados a la tabla de origen de mapeo y la tabla de resultados de mapeo deben estar conectados con líneas de puntos.

archivo

El backend de datos relevante de FlinkSQL se almacena en metaDataInfo, parentFlinkInfos representa la tabla de origen de la asignación y sonFlinkInfos representa la tabla de resultados de la asignación.

archivo

Por lo tanto, dependiendo de la ubicación de FlinkSQL, habrá una lógica de representación diferente:

· Nodo central

Representar parentFlinkInfos para crear parentFlinkTableNode y una conexión de línea sólida de nodo FlinkSQL, parentFlinkTableNode y el nodo ascendente usan una conexión de línea punteada; representar sonFlinkInfos crea sonFlinkTableNode y una conexión de línea sólida de nodo FlinkSQL, crearon sonFlinkTableNode y el nodo descendente usan una conexión de línea punteada.

· Nodo aguas arriba

Representar sonFlinkInfos Cree sonFlinkTableNode para conectarse con el nodo actual con una línea de puntos, represente el nodo FlinkSQL para conectarse con sonFlinkTableNode con una línea continua, represente parentFlinkInfos para crear parentFlinkTableNode para conectarse con el nodo FlinkSQL con una línea continua y parentFlinkTableNode para usar la línea de puntos para conectarse con otros nodos ascendentes.

· Nodos aguas abajo

Renderice parentFlinkInfos para crear parentFlinkTableNode para conectarse con el nodo actual con líneas punteadas, renderice nodos FlinkSQL para conectarse con parentFlinkTableNode con líneas continuas, renderice sonFlinkInfos para crear sonFlinkTableNode para conectarse con nodos FlinkSQL con líneas continuas y sonFlinkTableNode para conectarse con otros nodos posteriores con líneas punteadas.

● fuentes de datos similares

En fuentes de datos similares, el front-end no los ha procesado y el back-end los juzga como fuentes de datos similares y los presenta al front-end para su visualización relacionada.

● Linaje a nivel de campo

Lo mencionado anteriormente es la realización de la relación de sangre a nivel de tabla, y la relación de sangre a nivel de campo también realiza funciones como el intercambio de nodos, y la idea general es consistente con la relación de sangre a nivel de tabla. Lo único a lo que se debe prestar atención es a la relación de sangre del campo en tiempo real. Si hay dos tablas de resultados, habrá múltiples nodos centrales, por lo que es necesario atravesar y renderizar dos nodos centrales.

"Libro blanco del producto Dutstack": https://www.dtstack.com/resources/1004?src=szsm

Dirección de descarga del "Libro blanco de prácticas de la industria de gobierno de datos": https://www.dtstack.com/resources/1001?src=szsm Si desea saber o consultar más sobre los productos de big data, las soluciones de la industria y los casos de clientes de Kangaroo Cloud, visite el sitio web oficial de Kangaroo Cloud: https://www.dtstack.com/?src=szkyzg

Al mismo tiempo, los estudiantes que estén interesados ​​en proyectos de código abierto de big data pueden unirse a "Kangaroo Cloud Open Source Framework DingTalk Technology qun" para intercambiar la última información sobre tecnología de código abierto, número qun: 30537511, dirección del proyecto: https://github.com/DTStack

RustDesk 1.2: Usando Flutter para reescribir la versión de escritorio, apoyando a Wayland acusado de deepin V23 adaptándose con éxito a los lenguajes de programación WSL 8 con la mayor demanda en 2023: PHP es fuerte, la demanda de C/C++ se ralentiza ¿ React está experimentando el momento de Angular.js? El proyecto CentOS afirma estar "abierto a todos" Se lanzan oficialmente MySQL 8.1 y MySQL 8.0.34 Se lanza la versión estable de Rust 1.71.0
{{o.nombre}}
{{m.nombre}}

Supongo que te gusta

Origin my.oschina.net/u/3869098/blog/10089734
Recomendado
Clasificación