优化前:同步写入所有数据源 — editorial cover photo
专项治理

重读DDIA,我重构了一遍风控系统

风控系统是典型的数据密集型应用,其复杂性主要来自数据量、数据复杂度和数据变化速度,而非计算密集度。

2026年7月10日·返回文章列表
文章大纲

DDIA核心理念在风控系统中的应用

1. 数据密集型特征

风控系统是典型的数据密集型应用,其复杂性主要来自数据量、数据复杂度和数据变化速度,而非计算密集度。

核心改造案例

案例1:从单体到分布式 - 可靠性与可扩展性

优化前:单体风控系统

Mermaid
正在渲染图表…

问题

  • 单点故障风险
  • 垂直扩展瓶颈(单机8C16G已到极限)
  • 发布时服务中断

优化后:基于DDIA理念的分布式架构

Mermaid
正在渲染图表…

改进效果

  • 可靠性:3副本部署,可用性从99.9%提升到99.99%
  • 性能:TPS从2000提升到20000
  • 可维护性:支持灰度发布,服务不中断

案例2:数据一致性优化 - CAP权衡

优化前:强一致性设计

# 优化前:同步写入所有数据源
def save_decision_result(decision_data):
try:
# 事务开始
with db.transaction():
# 写主库
db.execute("INSERT INTO decisions ...", decision_data)
# 同步写缓存
redis.set(f"decision:{decision_data.id}", decision_data)
# 同步写搜索引擎
es.index("decisions", decision_data)
# 同步写消息队列
mq.send("decision_topic", decision_data)
except Exception as e:
rollback_all()
raise e

问题

  • 任一组件故障导致整体失败
  • 延迟累加,P99达到200ms

优化后:最终一致性设计

# 优化后:基于事件驱动的最终一致性
def save_decision_result(decision_data):
# 1. 只写主库(事务保证)
with db.transaction():
db.execute("INSERT INTO decisions ...", decision_data)
db.execute("INSERT INTO decision_events ...",
{"type": "DECISION_CREATED", "data": decision_data})
# 2. 异步处理其他数据源
async_update_cache.delay(decision_data.id)
async_update_search.delay(decision_data.id)
async_send_message.delay(decision_data.id)
# 事件处理器(可重试)
@retry(max_attempts=3, backoff=exponential)
def process_decision_event(event):
if event.type == "DECISION_CREATED":
update_cache(event.data)
update_search(event.data)
send_notification(event.data)

改进效果

  • 延迟降低:P99从200ms降到30ms
  • 可用性提升:部分组件故障不影响核心流程
  • 最终一致:通过重试机制保证数据最终一致
PlantUML
正在加载图表…
PlantUML diagram

图:数据一致性权衡——同步写所有数据源导致延迟累加、任一故障即失败;改为只写主库+事件驱动异步同步,P99 从 200ms 降到 30ms

案例3:分区策略优化 - 水平扩展

优化前:按时间分表

-- 优化前:简单按月分表
CREATE TABLE decisions_202401 (...);
CREATE TABLE decisions_202402 (...);
-- 问题:热点集中在当前月份表

优化后:多维度分区策略

# 基于用户ID的一致性哈希分区
class ShardingStrategy:
def __init__(self, shard_count=64):
self.shard_count = shard_count
self.virtual_nodes = 150 # 虚拟节点提高均衡性
def get_shard(self, user_id):
# 使用MurmurHash保证均匀分布
hash_value = mmh3.hash(user_id)
return hash_value % self.shard_count
def get_table_name(self, user_id, date):
shard_id = self.get_shard(user_id)
# 组合分区:用户维度 + 时间维度
return f"decisions_{shard_id}_{date.strftime('%Y%m')}"
# 路由层实现
class DecisionRouter:
def query(self, user_id, start_date, end_date):
tables = self.get_involved_tables(user_id, start_date, end_date)
results = []
# 并行查询多个分片
with ThreadPoolExecutor(max_workers=10) as executor:
futures = [
executor.submit(self.query_single_table, table, user_id)
for table in tables
]
for future in as_completed(futures):
results.extend(future.result())
return results

