:埋點采集、ODS到ADS分層與標簽打通)
簡介《數據中臺實戰(zhàn)手把手教你搭建數據中臺》電子書面向企業(yè)管理者、產品經理、運營設計開發(fā)人員及數據領域學習者聚焦數據中臺從概念認知到落地搭建的完整路徑幫助讀者判斷企業(yè)是否需要中臺并掌握采集、存儲、打通、應用四個核心環(huán)節(jié)。壓縮包為PDF格式僅含1個文件體積約11.3MB便于在電腦或移動設備上閱讀與檢索。目前已有402人學習下載具備一定關注度。書中結合作者在大型企業(yè)參與中臺從0到1建設的實戰(zhàn)經歷圍繞數據埋點、指標管理、分層建模、標簽平臺、用戶與商品流量交易分析、自助分析平臺及自動化營銷等模塊展開配有較多案例與方法論可幫助讀者理解中臺建設的適用條件、實施流程和常見陷阱也為中臺產品經理及企業(yè)數據化轉型項目提供可操作的實踐參考。1. 先別急著買引擎這份大數據中臺資料該在什么階段翻開很多團隊一上大數據中臺先買計算引擎、再拉埋點最后發(fā)現指標口徑對不上看板越做越多決策卻越來越慢。這份數據中臺實戰(zhàn)資料的價值不在概念而在它把采集、存儲、打通、應用四段拆成了能照著做的工序埋點表怎么設計、ODS 到 ADS 怎么分層、標簽平臺怎么從寬表長出來、交易看板怎么給不同角色用。適合正在做數據化轉型的產品、運營、開發(fā)和準備面試數據中臺崗位的人不適合只有一條產品線、數據量還沒起來的初創(chuàng)團隊。讀的時候別從第一頁順讀先拿自己公司的一條業(yè)務線做對照缺哪段補哪段。2. 數據采集埋點表、業(yè)務庫同步與 T-1 鏈路的落地拆解采集做不扎實后面所有分層建模都是空中樓閣。資料里反復強調業(yè)務中臺和數據中臺是兩條腿前者把通用業(yè)務模塊收口后者把跨產品線數據收口沒有業(yè)務中臺也能做數據中臺但采集階段要多跑幾條產品線做調研溝通成本會高很多。落到執(zhí)行采集不是“把日志丟進數倉”這么簡單至少要同時管住三件事埋點事件有沒有統(tǒng)一 schema、業(yè)務庫同步有沒有可重跑的 T-1 鏈路、采集結果有沒有驗收標準。2.1 埋點事件表先定 schema再談采集埋點最容易翻車的地方是事件名和屬性名各寫各的同一個“提交訂單”A 產品線叫submit_orderB 產品線叫order_commit屬性里一個用order_id一個用orderNo。資料里的做法是先定事件命名規(guī)范再讓開發(fā)按表填報。常見命名是“業(yè)務域_對象_動作”比如trade_order_submit、goods_detail_view、cart_add_click屬性分公共屬性和業(yè)務屬性公共屬性由 SDK 統(tǒng)一帶上業(yè)務屬性由埋點方案聲明。事件表不要一上來就寬表化先保證一行一個事件、事件 ID 可去重、時間字段統(tǒng)一到服務端時間。-- 埋點事件明細表按天分區(qū)properties 存業(yè)務屬性 CREATE TABLE IF NOT EXISTS ods_user_event ( event_id STRING COMMENT 事件唯一ID前端生成服務端補發(fā), event_name STRING COMMENT 事件名域_對象_動作, user_id STRING COMMENT 登錄用戶ID未登錄為空, anonymous_id STRING COMMENT 設備匿名ID未登錄時必填, session_id STRING COMMENT 會話ID, page_id STRING COMMENT 頁面ID, module_id STRING COMMENT 模塊ID, platform STRING COMMENT 端web/app/mini, app_version STRING COMMENT 客戶端版本, server_ts BIGINT COMMENT 服務端接收時間戳毫秒, properties STRING COMMENT JSON屬性限制2KB以內, dt STRING COMMENT 分區(qū)日期 yyyy-MM-dd ) PARTITIONED BY (dt) STORED AS PARQUET;這段建表語句的關鍵不是語法而是把“可去重”和“可分區(qū)”提前寫進 schema。event_id用來處理網絡重試導致的重復上報user_id和anonymous_id至少有一個不為空否則后續(xù)無法做用戶行為串聯server_ts不要用客戶端時間代替客戶端時間容易被改properties限制大小避免有人把整個商品對象塞進埋點。分區(qū)字段dt按服務端接收日期落跨天補發(fā)的事件要單獨標記否則日報會對不上。埋點方案評審時我一般會強迫產品把下面這張表填完填不完就不排期。事件名、觸發(fā)時機、必填屬性、屬性類型、責任人、驗收指標這六列缺一不可。尤其是“觸發(fā)時機”要寫到“點擊提交按鈕且接口返回成功后上報”不能寫“用戶下單時”。很多轉化率異常不是數倉算錯而是前端在按鈕點擊時就上報接口失敗也算了一筆。字段示例約束事件名trade_order_submit小寫字母下劃線最長 48 字符觸發(fā)時機提交訂單接口返回成功不能寫“用戶下單時”必填屬性order_id、pay_amount、goods_id空值率超過 1% 阻斷發(fā)布屬性類型pay_amount為 decimal(16,2)金額統(tǒng)一單位元責任人某業(yè)務線產品負責人變更需同步數據團隊驗收指標日事件量、UV、空值率、重復率發(fā)布后 24 小時看板核對埋點驗收不要靠人眼看日志直接跑 SQL 看四個數事件量、去重后事件量、必填屬性空值率、event_id重復率。下面這段查詢按事件名聚合適合放進每日調度超過閾值就告警。參數上dt取業(yè)務日期event_name可以先限定核心轉化事件不用全量跑重復率閾值我一般設 0.5%空值率設 1%超過就回滾 SDK 版本或修埋點方案不要先去改數倉清洗邏輯。-- 埋點驗收核心事件量、空值率、重復率 SELECT event_name, COUNT(1) AS pv, COUNT(DISTINCT event_id) AS pv_dedup, ROUND(1 - COUNT(DISTINCT event_id) / COUNT(1), 4) AS dup_rate, ROUND(SUM(CASE WHEN user_id IS NULL AND anonymous_id IS NULL THEN 1 ELSE 0 END) / COUNT(1), 4) AS id_null_rate, ROUND(SUM(CASE WHEN properties IS NULL OR properties THEN 1 ELSE 0 END) / COUNT(1), 4) AS prop_null_rate FROM ods_user_event WHERE dt ${biz_date} AND event_name IN (trade_order_submit, goods_detail_view, cart_add_click) GROUP BY event_name;提示埋點驗收不通過時先回滾 SDK 版本再查網絡重試和頁面曝光重復上報不要直接改數倉邏輯把異常藏掉。2.2 業(yè)務庫同步與日志采集的 T-1 鏈路業(yè)務庫數據是交易、訂單、商品、用戶這些“結果數據”埋點日志是瀏覽、點擊、加購這些“過程數據”。資料里把采集鏈路分成兩條業(yè)務庫走實時同步加 T-1 全量校對日志走消息隊列落地后按天分區(qū)。常見做法是業(yè)務庫變更用 binlog 追增量每天凌晨再跑一次全量或增量校對防止漏數日志采集在前端埋點后先落消息隊列再離線落到對象存儲或分布式文件系統(tǒng)按dt分區(qū)。兩條鏈路最后都收斂到 ODS 層表名和責任人登記到元數據表里。調度腳本不要寫成“一把梭”至少把業(yè)務日期、任務名、重跑標記、失敗告警四個參數留出來。下面是一個通用 T-1 同步腳本骨架job_config指向同步任務配置biz_date由調度系統(tǒng)傳入rerun用來控制是否覆蓋分區(qū)。腳本里先做源表行數探查再執(zhí)行同步最后寫一條元數據日志這樣出問題時能查到是源表空了還是同步任務失敗了。#!/bin/bash set -euo pipefail BIZ_DATE${1:?業(yè)務日期不能為空格式 yyyy-MM-dd} JOB_CONFIG${2:?同步任務配置不能為空} RERUN${3:-false} # 1. 同步前探查源表行數結果寫入臨時文件 python /opt/sync/bin/check_source.py \ --config ${JOB_CONFIG} \ --biz-date ${BIZ_DATE} \ --output /tmp/source_count_${BIZ_DATE}.json # 2. 執(zhí)行同步reruntrue 時覆蓋目標分區(qū) python /opt/sync/bin/run_job.py \ --config ${JOB_CONFIG} \ --biz-date ${BIZ_DATE} \ --mode overwrite \ --rerun ${RERUN} # 3. 寫元數據日志任務名、業(yè)務日期、源行數、目標行數、狀態(tài) python /opt/sync/bin/write_meta.py \ --job ${JOB_CONFIG} \ --biz-date ${BIZ_DATE} \ --status success參數說明上BIZ_DATE是業(yè)務日期不是執(zhí)行日期很多團隊凌晨跑數時把執(zhí)行日期當業(yè)務日期導致 T-1 數據錯位一天。RERUN只在補數時傳true日常調度傳false避免誤覆蓋。同步任務配置里要明確源表、目標表、主鍵、增量字段、分區(qū)字段、字段映射主鍵用于去重增量字段用于追變更分區(qū)字段用于重跑。日志采集側則要在 ODS 建外部表或分區(qū)表按dt掛載不要每天改表結構。采集完成后必須登記元數據不然三個月后沒人知道ods_order是誰負責、幾點更新、下游有誰用。資料里把中臺團隊拆成十類角色從負責人、產品、數據開發(fā)、分析師到測試和運維采集階段至少要有業(yè)務責任人、數據開發(fā)責任人、運維責任人三方簽字。元數據表不用復雜下面這幾列就夠用表名、來源系統(tǒng)、更新頻率、SLA、主鍵、責任人、下游任務。缺元數據的表我一般不允許進 DWD寧可先卡在 ODS。元數據項示例用途表名ods_order唯一標識來源系統(tǒng)某業(yè)務線訂單庫排查源端問題更新頻率每日 T-109:00 前約定 SLA主鍵order_id去重與對賬責任人某數據開發(fā)變更聯系人下游任務dwd_trade_order影響面分析2.3 采集驗收量、質、時三層卡點采集驗收要卡三層量、質、時。量看日環(huán)比和周同比核心事件量突然掉 30% 或漲 50% 都要查質看主鍵重復、必填字段空值、枚舉值越界時看 SLA比如每天 09:00 前 ODS 分區(qū)必須就緒晚了就觸發(fā)告警。資料里很多實戰(zhàn)案例都指向一個結論采集階段放過的小問題到應用層會變成大問題。比如訂單表里混入測試訂單交易看板會多算埋點里platform出現unknown流量分析會少一塊日志補發(fā)沒有標記日報和實時看板會對不上。采集驗收 SQL 可以固定成模板每個 ODS 表接進來時改表名和主鍵即可。下面這段檢查主鍵重復、分區(qū)行數、關鍵字段空值結果輸出到質量表調度系統(tǒng)根據status決定是否阻斷下游。參數上pk換成實際主鍵dt用業(yè)務日期min_rows設一個業(yè)務可接受的下限比如訂單表日行數不低于 1000低于就告警而不是直接阻斷避免節(jié)假日誤殺。-- 采集質量檢查主鍵重復、行數下限、關鍵字段空值 SELECT ods_order AS table_name, ${biz_date} AS dt, COUNT(1) AS row_cnt, COUNT(1) - COUNT(DISTINCT order_id) AS pk_dup_cnt, SUM(CASE WHEN order_id IS NULL THEN 1 ELSE 0 END) AS pk_null_cnt, SUM(CASE WHEN pay_amount IS NULL THEN 1 ELSE 0 END) AS amount_null_cnt, CASE WHEN COUNT(1) 1000 THEN WARN WHEN COUNT(1) - COUNT(DISTINCT order_id) 0 THEN FAIL WHEN SUM(CASE WHEN order_id IS NULL THEN 1 ELSE 0 END) 0 THEN FAIL ELSE PASS END AS status FROM ods_order WHERE dt ${biz_date};這一層做完采集才算有“黑匣子”可查。后面指標對不上時先看 ODS 行數和主鍵再看 DWD 清洗規(guī)則最后才懷疑匯總邏輯。順序反了會浪費很多時間。3. 存儲與計算ODS 到 ADS 的分層建模和指標口徑怎么落表分層建模最怕做成表名游戲ODS、DWD、DWS 建了一堆業(yè)務一問“昨天支付金額為什么和財務對不上”還是答不出來。資料里的分層不是照搬概念而是把“原始數據、清洗明細、主題匯總、應用結果”分開讓每層只干一件事。ODS 保留原樣方便回溯DWD 做清洗、去重、統(tǒng)一字段DWS 按主題做輕度匯總ADS 面向看板和接口。層級越往下越穩(wěn)定越往上越貼近業(yè)務變更頻率也越高。3.1 分層邊界每層只做該做的事ODS 層不要做復雜清洗尤其不要在這里過濾測試訂單或修改金額否則回溯時找不到原始值。DWD 層負責把 ODS 的臟數據變成可信明細去重、過濾測試賬號、統(tǒng)一金額單位、補全維度、解析 JSON 屬性。DWS 層按主題匯總比如交易主題按日、品類、渠道匯總支付金額和退款金額用戶主題按日匯總活躍、下單、復購。ADS 層直接服務看板和接口可以寬表化但要在指標口徑固定之后再做。選型上離線鏈路優(yōu)先保證可重跑和可對賬實時鏈路只承接真正需要秒級響應的場景。資料里把采集、存儲、打通、應用串起來但并沒有鼓勵所有表都實時化。交易看板 T1 足夠實時大屏才需要流式計算標簽平臺大部分標簽 T1 更新只有“最近 1 小時活躍”這類標簽走實時。分層表建議統(tǒng)一按天分區(qū)小文件多的任務加合并步驟冷數據按保留周期歸檔避免存儲成本失控。層級職責典型表更新頻率禁止事項ODS原樣落地、可回溯ods_order、ods_user_event每日/實時不做業(yè)務過濾DWD清洗、去重、統(tǒng)一口徑dwd_trade_order每日不直接出看板DWS主題匯總、輕度聚合dws_trade_day每日不堆過多維度ADS看板、接口、人群包ads_trade_board每日/按需不反寫明細注意指標口徑變更必須留版本否則歷史看板會集體失真。DWD 的過濾條件一改DWS 和 ADS 都要重跑不能只改最上層。3.2 指標管理體系一個指標一條 YAML指標口徑對不上根因通常不是 SQL 寫錯而是同一個詞在不同團隊嘴里含義不同。資料里強調指標要有業(yè)務口徑、技術口徑、計算周期、計算方式、責任人。我的做法是一個指標一條 YAML進 Git 管理變更走評審。指標碼用“域_主題_度量_周期”比如trade_order_pay_amt_1d業(yè)務口徑寫清楚“支付成功訂單金額扣除退款不含測試訂單”技術口徑寫清楚源表、過濾條件、去重主鍵、金額單位維度列出常用切分比如日期、品類、渠道、產品線。metric_code: trade_order_pay_amt_1d name: 日支付訂單金額 biz_caliber: 支付成功訂單金額扣除當日退款不含測試訂單和風控攔截訂單 tech_caliber: dwd_trade_order 按 order_id 去重 pay_status PAID order_type TEST pay_amount 單位為元保留兩位小數 owner: 某交易數據負責人 period: 1d dimensions: - dt - category_id - channel_id - product_line source_table: dwd_trade_order filter: pay_status PAID AND order_type TEST version: v1.2這份 YAML 的關鍵是“可執(zhí)行”。biz_caliber不是寫給領導看的而是給開發(fā)和分析師對照的tech_caliber要能直接翻譯成 SQLdimensions決定 DWS 建哪些匯總維度version讓歷史看板能追到口徑版本。指標變更時不要直接改原文件新增版本并保留舊版本ADS 表按版本號切換。否則一個“支付金額”改了口徑歷史報表全部對不上業(yè)務方會直接不信數據。指標管理還有一條血淚經驗不要讓每個看板自己寫 SQL 算指標。看板只消費 ADS 或指標服務指標服務背后統(tǒng)一走 YAML 定義。這樣新看板接入時不用重新討論口徑直接選指標碼和維度即可。自助分析平臺也一樣業(yè)務人員可以拖拽維度但指標必須是注冊過的不能讓他們在頁面上隨便寫sum(pay_amount)。3.3 從 ODS 到 ADS 的 SQL 骨架先看 DWD 清洗。下面這段 SQL 從 ODS 訂單表取數按order_id去重過濾測試訂單統(tǒng)一金額單位補上品類和渠道維度。參數上dt是業(yè)務日期row_number()按update_time倒序取最新一條處理業(yè)務庫同一訂單多次變更pay_amount如果源表是分要除以 100這里假設源表已經是元。測試訂單的識別條件用order_type TEST不要用訂單號前綴猜規(guī)則要寫在指標 YAML 里。-- DWD 訂單明細去重、過濾測試單、統(tǒng)一金額單位 INSERT OVERWRITE TABLE dwd_trade_order PARTITION (dt ${biz_date}) SELECT order_id, user_id, product_line, category_id, channel_id, pay_status, pay_amount, refund_amount, create_time, update_time FROM ( SELECT order_id, user_id, product_line, category_id, channel_id, pay_status, CAST(pay_amount AS DECIMAL(16,2)) AS pay_amount, CAST(refund_amount AS DECIMAL(16,2)) AS refund_amount, create_time, update_time, ROW_NUMBER() OVER (PARTITION BY order_id ORDER BY update_time DESC) AS rn FROM ods_order WHERE dt ${biz_date} AND order_type TEST ) t WHERE rn 1;DWS 匯總按日、品類、渠道、產品線聚合支付金額和退款金額分開算最后在 ADS 里做凈額。這樣退款政策變化時不用重跑 DWD只改匯總邏輯。下面這段 SQL 的pay_amt只統(tǒng)計支付成功訂單refund_amt統(tǒng)計當日退款net_amt是凈額。參數上dt分區(qū)對齊category_id和channel_id允許為空空值單獨歸到“未知”維度不要直接丟掉否則大盤對不上。-- DWS 交易日匯總按品類、渠道、產品線 INSERT OVERWRITE TABLE dws_trade_day PARTITION (dt ${biz_date}) SELECT ${biz_date} AS dt, product_line, COALESCE(category_id, UNKNOWN) AS category_id, COALESCE(channel_id, UNKNOWN) AS channel_id, COUNT(DISTINCT CASE WHEN pay_status PAID THEN order_id END) AS pay_order_cnt, SUM(CASE WHEN pay_status PAID THEN pay_amount ELSE 0 END) AS pay_amt, SUM(CASE WHEN refund_amount 0 THEN refund_amount ELSE 0 END) AS refund_amt, SUM(CASE WHEN pay_status PAID THEN pay_amount ELSE 0 END) - SUM(CASE WHEN refund_amount 0 THEN refund_amount ELSE 0 END) AS net_amt FROM dwd_trade_order WHERE dt ${biz_date} GROUP BY product_line, COALESCE(category_id, UNKNOWN), COALESCE(channel_id, UNKNOWN);ADS 看板表可以直接從 DWS 取數按角色拆指標。領導層看大盤凈額、同比環(huán)比運營看品類、渠道、退款率產品看轉化漏斗。下面這段只做一張交易看板樣例net_amt和refund_rate是核心指標。參數上dt用業(yè)務日期refund_rate分母為 0 時要返回 0避免除零同比環(huán)比可以在調度里另算不要在看板查詢時實時算否則數據量大時頁面會卡。-- ADS 交易看板大盤核心指標 INSERT OVERWRITE TABLE ads_trade_board PARTITION (dt ${biz_date}) SELECT dt, SUM(pay_amt) AS pay_amt, SUM(refund_amt) AS refund_amt, SUM(net_amt) AS net_amt, SUM(pay_order_cnt) AS pay_order_cnt, CASE WHEN SUM(pay_amt) 0 THEN 0 ELSE ROUND(SUM(refund_amt) / SUM(pay_amt), 4) END AS refund_rate FROM dws_trade_day WHERE dt ${biz_date} GROUP BY dt;跑完還要做對賬。至少查三項DWD 主鍵是否重復、DWS 匯總金額是否等于 DWD 明細金額、ADS 展示金額是否等于 DWS 匯總金額。下面這段對賬 SQL 輸出差異行差異不為 0 就阻斷下游看板發(fā)布。參數上${biz_date}同一天金額比較允許 0.01 的浮點誤差超過就查清洗規(guī)則或匯總維度是否丟數。-- 交易鏈路對賬DWD 明細 vs DWS 匯總 SELECT d.dt, d.pay_amt AS dwd_pay_amt, s.pay_amt AS dws_pay_amt, d.pay_amt - s.pay_amt AS diff_amt FROM ( SELECT dt, SUM(pay_amount) AS pay_amt FROM dwd_trade_order WHERE dt ${biz_date} AND pay_status PAID GROUP BY dt ) d LEFT JOIN ( SELECT dt, SUM(pay_amt) AS pay_amt FROM dws_trade_day WHERE dt ${biz_date} GROUP BY dt ) s ON d.dt s.dt WHERE ABS(COALESCE(d.pay_amt,0) - COALESCE(s.pay_amt,0)) 0.01;4. 數據打通標簽平臺從寬表到人群圈選的最小閉環(huán)標簽平臺不是把用戶屬性拼成一張大寬表就完事真正難的是身份映射和標簽更新節(jié)奏。資料里把標簽平臺放在“打通”環(huán)節(jié)是因為用戶行為數據和業(yè)務數據往往存在不同產品線、不同系統(tǒng)里行為數據知道用戶點了什么業(yè)務數據知道用戶買了什么兩邊對不上用戶畫像就是殘缺的。打通的目標不是把所有字段都塞進一張表而是讓“用戶是誰、干了什么、買了什么”能在同一個用戶 ID 下被查詢和圈選。4.1 標簽體系先分三層基礎、行為、業(yè)務標簽體系建議先分三層基礎標簽、行為標簽、業(yè)務標簽?;A標簽是注冊天數、會員等級、所在城市等級這類變化慢的屬性行為標簽是近 30 天點擊品類數、近 7 天訪問頻次、最近一次活躍時間業(yè)務標簽是首單金額、近 90 天訂單數、退款次數。每類標簽都要有元數據標簽碼、名稱、類型、更新方式、源表、責任人、有效期。不要一上來做幾百個標簽先做 20 個業(yè)務真用的跑通“寬表生成—標簽計算—人群圈選—營銷調用”閉環(huán)。寬表按天分區(qū)只放常用標簽長尾標簽按需計算。下面建表語句把用戶 ID、基礎標簽、行為標簽、業(yè)務標簽放在一張日分區(qū)表里dt是標簽日期user_id是主鍵。參數上標簽列允許為空空值代表“未知”而不是“否”更新方式用全量覆蓋避免增量 merge 寫錯導致標簽漂移。寬表字段不要無限擴超過 100 列就拆成基礎寬表和行為寬表否則查詢和跑批都會變慢。-- 用戶標簽寬表按天分區(qū)常用標簽放一張表 CREATE TABLE IF NOT EXISTS dws_user_tag_wide ( user_id STRING COMMENT 統(tǒng)一用戶ID, register_days INT COMMENT 注冊天數, member_level STRING COMMENT 會員等級, last_active_time STRING COMMENT 最近活躍時間, click_category_cnt_30d INT COMMENT 近30天點擊品類數, visit_cnt_7d INT COMMENT 近7天訪問頻次, first_order_amt DECIMAL(16,2) COMMENT 首單金額, order_cnt_90d INT COMMENT 近90天訂單數, refund_cnt_90d INT COMMENT 近90天退款次數, dt STRING COMMENT 標簽日期 yyyy-MM-dd ) PARTITIONED BY (dt) STORED AS PARQUET;標簽計算不要每個標簽一條獨立任務按源表分組批量算。行為標簽從 DWD 事件表算業(yè)務標簽從 DWD 訂單表算基礎標簽從用戶維度表算最后按user_id全外連接。連接時注意空值行為表里沒有行為的用戶也要保留否則寬表只?;钴S用戶圈選“沉默用戶”就圈不出來。標簽有效期也要寫進元數據比如“近 30 天點擊品類數”每天重算“會員等級”每天同步過期標簽要標記不可用。4.2 身份映射打通產品線數據的硬骨頭跨產品線打通最難的是身份映射。同一個用戶在產品線 A 用手機號注冊在產品線 B 用設備號瀏覽在 product_line C 用會員 ID 下單如果沒有映射表三個 ID 就是三個人。常見做法是建一張身份映射表字段包括統(tǒng)一用戶 ID、手機號哈希、設備 ID、會員 ID、來源產品線、最近活躍時間、分區(qū)日期。優(yōu)先級一般是會員 ID 高于手機號哈希手機號哈希高于設備 ID沖突時按最近活躍時間保留人工可修正。手機號只存哈希不落明文設備 ID 也要做脫敏。-- 身份映射表多產品線 ID 歸一到 union_id CREATE TABLE IF NOT EXISTS dim_user_identity ( union_id STRING COMMENT 統(tǒng)一用戶ID, user_id STRING COMMENT 產品線內用戶ID, phone_hash STRING COMMENT 手機號哈希不存明文, device_id STRING COMMENT 設備ID脫敏后存儲, member_id STRING COMMENT 會員ID, product_line STRING COMMENT 來源產品線, last_active STRING COMMENT 最近活躍時間, dt STRING COMMENT 分區(qū)日期 ) PARTITIONED BY (dt) STORED AS PARQUET;映射邏輯分兩步先按強 ID 配對手機號哈希相同或會員 ID 相同就歸到同一個union_id再用設備 ID 補弱關聯但設備 ID 沖突多不能單獨作為唯一依據。同一設備多賬號、同一手機號多產品線都要保留映射明細不能直接覆蓋。下面這段 SQL 是歸一的骨架ROW_NUMBER()按last_active倒序取最近一條COALESCE按優(yōu)先級選 ID。參數上dt用映射日期phone_hash要統(tǒng)一哈希算法和鹽值否則跨產品線對不上。-- 身份歸一強ID優(yōu)先設備ID兜底 INSERT OVERWRITE TABLE dim_user_identity PARTITION (dt ${biz_date}) SELECT CONCAT(U, MD5(CONCAT(COALESCE(phone_hash, member_id, device_id), ${salt}))) AS union_id, user_id, phone_hash, device_id, member_id, product_line, last_active, ${biz_date} AS dt FROM ( SELECT user_id, phone_hash, device_id, member_id, product_line, last_active, ROW_NUMBER() OVER ( PARTITION BY COALESCE(phone_hash, member_id, device_id) ORDER BY last_active DESC ) AS rn FROM ods_user_identity WHERE dt ${biz_date} ) t WHERE rn 1;提示身份映射表一定要保留來源產品線和最近活躍時間后續(xù)本文還有配套的精品資源點擊獲取