做风控系统最直接的痛点是,决策要在几十毫秒内返回,但底层数据量和查询复杂度在不断膨胀。日增几百万条决策记录,用户画像越来越厚,规则越来越复杂,单库 MySQL 扛到八九千 QPS 就到头了。再往上压,连接池打满、慢查询堆积、响应时间飙升,整个决策链路开始抖动。
阿里公开分享过他们用分库分表把 QPS 从几万拉到几十万甚至百万级的思路。这套思路能不能搬到风控系统上,要怎么搬,搬的过程中会踩什么坑,是这篇想讲清楚的。它不是一份可以直接抄的方案,而是一套从约束出发推导的思考过程。
先把瓶颈定位准
图:四种瓶颈对应四种完全不同的解法,定位错了优化就白做
优化之前先把瓶颈看清楚,别盲目上分库分表。
风控决策链路的典型瓶颈有几个。数据量大导致查询慢,决策表日增几百万条,索引再合理,到一定规模全表扫描的代价也扛不住。热点问题,高频用户的查询集中打到同一个分片,单点过载。规则计算复杂,每条请求要匹配几十上百条规则,CPU 成了瓶颈。依赖的外部服务(画像、特征)响应慢,拖累整个决策。
这几个瓶颈的解法完全不同。数据量大要分片,热点要散列,规则复杂要预计算或者下沉到内存,外部依赖慢要异步化或者缓存。不搞清楚瓶颈在哪就一通分库分表,很可能花了大力气,瓶颈还在原地,因为根本不是数据库的问题。
阿里的思路值得借鉴的地方在于,他们先做了细致的瓶颈归因,再针对性地用「垂直拆分、水平分片、缓存、异步」这套组合拳逐个击破。盲目照搬他们的方案而不做自己的瓶颈分析,是最大的误区。
垂直拆分,先把不同访问模式的数据分开
图:垂直拆分让不同访问模式的数据互不干扰,是水平分片的基础
水平分片之前,先做垂直拆分,这是性价比最高的一步。
风控系统里有几类数据,访问模式完全不同。实时决策表,写多读少,每次请求都要写入一条决策记录,查询主要是按时间和用户排查。历史记录表,写少读更少,但数据量巨大。用户画像表,读多写少,每次决策都要查。规则配置表,读极多写极少,更新频率低。
把这些访问模式不同的数据放在同一个库里,会互相干扰。决策表的高频写入拖慢画像表的查询,历史的巨大数据量让决策表的索引维护变慢。垂直拆分就是按业务维度把表分到不同的库,让每类数据在自己的库里,互不拖累。
这步成本不高,但收益明显。它也是后续水平分片的基础,因为只有先按业务拆清楚,才能对每个业务独立地做水平扩展。跳过这步直接水平分片,会把不同访问模式的数据混在一起,分片策略很难设计。
水平分片,突破单库的 QPS 天花板
垂直拆分做完,如果单个业务的 QPS 还是不够,就要水平分片了。这是阿里的核心思路,也是最难做对的一步。
水平分片把同一张表的数据,按某个分片键散到多个库的多个表里。分片键的选择是成败关键。风控系统一般用用户 ID 做分片键,因为决策请求都带用户 ID,按用户分片能让同一个用户的请求落到同一个分片,避免跨片查询。
分片策略上,哈希分片最常用。对用户 ID 做哈希取模,均匀散到 N 个分片。好处是分布均匀,不会有热点。代价是扩容麻烦,分片数变了,所有数据要重新分布。所以分片数一开始要规划好,按未来两三年的容量预估,一次定到位,别频繁扩容。
热点是个绕不开的问题。哈希分片能把数据均匀分布,但消除不了「业务上的热点」。某些高频用户(比如大商户)的请求量远超平均,他们的请求集中打到一个分片,那个分片就过载了。解决思路是热点识别和特殊处理,把已知的热点用户单独路由到独立的资源池,不参与普通分片。
分片前一个 SQL join 搞定的事,分片后可能要查多个分片再在应用层合并。风控决策链路如果依赖跨用户的数据聚合,分片后就会很痛。所以分片键和查询模式必须匹配,查询都带分片键,尽量做单分片查询。需要在设计阶段就把所有查询路径过一遍,确认它们都能落到单分片,分片方案才成立。
多级缓存,把读压力挡在数据库之外
光分片不够,风控决策的高频读还得靠缓存挡。这部分可以借鉴蜂控平台的实践。
蜂控查 TB 级发票数据,用的是 ES 做辅助索引先拿到 Rowkey,再回 HBase 大宽表取明细。这种「索引和存储分离」的设计,底层逻辑就是把读压力从重存储挪到轻索引上。
风控系统的多级缓存可以这样做。一级是本地缓存(比如 Caffeine),放最热的规则和高频用户画像,TTL 短,命中快。二级是 Redis 集群,放用户特征和实时指标,容量大,命中稍慢但远快于 DB。三级是分片后的数据库,只做最终持久化和兜底。
读请求层层往下穿透,大部分在缓存层就命中了,真正打到 DB 的很少。Matrix 营销系统的人群底座优化就是这么做的,缓存粒度细化到单个人群 ID 维度,叠 Caffeine 二级缓存,命中率从 40% 拉到 99%,上游策略 QPS 从 1300 扛到 2800。这套思路完全适用于风控的画像和特征缓存。
缓存的一致性要处理好。风控数据不能容忍长时间不一致,否则基于过期数据做决策会误判。监听 binlog 旁路刷新缓存,接受秒级延迟,是吞吐和一致性之间比较平衡的方案。
异步化,把写压力从同步链路挪走
决策的写入不要走同步链路。决策结果算出来之后,发到消息队列异步持久化,同步链路只负责算决策和返回结果,不承担写 DB 的延迟。
这样决策的响应时间只取决于「读缓存加规则计算」,不受 DB 写入慢的影响。写 DB 的压力被 MQ 削峰,DB 按自己的节奏消费,不会被打爆。这是典型的「读写分离加上异步化」组合,在风控这种读多写也多但要低延迟的场景里几乎是标配。
异步化的代价是一致性窗口。决策结果异步落库,短时间内有「决策已经做出但还没持久化」的窗口。如果这时候系统挂了,可能丢决策记录。所以异步落库要有补偿机制,比如本地消息表保证最终一致,不能裸着发 MQ。
降级和监控,扛住极端情况
优化做到一定程度,要为极端情况准备降级方案。
QPS 超过系统承受能力时,自动降级到简化规则集,牺牲一些精度换可用性。错误率飙升时,熔断部分非核心检查,保住核心的防欺诈决策。这些降级开关要提前设计好,平时不启用,关键时刻一键切换。
监控要覆盖几个关键指标。各分片的 QPS 是否均衡,有没有热点分片。缓存命中率是不是在预期范围,跌破阈值要报警。主从复制延迟是否可控,延迟太大会读到旧数据。热点用户的 QPS 占比,识别新出现的热点及时处理。
一个务实的演进路径
图:四阶段递进,每步验证收益再往下走,Phase 2 成本最高要慎重
把上面这些组合起来,一个务实的演进路径是分阶段推进。
第一阶段垂直拆分,把不同业务的数据分库,互不干扰,成本不高收益明显。第二阶段对瓶颈业务做水平分片,按用户 ID 哈希分片,分片数一次规划到位。第三阶段上多级缓存,把读压力挡在 DB 之外,这是 QPS 提升最显著的一步。第四阶段异步化写入,把同步链路的写压力挪走,进一步降低延迟。
每一步都要验证收益再往下走,别一上来就全做。优化的投入产出比要算账,Phase 2 的水平分片成本很高(数据迁移、双写过渡、跨片查询),只有前面几步做完还扛不住才值得投入。
风控系统的 QPS 优化没有银弹,它是垂直拆分、水平分片、多级缓存、异步化、降级监控这几招的组合运用。借鉴阿里的思路,不是抄他们的方案,而是学他们「先定位瓶颈再针对性出招」的方法论。把这个方法论用到自己的约束上,推出的方案才是适合自己的方案。








