主機廠資料資產血緣分析治理實踐

架構師修行手冊發表於2023-11-17


來源:之家技術


1. 背景

隨著汽車之家主機廠業務的發展,資料資產日益龐大,資料倉儲的構建也越來越複雜,在日常的數倉構建工作中,常遇到資料模型建設不規範無法治理,資料溯源困難,資料模型修改導致業務分析困難等難題,此類問題主要是由於資料血緣分析不足造成的,只有強化血緣關係,才能更好的服務於數倉建模及治理和最大程度的發揮資料價值。

血緣關係在數倉建模中扮演著核心的角色。透過深入解析SQL語句並對其進行分析,我們可以洞察出各種模型之間的依賴關係,從而形成一張精細的血緣關係圖。這張圖不僅展示了資料模型之間的連鎖關係,也為資料流程的上游和下游提供了清晰的指示。

對於數倉模型的構建來說,血緣關係也具有重要的意義。透過了解模型之間的依賴關係,我們可以更好地評估模型的健壯性和穩定性。此外,血緣關係還可以幫助我們最佳化資料模型的構建過程,提高模型的質量和效率。

在資料質量控制和治理方面,血緣關係同樣發揮著不可忽視的作用。透過血緣關係的追蹤,我們可以清晰地瞭解到資料從源頭到目的地的整個過程,從而更好地控制資料的質量。同時,血緣關係也可以幫助我們實現資料的可追溯性,為資料治理提供有效的溯源手段。這樣,一旦出現問題,我們就可以迅速找出問題的源頭,從而採取有效的措施進行治理和糾正。

總的來說,血緣關係在數倉建模中起著至關重要的作用,對於資料分析師、數倉模型的構建以及資料質量控制和治理都具有深遠的影響。


2. 遇到的問題

資料資產血緣分析過程中遇到的問題如下:

1.資料資產命名不規範:因歷史存在未明確的命名規則並未強制執行,使得研發人員命名隨意,增加血緣解析的複雜性。

2.ETL任務排程依賴遺漏:ETL任務排程依賴遺漏是一個常見的問題,這可能導致血緣關係的不完整或不準確。有時候,ETL任務之間的依賴關係可能非常複雜,如果某個任務沒有正確配置或者遺漏或者定時任務無法設定依賴,那麼血緣關係圖就會不完整。

3.ETL指令碼型別眾多:不同的ETL指令碼型別可能帶來解析的困難。例如:shell指令碼、Python指令碼、PySpark指令碼和HQL等都有可能在ETL過程中使用,這無疑增加了血緣解析的複雜性。

4.HQL檔案沒有統一的程式設計規範:單個HQL檔案中出現多個insert語句,查詢的表名不帶資料庫名。隨意的程式設計風格降低了可讀性和可維護性。

5.應用層資料資產呼叫不清晰:應用層資料服務方式有多種且沒有埋點資料,例如匯出至資料庫為介面提供資料,自助分析平臺直接檢索,Kylin查詢等,無法將資料資產與應用情況建立聯絡。


這些問題的出現對資料資產的血緣分析帶來困難和挑戰,也影響了資料治理和最佳化的效果。


3. 分析思路

3.1

埋點分析

業務埋點是在應用程式中嵌入特定的程式碼,用於跟蹤和記錄使用者行為和業務操作。透過業務埋點,可以獲取到應用程式中各個業務表的使用情況和呼叫次數,從而解決應用層資料資產呼叫不清晰的問題。


a.埋點分析整體流程

流程圖如下:

主機廠資料資產血緣分析治理實踐

圖1

如上圖紅色標識部分,對業務後端服務透過AOP(面向切面程式設計)方式,注入MyBatis 攔截外掛,切面將MyBaits攔截外掛從各模組抽出來,降低了對模組的耦合度。透過外掛的方式將與業務無關的程式碼封裝起來,減少了重複程式碼。將SQL攔截器關注點統一到一個外掛管理,修改更新方便,不影響其他業務模組。並且透過外掛方式,減少對原先模組的侵入性,增強其靈活度,可以隨意透過增加或者刪除SQL攔截器外掛來控制是否此業務模組需要進行埋點。


b.使用方式

