4. 金融核心事实表设计(交易与风控)

好,咱们进入第四章。这一章我打算聊聊金融数据仓库里最核心、也最让人头疼的部分——事实表设计。说白了,就是怎么把交易和风控这两大块数据,装进我们的模型里,还得跑得快、查得准。

我个人习惯把事实表分成三类:累积快照、周期快照,还有无事实的事实表。嗯,别被名字吓到,咱们一个一个拆开讲。

4.1 交易事实表:累积快照的实战

先说说交易事实表。你想想看,一笔交易从下单、支付、清算到最终结算,中间要经历好几个状态。如果用普通的明细事实表,每次状态变化都得插一条新记录,查最新状态还得做子查询,性能直接拉胯。

累积快照就是专门解决这个问题的。它把一笔交易的完整生命周期,压缩到一行记录里。每个关键节点都用一个字段来存时间戳或状态码。

核心思路: 一行记录 = 一笔交易的完整旅程

我在项目中遇到过这样一个场景:某支付平台每天处理上亿笔交易,业务方要查「今天下单但3天内未支付」的交易。如果用明细表,得关联好几张表,跑一次查询要几分钟。改成累积快照后,直接在交易事实表里加一个「支付时间」字段,查询秒级返回。

来看一个简化的表结构设计:

-- 交易累积快照事实表
CREATE TABLE fact_trade_accum_snapshot (
    trade_id          BIGINT COMMENT '交易ID',
    account_id        BIGINT COMMENT '账户ID',
    product_id        BIGINT COMMENT '产品ID',
    -- 时间维度
    create_dt         STRING COMMENT '下单日期',
    pay_dt            STRING COMMENT '支付日期',
    settle_dt         STRING COMMENT '结算日期',
    -- 金额度量
    trade_amount      DECIMAL(18,2) COMMENT '交易金额',
    fee_amount        DECIMAL(18,2) COMMENT '手续费',
    -- 状态退化维度
    trade_status      STRING COMMENT '交易状态: 0-待支付,1-已支付,2-已结算',
    pay_channel       STRING COMMENT '支付渠道: 01-微信,02-支付宝',
    -- 技术字段
    etl_time          TIMESTAMP COMMENT 'ETL更新时间'
)
PARTITIONED BY (dt STRING);

我的经验: 累积快照表里,时间字段建议用STRING类型存日期(如'2024-01-15'),不要用TIMESTAMP。为什么?分区裁剪更高效,而且避免时区转换的坑。

4.2 风险事件事实表:周期快照的艺术

风险事件和交易不一样。交易有明确的开始和结束,但风险是持续监控的。比如反洗钱监控,每天都要看账户的累计交易额是否超过阈值。这时候,周期快照就派上用场了。

周期快照的核心思想是:按固定时间间隔(比如每天),把当前状态拍一张「照片」存下来。不管这期间有没有变化,每天都存一条记录。

我曾经帮一家银行做反欺诈系统,他们原来的做法是只记录触发规则的事件。结果业务方问:「昨天有多少账户处于高风险状态?」——查不出来,因为没有历史状态。后来改成周期快照,每天凌晨跑一次全量快照,所有问题迎刃而解。

设计要点:

  • 快照粒度: 通常是「账户+日期」,每天一条
  • 度量指标: 累计交易额、当日交易笔数、风险评分等
  • 退化维度: 风险等级、预警状态等直接挂在事实表上
-- 风险事件周期快照事实表
CREATE TABLE fact_risk_daily_snapshot (
    account_id        BIGINT COMMENT '账户ID',
    snapshot_dt       STRING COMMENT '快照日期',
    -- 累积度量
    total_trade_amt   DECIMAL(18,2) COMMENT '累计交易金额(近30天)',
    trade_count_30d   BIGINT COMMENT '近30天交易笔数',
    -- 风险指标
    risk_score        DECIMAL(5,2) COMMENT '风险评分(0-100)',
    risk_level        STRING COMMENT '风险等级: L1-低, L2-中, L3-高',
    -- 预警信息
    alert_flag        STRING COMMENT '是否触发预警: Y/N',
    alert_reason      STRING COMMENT '预警原因编码',
    -- 技术字段
    etl_time          TIMESTAMP COMMENT 'ETL更新时间'
)
PARTITIONED BY (dt STRING);

