揭秘|來看看袋鼠雲數棧內部的資產血緣方案設計與實現

来源:https://www.cnblogs.com/DTinsight/archive/2023/07/20/17568303.html
-Advertisement-
Play Games

[數據資產](https://www.dtstack.com/dtinsight/dataassets?src=szsm=07)現在需要接入數棧內部相關應用的時候,支持查看血緣的類型從表、離線任務增加到需要表、離線任務、實時任務、API任務、指標、標簽等,需要支持數棧現有的所有應用任務,最終實現在[ ...


數據資產現在需要接入數棧內部相關應用的時候,支持查看血緣的類型從表、離線任務增加到需要表、離線任務、實時任務、API任務、指標、標簽等,需要支持數棧現有的所有應用任務,最終實現在數據資產平臺查看任務的完整應用鏈路。

雖然增加不同的任務,現階段資產實現的血緣大體上能夠滿足需求,但是也會出現問題,因此需要進行技術革新。本文將聚焦資產血緣的實現方案,並介紹袋鼠雲數棧在數據血緣建設過程中所遇到的挑戰和技術實現。

資產血緣的當前問題

現階段血緣展示內容重覆高

在資產的 A 任務血緣鏈路中,C/D 任務下游均為 E 任務,當我們同時打開了 C/D 任務的下游節點,就會發現大量的重覆節點出現,但本質上他們是一模一樣的任務節點,如下圖。

file

當一條血緣上的任務越多,出現上述問題的概率增大,會導致畫布內容顯示的都是重覆節點,查看血緣關係效率低下。

期望能夠做到同一個節點只在畫布中展示一次,在這個節點存在於畫布中時,後續再有相同節點就做共用,如下圖。

file

逆向血緣的展示

先簡單介紹一下,何為逆向血緣?用 API 任務舉例子來說:

file

API 平臺中,通過 DQL 模式使用兩張表創建一個 API,在資產平臺以 API 任務進入血緣就會展示如下圖的血緣關係。如果 Table A 已經同步到了資產平臺,以 Table A 表進入血緣,希望展示如下圖的血緣關係。

由於 API 是由 Table A/B 共同生成的,因此需要在 API 左側展示加號,能夠將 Table B 載入出來,這就是逆向血緣,也是全鏈路實現的重要一環。

file

相似數據源

當同一個數據源被不同的引擎連接之後,會產生不同的數據源A或數據源A1,但是實際上底層是同一個數據源,被我們稱之為相似數據源

file

此時任務1和任務2並不會有相關聯繫,但其實它兩用的是相同的底層表,展示是不符合期望的,希望能夠在血緣展示上將其視為同一張表,相關的任務也能關聯展示。

file

上述問題成為資產實現全鏈路血緣需要去實現或者優化的當前血緣方案的理由,下文將給出對應問題的解決方案。

資產血緣實現解決方案

前置內容瞭解

● 任務血緣數據結構

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;                  // 是否存在逆向血緣
}

● 欄位血緣數據結構

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[];           // 欄位血緣
}

● 血緣關係圖

· 初始化時預設展示3層:即只展示以數據表本節點為中心,上下游分別的一個節點,共3層

· 節點可擴展:點擊“+”按鈕,在節點前方或後方再展示出3個節點;點擊“-”按鈕,斷開該“-”按鈕對應的連線

· 右鍵:在非中心節點上,添加右鍵菜單“查看此節點血緣”;滑鼠右鍵可查看當前節點血緣,以該節點為中心的血緣

整體思路

● 實現節點共用

我們想要去實現節點共用,就會去判斷當前節點是否已經存在於 graph 中,如果存在我們就不再渲染節點,否則就去渲染節點,這樣就可以實現節點只被渲染一次。

對於每一個節點信息來說,tableKey 都是唯一的,因此我們將 tableKey 作為每一個 vertex 的唯一標識,在創建 vertex 時,傳入 tableKey 作為唯一標識。

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 };
}

如果我們採用上述的想法,那這個節點可以同時成為中心節點的上游節點和下有節點,甚至就是中心節點。因此我們需要更改我們存儲節點的結構,需要存儲節點作為上游節點的信息以及作為下有節點的信息。

當從後端請求來數據之後,我們需要對數據做一個整理,不再存放在 state 中,而是將其存在以 tableKey 為 key,名為 vertexMap 的 Map 對象中。後續的渲染對象,也根據 vertexMap 去做渲染。

vertexMap 的定義如下,主要用於存儲 tableKey 為鍵值的節點信息。