如果模組需要對業務表進行埋點統計,只需在專案中引入SQL攔截器外掛即可。如下:





<dependency>    <groupId>com.autohome.index</groupId>    <artifactId>common-sqlplugin</artifactId>    <version>1.1-SNAPSHOT</version></dependency>


c.SQL攔截器

首先指定了要攔截的方法是Executor類的update方法、query方法,在 intercept方法中,獲取查詢或者更新的SQL語句,解析SQL獲取表資訊,然後存入redis中,然後,我們透過呼叫 invocation.proceed()來執行原始方法。

具體實現部分程式碼如下:































...//攔截器註解@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),public class SqlPluginInterceptor implements Interceptor {         @Override    public Object intercept(Invocation invocation) throws Throwable {        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];        Object parameter = null;        if (invocation.getArgs().length > 1) {            parameter = invocation.getArgs()[1];        }        BoundSql boundSql = mappedStatement.getBoundSql(parameter);        Configuration configuration = mappedStatement.getConfiguration();        long startTime = System.currentTimeMillis();        try {            return invocation.proceed();        } finally {            try {                 ......                //將查詢sql和表資訊存入redis                saveSqlToRedis(retSQL,sqlCost,mappedStatement.getId());            } catch (Exception e) {                System.out.println("sql攔截出錯!");                e.printStackTrace();            }        }    }    }


3.2

ETL任務解析

透過ETL任務指令碼解析,解決單獨依靠任務依賴分析血緣造成的血緣不完整問題,從而將血緣補充完整。另外解決ETL指令碼型別眾多,不能將全部的依賴關係融入到血緣關係中。


a.任務解析流程

主機廠資料資產血緣分析治理實踐

圖2

任務解析分為非常規指令碼任務和常規ETL指令碼任務兩種,常規ETL指令碼任務為正常的hql指令碼檔案,解析流程為透過SQL語句解析出血緣關係,存入MYSQL資料庫;非常規指令碼任務有shell指令碼,Python指令碼,PySpark指令碼等,常規任務解析失敗的也都歸為非常規任務,每天解析指令碼任務時,將此類指令碼任務彙總傳送預警郵件,然後線下,根據不同型別任務解析指令碼,程式識別不了的,人工手動處理,透過上述自動程式處理後,人工處理部分佔比為5%以內。


b.任務解析方式

首先獲取常規腳ETL任務指令碼內容,經過SQL解析工具解析出表血緣關係,最後存入MYSQL,如果解析失敗,則存入非常規任務結果表,最終統一收集處理。

















try :        file_content = hql_parse_util.parse_hql(hqlfile)        table_dict= hql_parse_util.parse_output(file_content.lower())        for out_put_table in table_dict :            input_tables = table_dict[out_put_table]            if out_put_table == 'tbnames':                out_put_table = '.'            for input_table in input_tables:                if input_table.startswith('--'):                    continue                sql_val.append([hqlfile,out_put_table.split('.',1)[0],out_put_table.split('.',1)[1],                                input_table.split('.',1)[0],input_table.split('.',1)[1],uid,name,'hive',job_status,date_time])    cnt = cursor.executemany(insert_sql, sql_val)    print ('已入庫常規任務' + str(cnt) + '條')    conn.commit()finally:    cursor.close()


3.3

SQL語句分析

主機廠資料資產血緣分析治理實踐

圖3


在處理SQL語句時,我們可以將其主要分為兩個部分:source table和target table。這兩個部分的解析和處理方式可能會有所不同。

首先,source table部分,如果包含with塊,那麼我們可以透過關鍵字with分割SQL語句擷取出with部分,對with塊內的select語句進行正則解析,生成KV資料。在這個過程中,我們可以將with塊的名稱作為key,將解析後的tableName陣列作為value。


with塊解析程式碼如下:





















