业务需求:统计广告数据,生成流量漏斗,查看广告投放效果。
StarRocks原表结构:
CREATE TABLE `ad_events` (
`event_time` DATETIME NOT NULL COMMENT '时间',
`event_time_hour` DATETIME NOT NULL COMMENT '时间(分、秒为0)',
`event_type` TINYINT(4) NOT NULL COMMENT '事件类型(1:请求,2:展示,3:点击)',
`user_id` INT(11) NOT NULL COMMENT '用户ID(映射为整数)',
`jh_more` TINYINT(4) NOT NULL COMMENT '是否聚合',
`ad_site_id` VARCHAR(128) NOT NULL COMMENT '代码位id',
`client_type` TINYINT(4) NOT NULL COMMENT '客户端类型',
`my_app_id` VARCHAR(64) NOT NULL COMMENT '应用id',
`ad_type` VARCHAR(32) NOT NULL COMMENT '广告类型',
`platform` TINYINT(4) NOT NULL COMMENT '广告源',
`device_id` VARCHAR(64) NOT NULL COMMENT '设备号'
)
COLLATE='utf8_general_ci'
ENGINE=OLAP
PARTITION BY date_trunc('month', event_time) # 按月分区
DISTRIBUTED BY HASH(`user_id`) BUCKETS 100
PROPERTIES (
"compression" = "LZ4", # 存储算法
"fast_schema_evolution" = "true",
"partition_live_number" = "12", # 保留12个月数据,就是1年的数据
"replicated_storage" = "true",
"replication_num" = "3" # 3个副本
);
统计分析sql:
SELECT
event_type,
COUNT(event_type) AS event_type_nums,
COUNT(DISTINCT device_id) AS dau_nums
FROM
ssp_ad_events
WHERE
user_id = 10077 -- 动态参数
AND event_time >= '2025-07-01 00:00:00' -- 动态参数
AND event_time <= '2025-07-01 23:59:59' -- 动态参数
GROUP BY
event_type;
存在问题:
1、分区字段未有效利用
使用了 event_time精确过滤,但表是按 date_trunc('month', event_time)按月分区,无法利用分区裁剪,会扫描多个分区。
PARTITION BY date_trunc('month', event_time) -- 按月分区
2、COUNT(DISTINCT)函数
COUNT(DISTINCT) 是计算密集型操作,尤其当 device_id 数量很大时,比如上百万个不同设备访问,性能开销非常高。
优化方式:
1、将分区字段改为按天分区
PARTITION BY date_trunc('day', event_time) -- 按天分区
2、COUNT(DISTINCT device_id) ------ 性能瓶颈优化
当 device_id是字符串类型 VARCHAR(64),并且有大量不同设备访问时,StarRocks 会对所有值进行去重计算。
这是一个高CPU、高内存、低效的操作,尤其在大规模数据下,可能消耗数秒甚至更长时间。
使用 Bitmap 类型 + BITMAP_UNION_COUNT 实现高性能去重统计。
优化后的表结构:
CREATE TABLE `ad_events_optimized` (
event_time DATETIME NOT NULL COMMENT '事件时间',
event_time_hour DATETIME NOT NULL COMMENT '小时粒度时间(分秒归零)',
event_type TINYINT NOT NULL COMMENT '事件类型',
user_id INT NOT NULL COMMENT '用户ID',
jhn_more TINYINT NOT NULL,
ad_site_id VARCHAR(128) NOT NULL,
client_type TINYINT NOT NULL,
my_app_id VARCHAR(64) NOT NULL,
ad_type VARCHAR(32) NOT NULL,
platform TINYINT NOT NULL,
device_id VARCHAR(64) NOT NULL,
-- 使用 BIGINT 存储哈希值(避免冲突)
device_id_hash BIGINT,
-- BITMAP 列(用于聚合去重)
device_bitmap BITMAP BITMAP_UNION
)
PARTITION BY date_trunc('day', event_time)
DISTRIBUTED BY HASH(user_id) BUCKETS 100
PROPERTIES (
"replication_num" = "3",
"compression" = "LZ4"
);
优化后的sql:
SELECT
event_type,
COUNT(event_type) AS event_type_nums,
bitmap_union_count(device_bitmap) AS dau_nums -- 优化的地方
FROM
ssp_ad_events
WHERE
user_id = 10077
AND event_time >= '2025-07-01 00:00:00'
AND event_time <= '2025-07-01 23:59:59'
GROUP BY
event_type;
导入数据时生成 Bitmap:
-- INSERT 时直接生成
INSERT INTO ad_events_optimized (
event_time, event_time_hour, event_type, user_id,
jhn_more, ad_site_id, client_type, my_app_id,
ad_type, platform, device_id,
device_id_hash,
device_bitmap
)
VALUES
('2025-07-01 10:30:00', '2025-07-01 10:00:00', 1, 10077,
1, 'site_001', 2, 'app_123',
'banner', 1, 'device_abc123',
bitmap_hash('device_abc123'), -- 自动生成哈希
to_bitmap(bitmap_hash('device_abc123')) -- 转为 Bitmap
);
优化后性能提升10倍以上,在十亿级数据量下能接近秒级响应(BE节点的资源要给够)~
扩展:使用物化视图
物化视图适合固定单参数,由于业务存在动态参数,暂时无法使用