改进效果

  • 负载均衡:数据均匀分布在64个分片
  • 扩展性:支持动态增加分片
  • 查询性能:单用户查询只涉及1/64的数据
PlantUML
正在加载图表…
PlantUML diagram

图:分区策略演进——从按月分表(热点集中)升级到用户维度一致性哈希+组合分区,数据均匀分布、单用户查询只触达 1/64 数据

案例4:流处理架构 - 实时性提升

优化前:批处理计算指标

# 每小时运行的定时任务
def calculate_risk_metrics():
# 查询过去1小时的所有决策
decisions = db.query("""
SELECT * FROM decisions
WHERE created_at > NOW() - INTERVAL 1 HOUR
""")
# 批量计算指标
for decision in decisions:
metrics = calculate_metrics(decision)
db.update_metrics(decision.user_id, metrics)

优化后:流式实时计算

# 基于Flink的实时流处理
@flink.process_function
class RiskMetricsProcessor:
def __init__(self):
# 使用RocksDB存储状态
self.state = self.get_keyed_state(
"user_metrics",
ValueStateDescriptor("metrics", RiskMetrics)
)
def process_element(self, decision: Decision, ctx: Context):
# 获取当前用户状态
current_metrics = self.state.value() or RiskMetrics()
# 增量更新指标
current_metrics.decision_count += 1
current_metrics.total_amount += decision.amount
current_metrics.update_risk_score(decision)
# 使用时间窗口
if current_metrics.need_output():
ctx.collect(current_metrics)
# 更新状态
self.state.update(current_metrics)
def on_timer(self, timestamp: int, ctx: Context):
# 定期清理过期状态
if self.state.value().is_expired():
self.state.clear()

改进效果

  • 实时性:从小时级延迟降到秒级
  • 准确性:增量计算避免重复统计
  • 资源效率:内存占用降低80%

案例5:批流一体架构 - Lambda到Kappa

优化前:Lambda架构

Lambda架构问题:
- 维护成本高: 批处理和流处理两套代码
- 数据不一致: 批流结果偏差
- 复杂度高: 需要合并层处理

优化后:Kappa架构

# 统一的流处理架构
class UnifiedRiskProcessor:
def __init__(self):
self.checkpoint_interval = 30 # 30秒checkpoint
def process_stream(self, kafka_stream):
return (kafka_stream
.key_by(lambda x: x.user_id)
.window(TumblingWindow(minutes=5))
.aggregate(
RiskAggregator(),
output_mode="update"
)
.sink_to(self.multi_sink())
)
def multi_sink(self):
# 同时输出到多个下游
return MultiSink([
RedisRealTimeSink(), # 实时查询
HBaseHistorySink(), # 历史存储
ElasticSearchSink() # 搜索分析
])
def replay_history(self, start_time):
# 历史数据重放使用相同逻辑
return self.process_stream(
KafkaSource(start_position=start_time)
)
PlantUML
正在加载图表…
PlantUML diagram

图:批流一体演进——Lambda 维护批/流两套代码且结果有偏差;Kappa 用一套流处理代码同时服务实时查询、历史存储、搜索分析,历史重放复用同一逻辑

总结:DDIA带来的关键改进

量化收益

指标优化前优化后提升
系统可用性99.9%99.99%10x
决策TPS2,00020,00010x
P99延迟200ms30ms85%↓
数据延迟1小时10秒360x
运维成本5人2人60%↓

架构演进原则

  1. 渐进式改进:不追求一步到位,持续优化
  2. 数据驱动决策:基于监控数据发现瓶颈
  3. 权衡思维:没有银弹,根据场景选择合适方案
  4. 简单优先:复杂度是最大的敌人

通过DDIA的指导原则,我们成功将一个传统的单体风控系统改造成了高性能、高可用、可扩展的现代化数据密集型应用。

Continue Reading

关联文档推荐

查看全部
营销系统「资损防控」指南 — editorial cover photo降级开关治理:统一管控与一键熔断 — editorial cover photo容量治理:水位监控与弹性伸缩 — editorial cover photo
作者:archy.shawn
声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。