def sql_parse(sql):    sql = sql.lower()    with_tb_dict = {}    sql = re.sub('with[-_a-z]','',sql)    if 'with' in sql:        with_all_arr = sql.split("with")[1:]        for with_content in with_all_arr:            # with 塊解析            with_content = with_content[0:with_content.index("insert")].strip()            # 正則匹配with部分            with_content = re.sub('\\)\s*,\s*\'','',with_content)            with_arr =re.split('\\)\s*,', with_content,0)            with_arr = [content + ")" for content in with_arr[:-1]] + [with_arr[-1]]            with_list = []            # 遍歷擷取的with塊語句 解析出with 別名與內部查詢的表名關係            ...            # 解析多with塊,查詢引用的情況            ...        print (with_tb_dict)
然後,我們還需要對select語句進行解析,得到tableName陣列。在以上過程中,如果tableName陣列中存在不包含db的tableName,我們需要解析use語句,完成db對映。最後,將解析的tableName陣列中的with名稱替換為相應的tableName。

對於target table部分,我們可以透過解析insert語句得到。具體來說,我們需要解析insert into和insert overwrite table語句,得到target tableName。

最後,我們將target table和source table進行關聯。在這個過程中,我們需要將source table中的tableName陣列和target tableName進行匹配,完成關聯操作。

獲取select語句中的表名:












def sql_parse_get_tablenames(sql_query):    table_names = re.findall(r'from\s+(\S+)', sql_query, re.IGNORECASE)    table_names += re.findall(r'join\s+(\S+)', sql_query, re.IGNORECASE)    tb_names = []    for name in table_names:        # 不包含 '('        if '(' not in name:            tb_names.append(name.replace(')','').replace('`','').replace(';',''))    result = list(set(tb_names))    return result

如果SQL語句中包含use語句且存在不含資料庫資訊的表名,則實現資料庫與表名對映。程式碼如下:






























# 判斷sql中是否包含useif 'use ' in sql:    use_arrs = sql.split('use ')[1:]    for arr in use_arrs:        db_name = arr.split(';')[0]        if 'insert' in arr:            # insert 解析            insert_arr = arr.split("insert ")[1:]            for tb in insert_arr:                if 'into' in tb:                    insert_tb = tb[tb.index("into ") + len("into "):tb.index("\n")].strip()                else:                    insert_tb = tb[tb.index("table ") + len("table "):tb.index("\n")].strip()                hive_target_tb = insert_tb.split(" ")[0]                if '.' not in hive_target_tb:                    hive_target_tb = db_name + '.' + hive_target_tb                tb_source_tb = []                # 對映tableName陣列中with名                ...                # 對映target table 和 source table                ...        else:            tb_source_tb = []            # 對映tableName陣列中with名            ...            # 對映target table 和 source table            ...

解析得到的tableName陣列中如果存在為with塊名稱,則替換with對應的tableName陣列。程式碼如下:









def sql_parse_tb_map(with_tb_dict,sql):    source_tb = sql_parse_get_tablenames(sql)    if len(with_tb_dict) > 0:        for s in range(len(source_tb)-1,-1,-1):            if source_tb[s] in with_tb_dict:                source_tb +=  with_tb_dict.get(source_tb[s])                source_tb.remove(source_tb[s])    return source_tb


4. 分析及治理效果

4.1

分析效果資料

為了有效地分析SQL解析後資產的血緣狀況,我們透過制定一種量化指標——血緣覆蓋率,來關注資料資產的血緣情況。這種度量標準可以幫助我們更好地瞭解資料資產之間的關係以及資料從源頭到目標的流動情況。

血緣覆蓋率是一種關鍵效能指標,用於衡量資料資產的血緣關係被解析和追蹤到的程度。這個指標含義指當至少有一條血緣鏈路與資產相關時,該資產即被血緣覆蓋,而被血緣覆蓋的資產佔所有關注資產的比例即為血緣覆蓋率。


在實際操作中,我們可以透過以下步驟來計算血緣覆蓋率:

1. 確定關注的資產:根據業務需求和資料治理策略,確定需要關注的資料資產範圍。

2. 識別血緣關係:透過解析SQL語句,構建資料資產之間的血緣關係。

3.計算血緣覆蓋率:將血緣覆蓋的資產數量與所有關注資產的總量相除,得到血緣覆蓋率的數值。

4.分析血緣覆蓋率:將計算得出的血緣覆蓋率與其他指標進行對比,例如資料資產的總量、血緣關係的數量等。透過這種比較,可以發現哪些資料資產的血緣關係未被覆蓋,以及哪些血緣鏈路未涉及到目標資產。