vertexMap = new Map<
    string,
    { 
        rootData?: ITaskKinShip;      // 節點作為根節點存儲的數據
        parentData?: ITaskKinShip;    // 節點作為上游節點存儲的數據
        childData?: ITaskKinShip;     // 節點作為下游節點存儲的數據
        canDeleteData?: any;          // 能夠被刪除的數據,用於刪除的時候判斷
    }
>()

通過 checkData 方法來將後端給的數據處理到 vertexMap 中,重點是需要整合同為上游節點或者同為下游節點的數據。

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 });
};

file

上述是對血緣數據的處理,對於整個血緣圖節點如下圖:

file

● 如何控制節點擴展

當我們的每一個節點被渲染出來的時候,可能會存在按鈕,可以展開上下三層的更多節點。由於節點共用以及需要支持逆向血緣的需求,因此按鈕的狀態會發生變化:

· 中心節點不會有按鈕

· 中心節點下游節點有下游血緣時,有右側按鈕;存在逆向血緣時,有左側按鈕

· 中心節點上游節點有上游血緣時,有左側按鈕;存在逆向血緣時,有右側按鈕

註意是否存在逆向血緣,通過後端介面的 isOpen 標識來做判斷。

file

對於普通的 + 號來說,點擊 + 號時,一次性會請求三層數據;對於逆向血緣的 + 號來說,一次性請求一層數據;點擊 - 號來說,會將其後續的節點都收起來。

對於每一個節點來說,我們會添加 isParentShow/isChildShow 來表示當前節點上下游是否展開。

· 收起操作:如果點擊的按鈕處於展開狀態,則將相關聯節點收起。點擊右邊按鈕時,對當前節點設置 isChildShow = false;點擊左邊按鈕時,對當前節點設置isParentShow = false。

· 展開操作:如果點擊的按鈕處於收起狀態,則將相關聯節點展開。點擊右邊按鈕時,如果不存在 sonLineage 說明尚未獲取過血緣節點信息,向後端發起請求;否則對當前節點設置isChildShow = true。對於左邊按鈕也是如此。

相關的判斷流程如下圖:

file

● 如何處理增加/刪除節點

我們可以採用右鍵去插入血緣表或者插入影響表。當我們去做插入血緣操作的時候,也分為兩種情況,判斷當前節點是否已經載入過上游、下游節點,如果載入過,直接把新血緣數據添加到 vertexMap 中;否則,向後端發起請求,獲取當前節點的血緣數據。

file

對於刪除節點來說,不能夠再像之前那樣找到其父級節點刪除對應節點之後再重新渲染畫布,對於節點共用來說,這會出現問題。在資產平臺來說,血緣節點分為兩種,一種是通過 sqlParser 解析出來的,不能夠進行手動刪除;另一種是我們通過右鍵手動添加的,是可以刪除的。

當我們遇到手動添加和解析出來的節點共用時,我們進行刪除就需要特殊處理,將解析出來節點的相關信息保留下來。

在我們插入節點的時候,我們會根據後端標識位 withManual 得知是解析還是手動添加的,使用 canDeleteData 維護手動添加的節點父級信息

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

在最開始我們實現節點共用的時候,我們將同一個節點的 sonIds/sonLineage 等信息通過 checkData 方法整合在一起,刪除的時候應該配合 canDeleteData 把對應的數據一同清理掉。

刪除操作的大致流程如下圖:

file

● 處理特殊的類型節點

由於承接了各個應用的血緣展示,不乏一些特殊的節點,常規的方案無法滿足節點展示需求,因此需要做特殊處理。

對於實時的 FlinkSQL 就是需要特殊處理的,虛線框中的是 FlinkSQL 的相關內容,和映射源表、映射結果表連接的節點需要用虛線連接。

file

對於 FlinkSQL 的相關數據後端存放於 metaDataInfo 中,parentFlinkInfos 表示映射源表,sonFlinkInfos 表示映射結果表。

file

因此根據 FlinkSQL 所處的位置不同,會有不同的渲染邏輯:

· 中心節點

渲染 parentFlinkInfos 創建 parentFlinkTableNode 與 FlinkSQL 節點實線連接,parentFlinkTableNode 再和上游節點使用虛線連接;渲染 sonFlinkInfos 創建 sonFlinkTableNode 與 FlinkSQL 節點實線連接,創建的 sonFlinkTableNode 再和下游節點使用虛線連接。

· 上游節點

