规则集拆分策略:大规则集拆分为小规则集、规则分组与分层、规则优先级设计
规则引擎跑得慢,十有八九是规则集太大了。我见过一个生产系统,单次请求要匹配 8000 多条规则,响应时间直接飙到 3 秒多。后来我们把规则集拆成 12 个小包,响应时间降到 200 毫秒以内。说白了,规则引擎的性能瓶颈,很多时候不是引擎本身不行,而是你喂给它的规则集太「胖」了。
这一章,我们就聊聊怎么把大规则集拆小、怎么分组分层、怎么设计优先级。这些都是我这些年踩坑踩出来的经验。
为什么大规则集必须拆?
先看一个简单的数学问题。假设你有 N 条规则,引擎每来一个事实,都要遍历这些规则做条件匹配。如果规则之间没有索引优化,复杂度就是 O(N)。N 从 100 涨到 1000,匹配时间涨 10 倍。这还不算规则之间的冲突检测、优先级排序、结果合并这些开销。
我在项目中遇到过最极端的情况:一个保险核保系统,单条规则集里塞了 15000 多条规则。每次规则加载就要 40 秒,匹配一次要 6 秒。用户点一下「核保」,等半天没反应。后来我们拆成 6 个子规则集,加载时间降到 2 秒,匹配时间降到 300 毫秒。
拆分策略一:按业务维度拆
这是最直观的拆分方式。把规则按照业务领域、产品线、场景等维度切分。每个子规则集只负责一个明确的业务范围。
举个例子,一个电商平台的促销规则引擎,可以这样拆:
| 子规则集名称 | 业务范围 | 规则数量 | 触发条件 |
|---|---|---|---|
| 满减规则 | 订单满额减价 | 120 条 | 订单金额 > 0 |
| 折扣规则 | 商品折扣、品类折扣 | 200 条 | 商品 ID 不为空 |
| 优惠券规则 | 优惠券发放与核销 | 80 条 | 用户有优惠券 |
| 赠品规则 | 满额赠品、买赠 | 50 条 | 订单金额 > 阈值 |
| 会员专享规则 | 会员等级专属优惠 | 60 条 | 用户等级 > 0 |
你看,每个子规则集都有明确的触发条件。引擎先判断触发条件,只加载匹配的子规则集。这样就不用每次都加载全部 500 多条规则了。
拆分策略二:按执行阶段拆
有些规则集虽然业务上是一体的,但执行逻辑有先后依赖关系。这时候可以按阶段拆。
我记得有个风控系统,规则执行分三个阶段:
- 预筛选阶段: 快速过滤掉明显不合规的请求。比如黑名单检查、IP 地域检查。这些规则执行快,命中率高。
- 详细评估阶段: 对通过预筛选的请求做深度分析。比如用户行为评分、交易模式匹配。这些规则计算量大。
- 决策输出阶段: 根据前两个阶段的结果,生成最终决策。比如「通过」「人工审核」「拒绝」。
这样拆的好处是:大部分请求在预筛选阶段就被拦截了,根本不需要进入详细评估。系统压力大大降低。
规则分组与分层
拆完规则集之后,还要考虑怎么组织这些子规则集。我习惯用「分组 + 分层」的方式。
分组是把同一业务维度的规则放在一个组里。组内规则可以共享一些上下文数据,比如同一个事实对象、同一个结果集。
分层是给规则组定义执行顺序。比如第一层是全局规则,第二层是业务规则,第三层是特殊规则。每一层执行完后,结果可以传递给下一层。
下面这张图展示了分组与分层的结构:
规则优先级设计
优先级设计是规则引擎调优中最容易被忽视的一环。很多人觉得优先级就是给规则排个序,其实没那么简单。
优先级设计要解决三个问题:
- 冲突解决: 多条规则同时匹配时,谁说了算?
- 执行顺序: 规则之间的依赖关系怎么保证?
- 性能优化: 高优先级规则能不能提前拦截,避免低优先级规则执行?
我常用的优先级设计模式有三种:
| 模式 | 适用场景 | 实现方式 | 注意事项 |
|---|---|---|---|
| 数值优先级 | 规则之间无依赖,只需决定谁先执行 | 给每条规则分配一个整数优先级(1-1000) | 避免优先级相同,否则结果不确定 |
| 层级优先级 | 规则有明确的执行阶段 | 按层级分组,层级内再设数值优先级 | 层级间不要有循环依赖 |
| 条件优先级 | 优先级取决于运行时上下文 | 在规则条件中动态计算优先级 | 性能开销较大,需要缓存 |
实战:一个完整的拆分案例
最后,我分享一个真实的拆分案例。这是一个金融产品的规则引擎,原始规则集有 3000 多条,我们把它拆成了这样:
规则集结构(拆分后):
├── 第一层:准入规则(500 条)
│ ├── 年龄检查组(100 条)
│ ├── 身份验证组(200 条)
│ └── 信用检查组(200 条)
├── 第二层:产品匹配规则(800 条)
│ ├── 存款产品组(300 条)
│ ├── 贷款产品组(300 条)
│ └── 理财产品组(200 条)
├── 第三层:定价规则(1200 条)
│ ├── 利率计算组(500 条)
│ ├── 费用计算组(400 条)
│ └── 优惠策略组(300 条)
└── 第四层:决策输出规则(500 条)
├── 通过组(200 条)
├── 拒绝组(200 条)
└── 人工审核组(100 条)
每个子规则集都控制在 500 条以内。引擎根据用户请求的类型,只加载对应的子规则集。比如用户申请存款产品,就只加载「存款产品组」和「利率计算组」,其他组根本不加载。
结果怎么样?规则加载时间从 12 秒降到 1.5 秒,单次匹配时间从 2 秒降到 150 毫秒。系统吞吐量提升了 8 倍。
嗯,规则集拆分这件事,说白了就是「分而治之」。把大问题拆成小问题,每个小问题单独优化。你想想看,一个 10000 条规则的集合,再怎么优化也跑不过 10 个 1000 条规则的集合。这就是拆分的核心价值。