1. 从"1天响应"到"定义即可查"的进化之路
数美科技作为国内领先的AI风控服务商,每天需要处理数百TB的实时数据流。在早期架构中,业务团队提出一个数据查询需求,从需求评审到最终交付通常需要1天时间。这种延迟在风控场景下是致命的——黑产攻击手法每小时都在变化,我们的防御策略必须更快。
传统流程的瓶颈主要存在于三个环节:
- 需求沟通阶段:业务方与数据团队需要反复确认查询口径
- SQL开发阶段:工程师需要手动编写复杂查询逻辑
- 资源调度阶段:临时查询可能挤占实时计算资源
我们通过三层架构革新解决了这些问题:
- 交互层:自然语言转SQL工具(基于内部训练的NLP模型)
- 计算层:动态资源隔离的Spark+ClickHouse混合引擎
- 存储层:云器Lakehouse支持的统一元数据管理
关键突破:将业务指标定义为可复用的"数据原子",通过JSON配置实现语义化查询。例如"同一设备1小时内注册账号数"被封装为
anti_fraud.device_register_count[1h],业务人员直接组合这些原子即可生成查询。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 混合计算引擎的技术选型
面对实时风控场景中既要低延迟又要高吞吐的需求,我们采用了Spark与ClickHouse的混合部署方案:
2.1 Spark批处理优化
- 参数调优:
python复制# 关键配置示例 spark.conf.set("spark.sql.shuffle.partitions", 2000) # 避免shuffle时数据倾斜 spark.conf.set("spark.executor.memoryOverhead", "2g") # 防止OOM - 数据倾斜处理:
- 对UID等倾斜键增加随机前缀
- 采用两阶段聚合(局部聚合+全局聚合)
2.2 ClickHouse实时查询优化
- 表引擎选择:
- ReplacingMergeTree处理去重场景
- CollapsingMergeTree处理状态变更场景
- 跳数索引配置:
sql复制ALTER TABLE fraud_log ADD INDEX device_idx(device_id) TYPE bloom_filter GRANULARITY 3
2.3 资源隔离方案
通过YARN的Node Label实现混合部署:
- 批处理任务:使用
batch标签节点,允许资源抢占 - 实时查询:使用
realtime标签节点,保证最低资源配额
3. 元数据驱动的查询范式
核心创新在于将业务逻辑全部沉淀为元数据,通过三层映射实现"定义即可查":
-
物理存储层(HDFS路径)
code复制/user/data/anti_fraud/dt=20230701/event=register -
逻辑模型层(JSON Schema)
json复制{ "metrics": [ { "name": "device_register_count", "type": "count_distinct", "dimension": ["device_id"], "time_window": ["1h","24h"] } ] } -
业务语义层(自然语言)
sql复制SELECT device_register_count[1h] FROM anti_fraud WHERE province = '广东'
这套体系使得95%的日常查询不再需要工程师介入,需求响应时间从24小时缩短到5分钟以内。
4. 踩坑实录:那些年我们遇到的性能陷阱
4.1 JSON解析的性能灾难
初期直接使用Spark原生JSON解析,发现CPU利用率长期90%+。通过基准测试对比方案:
| 方案 | 吞吐量(GB/s) | CPU使用率 |
|---|---|---|
| Spark原生JSON | 0.8 | 92% |
| SimdJson+UDF | 3.2 | 45% |
| 预处理为Parquet | 5.7 | 28% |
最终采用预处理方案:原始JSON通过Flink实时转Parquet,牺牲5分钟延迟换取5倍吞吐提升。
4.2 ClickHouse的并发瓶颈
当并发查询超过200时,CH出现性能骤降。通过以下优化解决:
- 增加query_thread_log记录慢查询
- 对高频查询建立物化视图
- 配置max_concurrent_queries=500
4.3 数据倾斜的连锁反应
某次大促期间,某个异常设备生成数百万日志,导致Spark作业卡死。解决方案:
scala复制// 采样检测倾斜key
val skewKeys = df.stat.approxQuantile("device_id", Array(0.99), 0.01)
// 动态添加随机后缀
val processedDF = df.withColumn("skew_key",
when($"device_id".isin(skewKeys:_*), concat($"device_id", lit("_"), floor(rand()*10)))
.otherwise($"device_id"))
5. 云器Lakehouse的落地实践
作为存储基座,Lakehouse带来三个核心价值:
-
ACID事务保障:通过Delta Lake实现批流一体
sql复制-- 合并流式数据到批处理表 MERGE INTO user_profiles t USING streaming_updates s ON t.user_id = s.user_id WHEN MATCHED THEN UPDATE SET * WHEN NOT MATCHED THEN INSERT * -
统一元数据管理:所有数据资产通过GraphQL API暴露
graphql复制query { metrics(domain: "anti_fraud") { name description sqlDefinition } } -
智能分层存储:
- 热数据:Alluxio内存缓存
- 温数据:NVMe SSD存储
- 冷数据:对象存储+ZSTD压缩
这套架构使得我们的存储成本降低60%,同时查询性能提升3倍。目前平台日均处理:
- 实时事件:1200亿条
- 批量计算:800TB
- 即席查询:15万次
在风控这个争分夺秒的战场上,数据平台的速度就是业务的生命线。当黑产凌晨2点发起攻击时,我们的规则工程师能在10分钟内完成从数据探查到策略上线的全过程——这才是大数据平台该有的样子。