渲染 sonFlinkInfos 創建 sonFlinkTableNode 與當前節點虛線連接,渲染 FlinkSQL 節點和 sonFlinkTableNode 實線連接,渲染 parentFlinkInfos 創建 parentFlinkTableNode 與 FlinkSQL 節點實線連接,parentFlinkTableNode 再去和別的上游節點使用虛線連接。

· 下游節點

渲染 parentFlinkInfos 創建 parentFlinkTableNode 與當前節點虛線連接,渲染 FlinkSQL 節點和 parentFlinkTableNode 實線連接,渲染 sonFlinkInfos 創建 sonFlinkTableNode 與 FlinkSQL 節點實線連接,sonFlinkTableNode 再去和別的下游節點使用虛線連接。

● 相似數據源

在相似數據源上,前端並沒有對其做過的處理,均為後端判斷為相似數據源給到前端做相關展示。

● 欄位級血緣

上述講的都是表級血緣的實現,欄位級血緣也實現了節點共用等功能,整體思路和表級血緣一致。唯一需要註意的是實時的欄位血緣,如果存在兩個結果表時,會存在多個中心節點,所以需要遍歷,渲染兩個中心節點。

《數棧產品白皮書》:https://www.dtstack.com/resources/1004?src=szsm

《數據治理行業實踐白皮書》下載地址:https://www.dtstack.com/resources/1001?src=szsm

想瞭解或咨詢更多有關袋鼠雲大數據產品、行業解決方案、客戶案例的朋友,瀏覽袋鼠雲官網:https://www.dtstack.com/?src=szbky

同時,歡迎對大數據開源項目有興趣的同學加入「袋鼠雲開源框架釘釘技術qun」,交流最新開源技術信息,qun號碼:30537511,項目地址:https://github.com/DTStack


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文介紹了值類型和引用類型在編程中的區別。值類型包括簡單類型、枚舉類型和結構體類型,通常被分配線上程的堆棧上,變數保存的是實例數據本身。引用類型實例則被分配在托管堆上,變數保存的是實例數據的記憶體地址。值類型由操作系統負責管理,而引用類型則由垃圾回收機制(GC)負責管理。本文還通過示例代碼展示了值類型... ...
  • ## 一:背景 ### 1. 講故事 這篇文章源自於分析一些疑難dump的思考而產生的靈感,在dump分析中經常要尋找的一個答案就是如何找到死亡線程的生前都做了一些什麼?參考如下輸出: ``` C# 0:001> !t ThreadCount: 22 UnstartedThread: 0 Backg ...
  • 本文介紹了 C# 中委托的定義、使用、為什麼引入委托以及委托的本質。同時,還介紹了委托鏈的使用,即將多個委托鏈接在一起,提高程式的可擴展性。 ...
  • > 2023/7/20 初學內核,記錄與分享,感嘆內核學了後真的感覺很多東西都通透了,但是難度太大,只能淺淺初探。 # 前提 內核五大功能 ➢ **進程管理**:進程的創建,銷毀,調度等功能 註:可中斷,不可中斷,就是是否被信號打斷。從運行狀態怎樣改到可中斷等待態,和不可中斷等待態操作系統開始會對每 ...
  • ARP學習和老化機制在Linux網路通信中起著至關重要的作用。ARP(Address Resolution Protocol)地址解析協議是將IP地址解析為MAC地址的一種機制。本文將詳細介紹Linux中ARP學習和老化的概念、工作原理以及其重要性。 ...
  • ![](https://img2023.cnblogs.com/blog/3076680/202307/3076680-20230719150159605-2139117895.png) # 1. 變換結果集成一行 ## 1.1. 結果集 ```sql DEPTNO CNT 10 3 20 5 30 ...
  • 該如何選擇ClickHouse的表引擎 本文將介紹ClickHouse中一個非常重要的概念—表引擎(table engine)。如果對MySQL熟悉的話,或許你應該聽說過InnoDB和MyISAM存儲引擎。不同的存儲引擎提供不同的存儲機制、索引方式、鎖定水平等功能,也可以稱之為表類型。ClickHo ...
  • 在之前的這篇文章Oracle/MySQL/PG/SQL Server關係資料庫中NULL與空字元串的區別[1]中,簡單對比、介紹了一下MySQL的NULL/空格/尾部空格處理方式,主要對比了NULL與長度為空的字元串,其實很多地方沒有分析到位就一筆帶過了。這篇文章重新來細說一下MySQL的尾部空格處 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...