
1) 【一句话结论】
针对用户行为数据仓库,采用星型模式构建事实表(存储行为日志),维度表(用户、物品、时间等),选择ClickHouse作为存储引擎,通过Flink实现ETL,并配置物化视图和倾斜处理,确保高效分析用户行为数据。
2) 【原理/概念讲解】
老师口吻解释核心概念:
3) 【对比与适用场景】
| 模式 | 事实表与维度表关系 | 数据冗余 | 查询复杂度 | 适用场景 | 注意点 |
|---|---|---|---|---|---|
| 星型模式 | 事实表直接连接维度表,无嵌套 | 较高(维度表字段直接关联) | 低(查询路径短,如JOIN操作简单) | 大多数分析型场景,如用户行为分析、流失率计算 | 维度表字段过多可能导致事实表过大,需合理设计维度表 |
| 雪花模式 | 维度表可能嵌套(如用户维度表嵌套设备维度表) | 较低(减少冗余) | 高(查询需多表连接,复杂度增加) | 需要更细粒度分析,但查询效率可能下降 | 查询复杂,维护成本高 |
4) 【示例】
user_behavior_fact):
CREATE TABLE user_behavior_fact (
behavior_id UUID PRIMARY KEY,
user_id BIGINT,
item_id BIGINT,
behavior_type STRING,
behavior_time TIMESTAMP,
device_type STRING,
user_name STRING,
item_name STRING,
user_gender STRING,
item_category STRING,
PARTITION BY TOYYYYMM(behavior_time) AND TOHH(behavior_time) AND TOINTPART(user_id / 1000000) -- 按时间+用户ID分桶(分桶大小1M)
) ENGINE = MergeTree()
ORDER BY behavior_time;
user_dim):
CREATE TABLE user_dim (
user_id BIGINT PRIMARY KEY,
user_name STRING,
register_time TIMESTAMP,
gender STRING,
age INT
) ENGINE = MergeTree() ORDER BY user_id;
CREATE MATERIALIZED VIEW user_behavior_stats AS
SELECT
user_id,
COUNT(*) AS behavior_count,
SUM(CASE WHEN behavior_type = 'click' THEN 1 ELSE 0 END) AS click_count,
SUM(CASE WHEN behavior_type = 'share' THEN 1 ELSE 0 END) AS share_count
FROM
user_behavior_fact
WHERE
behavior_time >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY
user_id;
from flink import Flink, KafkaSource, ClickHouseSink
source = KafkaSource("user_behavior_topic", "localhost:9092")
def clean_data(record):
if record["behavior_type"] not in ["click", "like", "share"]:
return None
return record
def transform_data(record):
user = get_user_from_dim(record["user_id"])
item = get_item_from_dim(record["item_id"])
return {
"behavior_id": str(uuid.uuid4()),
"user_id": record["user_id"],
"item_id": record["item_id"],
"behavior_type": record["behavior_type"],
"behavior_time": record["behavior_time"],
"device_type": record["device_type"],
"user_name": user["user_name"],
"item_name": item["item_name"],
"user_gender": user["gender"],
"item_category": item["category"]
}
sink = ClickHouseSink("hdfs://namenode:9000/user_behavior_fact", "clickhouse", checkpoint_interval=5, state_backend="redis")
flink = Flink()
flink.add_source(source)
flink.add_transform(clean_data)
flink.add_transform(transform_data)
flink.add_sink(sink)
flink.execute()
SELECT
user_name,
behavior_count,
click_count,
share_count
FROM
user_behavior_stats
WHERE
behavior_count < 3 -- 定义流失用户(行为次数<3)
ORDER BY
behavior_count ASC;
5) 【面试口播版答案】
面试官您好,针对用户行为数据仓库设计,我会采用星型模式。事实表存储行为日志,主键用UUID,ClickHouse按时间+用户ID分桶(分桶大小1M,平衡分区数量与查询性能),维度表包括用户、物品、时间。ETL用Flink从Kafka采集,清洗后关联维度表,生成事实表,并配置checkpoint保证容错。同时,预计算用户行为统计的物化视图,支持快速查询用户流失率,确保数据一致性和查询效率。具体来说,事实表按时间(年月日)和用户ID分桶存储,分桶大小1M能避免分区过多或数据倾斜,维度表通过Debezium同步用户表变更,ETL用Flink的Exactly-Once语义保证数据完整性,物化视图每小时刷新,延迟<1秒,支持实时分析。
6) 【追问清单】
7) 【常见坑/雷区】