透過制定和計算血緣覆蓋率這種量化指標,我們可以更好地瞭解SQL解析後資料資產的血緣狀況,進而最佳化資料治理策略、提升資料質量、加強資料安全。

下圖為分析效果資料:


主機廠資料資產血緣分析治理實踐

圖4


4.2

治理資料

根據業務埋點,統計出表使用頻次,透過與業務庫中的全量業務表進行對比,分析出殭屍表(長時間沒有維護,沒有人使用的表) ,這部分表也佔用大量的儲存空間,造成資源浪費。所以定期自動化分析清理殭屍表,協同業務方共同確認,是否已經真正沒有人使用,包括除業務產品外的其他使用場景,比如不定時出業務報告,業務人員自己配置的業務看板等,最終確定的殭屍表,清理時,首先將資料備份到備份庫中,其次將原庫中的表刪除,等待1個月時間後,確定沒有任何業務異常發生,則從備份庫中物理刪除。目前,每週大約清理出10張殭屍表,後續清理不斷推進中。


5. 總結

資料資產血緣分析是一種用於理解和追蹤資料資產之間關係的過程,其目的是幫助組織更好地管理和保護其資料資產。

以下是資料資產血緣分析的過程:

1.確定分析目標:首先需要明確血緣分析的目標,例如理解資料流、發現資料依賴關係、識別資料風險等。

2. 收集資料資產:收集需要進行血緣分析的資料資產,這可能包括各種資料表、ETL過程等。

3.資產關係解析:透過解析資料資產(如SQL查詢、ETL過程等),理解它們之間的依賴關係。

4.分析血緣關係:基於血緣資料分析各資料資產之間的血緣關係,例如哪些資料表被其他表引用、哪些查詢依賴於其他查詢等。這種分析可以幫助發現潛在的資料依賴問題,例如迴圈依賴、單點故障、跨層引用等。

5.風險評估:透過血緣分析,可以評估資料資產的風險,例如某表被大量查詢引用可能需要進行最佳化,某查詢結果被其他大量查詢引用可能存在效能風險等。

6. 生成血緣報告:根據血緣分析的結果,生成血緣報告。報告應清晰地描述各資料資產之間的血緣關係、發現的問題及建議的解決方案。

7.最佳化建議:基於血緣報告的結果,可以提出資料治理、最佳化或保護的建議,例如對資料庫進行最佳化、改進ETL過程等。


總之,資料資產血緣分析是一個持續的過程,需要定期進行以發現和解決資料管理問題並在實踐中不斷總結經驗和方法。此外,隨著組織環境和資料資產的改變,血緣分析的結果和應用也需要不斷更新和改進。


6. 後續規劃

後續的重點工作將會放在資料質量保障、整合多資料來源、構建血緣關係圖譜等方向,幫助改進資料資產血緣分析結果和應用,提高組織對資料管理和使用的效率和效果。

以下是一些思路:

1.完善資料質量保障:血緣分析的準確性很大程度上取決於資料本身的質量。可以進一步制定嚴格的資料質量管理策略,包括資料規範、資料清洗、資料對映等,以確保資料的準確性和一致性。

2.整合多源資料:探索將不同來源的資料進行整合,包括內部資料、外部資料、第三方資料等,以便更全面地瞭解資料的來龍去脈。

3.持續最佳化資料血緣模型:隨著業務複雜性和資料量的增長,資料血緣模型可能需要進行調整和最佳化。可以針對不同的資料型別和業務需求,探索更加靈活、可擴充套件的血緣模型,以適應不斷變化的資料流和資料處理過程。

4.構建血緣關係圖譜:將資料血緣關係視覺化,可以更直觀地展示資料之間的關係和影響。幫助使用者更好地理解資料資產之間的關聯和依賴關係。

建立血緣分析標準和規範:為了使血緣分析更具可操作性和一致性,可以制定相關的標準和規範,明確血緣分析的流程、方法、工具和技術要求。這樣可以提高血緣分析的可信度和可靠性,併為後續的血緣分析改進提供參考和依據。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70027824/viewspace-2995810/,如需轉載,請註明出處,否則將追究法律責任。

相關文章