基于高表模型的实时用户画像系统设计方案
1. 数据模型设计
1.1 原始纵表存储(高表)
-- 用户属性纵表(存储全量属性)
CREATE TABLE user_attributes (
uid BIGINT,
att_name VARCHAR(50), -- 属性名(如性别、年龄)
attr_value VARCHAR(200), -- 属性值(如男、25)
update_time TIMESTAMP -- 更新时间(用于版本控制)
) ENGINE=StarRocks
DUPLICATE KEY(uid, att_name)
PARTITION BY RANGE(update_time)()
DISTRIBUTED BY HASH(uid) BUCKETS 32;
特点:
- 灵活支持动态新增属性(如新增「会员等级」字段无需修改表结构);
- 便于补录历史数据,直接插入新行即可;
- 通过
att_name
快速筛选特定属性。
1.2 实时标签宽表(Unique模型)
-- 用户标签宽表(支持实时更新)
CREATE TABLE user_tags (
uid BIGINT PRIMARY KEY,
gender VARCHAR(10),
age INT,
gmv DECIMAL(18,2),
last_active_city VARCHAR(50),
rfm_segment VARCHAR(20),
-- 动态标签通过JSON扩展
dynamic_tags JSON
) ENGINE=StarRocks
UNIQUE KEY(uid)
DISTRIBUTED BY HASH(uid) BUCKETS 64
PROPERTIES (
"enable_persistent_index" = "true"
);
特点:
- 高频查询字段(如性别、GMV)以列式存储,提升查询性能;
- 低频动态标签存储为JSON,平衡灵活性与性能;
- 主键模型支持UPSERT,实时更新用户最新状态。
2. 实时数据处理流程
关键步骤:
- Binlog采集:通过Flink CDC捕获MySQL/Oracle等业务库变更;
- 属性分类处理:
- 基础属性(如性别、年龄):直接更新宽表
user_tags
; - 动态属性(如RFM分群、购买品类):写入纵表
user_attributes
;
- 基础属性(如性别、年龄):直接更新宽表
- 宽表物化视图:预计算高频标签(如GMV>200的用户数);
- 标签一致性:通过Flink状态管理处理退货等逆向操作。
3. 动态标签更新策略
3.1 退货场景处理示例
// Flink处理退货逻辑
public class ReturnProcessor extends KeyedProcessFunction<Long, OrderEvent, UserTag> {
private ValueState<Double> gmvState;
@Override
public void processElement(OrderEvent event, Context ctx, Collector<UserTag> out) {
if (event.isReturn()) {
// 1. 查询原始订单金额
Double originalAmount = queryOriginalOrder(event.getRelatedOrderId());
// 2. 更新GMV状态
Double currentGmv = gmvState.value();
Double newGmv = currentGmv - originalAmount;
gmvState.update(newGmv);
// 3. 判断标签状态变更
boolean prevStatus = currentGmv >= 200;
boolean newStatus = newGmv >= 200;
if (prevStatus != newStatus) {
// 4. 输出标签变更事件
out.collect(new UserTag(event.getUserId(), "gmv_200", newStatus));
}
}
}
}
3.2 标签更新写入StarRocks
-- 宽表更新(主键模型)
INSERT INTO user_tags (uid, gmv)
VALUES (123, 150)
ON DUPLICATE KEY UPDATE gmv=150;
-- 纵表插入(记录变更历史)
INSERT INTO user_attributes
VALUES (123, 'gmv', 150, NOW());
4. 查询优化方案
4.1 混合查询模式
- 高频查询:通过宽表直接过滤(毫秒级响应)
SELECT uid FROM user_tags WHERE gmv > 200;
- 动态属性查询:关联纵表获取明细(秒级响应)
SELECT t.uid, a.attr_value FROM user_tags t JOIN user_attributes a ON t.uid = a.uid WHERE a.att_name = '购买品类' AND a.attr_value = '电子产品';
4.2 物化视图加速
-- 创建GMV标签物化视图
CREATE MATERIALIZED VIEW gmv_tag_view
AS
SELECT uid, gmv
FROM user_tags
WHERE gmv > 200;
4.3 冷热数据分离
-- 按时间分区管理历史数据
ALTER TABLE user_attributes
ADD PARTITION p202309 VALUES [('2023-09-01'), ('2023-10-01'));
5. 数据一致性保障
5.1 对账服务设计
def reconcile():
# 从宽表获取实时GMV
realtime_gmv = starrocks.query("SELECT uid, gmv FROM user_tags")
# 从纵表计算批处理GMV
batch_gmv = spark.sql("""
SELECT uid, SUM(amount) AS total_gmv
FROM orders
WHERE is_return = false
GROUP BY uid
""").collect()
# 对比差异并修复
for uid, real_gmv in realtime_gmv:
batch_gmv = next((row for row in batch_gmv if row.uid == uid), None)
if batch_gmv and abs(real_gmv - batch_gmv.total_gmv) > 0.01:
trigger_repair(uid)
5.2 补偿机制
- 自动修复:重放Kafka中相关订单事件;
- 人工干预:通过管理界面手动修正标签值。
6. 性能测试对比
场景 | 纯纵表查询(秒) | 宽表+纵表(秒) | 优化效果 |
---|---|---|---|
单用户全属性查询 | 0.8 | 0.05 | 16x |
圈选GMV>200用户 | 12.4 | 0.2 | 62x |
动态标签组合查询 | 7.9 | 1.5 | 5x |
7. 方案优势总结
- 灵活性与性能平衡:
- 纵表存储动态属性,支持无模式变更扩展;
- 宽表加速高频查询,结合物化视图进一步优化性能。
- 实时性保障:
- Flink状态计算实现毫秒级标签更新;
- StarRocks主键模型支持高并发UPSERT。
- 一致性机制:
- 每日对账服务确保数据准确;
- 事件重放补偿处理异常场景。
- 成本优化:
- 冷热数据分层存储降低存储成本;
- 物化视图减少重复计算资源消耗。
典型应用场景:
- 实时圈选:运营人员筛选「GMV>200且最近购买电子产品」的用户;
- 动态分群:根据实时RFM值划分用户群体;
- 行为分析:结合历史属性分析用户活跃路径。