注意: 周期快照的数据量会随时间线性增长。假设有1000万个活跃账户,每天存一条,一年就是36.5亿条。建议按季度或半年做一次数据归档,把旧数据移到冷存储。

4.3 金融指标聚合设计

事实表设计好了,但业务方不会直接查原始表。他们要的是各种聚合指标:比如「今日交易总额」、「高风险账户占比」、「支付成功率」等等。

我的做法是:分层聚合。不要一把梭,从明细到汇总一步到位。那样ETL跑得慢,而且不好排查问题。

我建议分三层:

  1. DWS层(轻度汇总): 按小时、按产品维度聚合。比如每小时每个产品的交易额、笔数。
  2. ADS层(应用汇总): 按天、按业务线聚合。比如每天各业务线的交易指标。
  3. 报表层: 直接对接BI工具,按需聚合。

举个例子,计算「支付成功率」这个指标:

-- DWS层:每小时聚合
INSERT OVERWRITE TABLE dws_trade_hourly
PARTITION (dt='2024-01-15')
SELECT 
    product_id,
    hour(create_time) as hour_id,
    COUNT(*) as total_count,
    SUM(CASE WHEN pay_status = 'SUCCESS' THEN 1 ELSE 0 END) as success_count,
    SUM(trade_amount) as total_amount
FROM fact_trade_accum_snapshot
WHERE dt = '2024-01-15'
GROUP BY product_id, hour(create_time);

-- ADS层:每日聚合,直接算成功率
INSERT OVERWRITE TABLE ads_trade_daily
PARTITION (dt='2024-01-15')
SELECT 
    product_id,
    SUM(total_count) as total_trades,
    SUM(success_count) as success_trades,
    ROUND(SUM(success_count) * 1.0 / SUM(total_count), 4) as success_rate
FROM dws_trade_hourly
WHERE dt = '2024-01-15'
GROUP BY product_id;

避坑指南: 我曾经在聚合层直接用明细表算指标,结果跑一次要2小时。后来改成先轻度汇总,再二次聚合,时间缩短到15分钟。记住:能提前聚合的,绝不拖到后面。

4.4 退化维度处理

最后聊聊退化维度。这个概念听着高大上,其实说白了就是:把那些本来应该做成维度的字段,直接塞进事实表里

什么时候该退化?我总结了三个场景:

  • 字段取值很少: 比如交易状态,就3-5个值。单独建一张维度表,查询时还得JOIN,得不偿失。
  • 字段变化不频繁: 比如支付渠道,一年可能才新增一两个。直接放事实表里,省事。
  • 字段和事实强绑定: 比如交易ID,它本身就是事实表的主键一部分,没必要再建维度表。

我见过最夸张的一个案例:某团队把「性别」字段做成了维度表,然后事实表里存性别ID。每次查交易数据都要关联性别维度表。你说图啥?性别就男、女、未知三个值,直接退化到事实表里,一个字段搞定。

当然,也不是所有字段都适合退化。如果某个字段有几十个属性(比如产品信息:产品名称、类别、风险等级、利率等),那还是老老实实建维度表吧。

下面我用一张图来总结本章的知识体系:

金融核心事实表设计知识体系 交易事实表 累积快照模型 • 一行记录=完整生命周期 • 关键节点时间戳字段 • 状态字段退化处理 • 适合交易状态跟踪 • 查询性能提升明显 风险事件事实表 周期快照模型 • 固定间隔拍快照 • 账户+日期为粒度 • 累积度量指标 • 风险评分与等级 • 预警状态跟踪 金融指标聚合 分层聚合设计 • DWS层:轻度汇总 • ADS层:应用汇总 • 报表层:直接对接BI • 成功率等衍生指标 • 分层ETL提升效率 退化维度处理 适用场景:字段取值少(≤10个) | 变化不频繁 | 与事实强绑定 典型例子:交易状态、支付渠道、风险等级、性别等直接放入事实表 三种事实表模型 + 聚合设计 + 退化维度 = 金融数仓核心能力

嗯,这一章的内容就这些。总结一下:交易用累积快照,风险用周期快照,指标要分层聚合,小字段直接退化。这些都是我在实际项目中踩过坑、填过坑之后总结出来的经验。你照着做,至少能少走一半弯路。


公众号:蓝海资料掘金营,微信deep3321