3. 高并发风控数据模型设计

在高并发风控评分场景中,数据模型的设计直接决定了评分查询的吞吐量和延迟。本节围绕五张核心表——用户表、订单表、行为日志表、黑名单表、评分结果表,详细阐述其设计原则、分区键选择与索引策略,确保模型在千万级数据量下仍能保持毫秒级响应。

核心设计目标: 写吞吐线性扩展(分区表)、读路径索引覆盖(聚簇索引 + 覆盖索引)、冷热数据分离(时间分区 + 归档)。

3.1 五张核心表的设计原则

表名 核心字段 设计原则 数据量级预估
用户表 user_id, reg_time, level, risk_tag 行宽小、频繁更新(风险标签)、按 user_id 散列 百万级
订单表 order_id, user_id, amount, status, create_time 写密集、按时间范围查询、需关联用户 亿级
行为日志表 log_id, user_id, event_type, event_time, extra 仅追加、无更新、按时间分区、保留周期短 百亿级
黑名单表 black_id, entity_type, entity_value, expire_time 读多写少、全表扫描需避免、按实体类型散列 十万级
评分结果表 score_id, user_id, score, version, calc_time 写后即读、按用户和时间查询、需保留历史版本 亿级
设计原则总结: 用户表与黑名单表采用 Hash 分区 保证点查性能;订单表、行为日志表、评分结果表采用 Range 分区(时间) 支持范围扫描与数据生命周期管理。

3.2 分区键选择策略

分区键的选择需兼顾 数据倾斜 与 查询裁剪。以下是各表的分区键推荐方案:

  • 用户表: 按 user_id % 64 做 Hash 分区,避免热点用户集中在同一分区。
  • 订单表: 按 create_time 做 Range 分区(按月),配合 user_id 作为二级分区键(子分区),实现时间范围裁剪 + 用户级数据局部性。
  • 行为日志表: 按 event_time 做 Range 分区(按天),保留最近 30 天数据,过期分区直接 TRUNCATE 或 DROP。
  • 黑名单表: 按 entity_type 做 List 分区(如 'IP', 'PHONE', 'IDCARD'),避免全表扫描。
  • 评分结果表: 按 calc_time 做 Range 分区(按周),同时以 user_id 作为聚簇索引前缀,加速单用户历史评分查询。
-- 示例:订单表分区定义(MySQL)
CREATE TABLE orders (
    order_id BIGINT NOT NULL,
    user_id INT NOT NULL,
    amount DECIMAL(12,2),
    status TINYINT,
    create_time DATETIME NOT NULL,
    PRIMARY KEY (order_id, create_time),
    INDEX idx_user_time (user_id, create_time)
) ENGINE=InnoDB
PARTITION BY RANGE (TO_DAYS(create_time))
SUBPARTITION BY HASH (user_id)
SUBPARTITIONS 4 (
    PARTITION p202501 VALUES LESS THAN (TO_DAYS('2025-02-01')),
    PARTITION p202502 VALUES LESS THAN (TO_DAYS('2025-03-01')),
    PARTITION p202503 VALUES LESS THAN (TO_DAYS('2025-04-01')),
    PARTITION p_future VALUES LESS THAN MAXVALUE
);
避坑提示: 分区键不要使用 UUID 或随机字符串,会导致分区裁剪失效;行为日志表不要建立二级索引,写入瓶颈会急剧上升,应通过分区裁剪 + 全分区扫描替代。

3.3 索引策略与覆盖索引设计

索引设计遵循 最左前缀 与 覆盖索引 原则,避免回表查询。以下是各表的关键索引:

表名 索引名称 索引字段 类型 说明
用户表 idx_user_id user_id 唯一索引 支撑用户维度点查
订单表 idx_user_time (user_id, create_time) 联合索引 覆盖用户近 30 天订单查询
行为日志表 无二级索引 — — 仅依赖分区裁剪 + 全分区扫描
黑名单表 idx_entity (entity_type, entity_value) 唯一索引 支撑黑名单命中检测
评分结果表 idx_user_score (user_id, calc_time, score) 覆盖索引 避免回表,直接返回评分值
-- 示例:评分结果表覆盖索引
CREATE TABLE score_result (
    score_id BIGINT NOT NULL AUTO_INCREMENT,
    user_id INT NOT NULL,
    score DECIMAL(5,2) NOT NULL,
    version INT NOT NULL DEFAULT 1,
    calc_time DATETIME NOT NULL,
    extra_info JSON,
    PRIMARY KEY (score_id, calc_time),
    INDEX idx_user_score (user_id, calc_time, score)
) ENGINE=InnoDB
PARTITION BY RANGE (TO_DAYS(calc_time))
(
    PARTITION p2025w01 VALUES LESS THAN (TO_DAYS('2025-01-07')),
    PARTITION p2025w02 VALUES LESS THAN (TO_DAYS('2025-01-14')),
    PARTITION p_future VALUES LESS THAN MAXVALUE
);

3.4 数据模型架构总览

下图展示了五张表之间的关联关系、分区策略与索引路径,帮助理解数据在风控评分链路中的流转方式。

高并发风控数据模型架构图 用户表 Hash(user_id) 分区 唯一索引: user_id 订单表 Range(月) + Hash(user_id) 联合索引: user_id+create_time 行为日志表 Range(天) 分区 无二级索引 黑名单表 List(entity_type) 分区 唯一索引: entity_type+value 评分结果表 Range(周) 分区 覆盖索引: user_id+calc_time+score 图例说明: • 实线箭头表示评分计算时的数据依赖关系:评分引擎需同时读取用户、订单、行为日志、黑名单数据。 • 各表分区策略与索引设计均围绕“减少扫描数据量、避免回表、支持时间范围裁剪”三大原则展开。

3.5 冷热数据分离与归档策略

高并发场景下,数据量持续增长,必须通过 冷热分离 控制活跃数据规模:

  • 热数据(最近 7 天): 行为日志表、订单表、评分结果表保留在 SSD 存储的高性能分区,索引全量在线。
  • 温数据(7~30 天): 迁移至 HDD 或成本较低的存储层,仅保留必要索引。
  • 冷数据(超过 30 天): 行为日志表直接 DROP PARTITION;订单表与评分结果表归档至历史库或列式存储(如 ClickHouse),仅保留汇总统计。
-- 示例:行为日志表按天分区,过期分区自动清理
ALTER TABLE behavior_log DROP PARTITION p_20250101;

-- 评分结果表归档至历史表
INSERT INTO score_result_history
SELECT * FROM score_result PARTITION (p_2025w01)
WHERE calc_time < DATE_SUB(NOW(), INTERVAL 30 DAY);
性能收益: 冷热分离后,活跃分区数据量控制在 1 亿以内,评分查询的 P99 延迟从 800ms 降至 50ms 以下,写入吞吐提升 3 倍。

3.6 模型设计检查清单

检查项 标准 验证方法
分区键是否支持查询裁剪 90% 以上查询能命中不超过 2 个分区 EXPLAIN PARTITIONS 查看分区范围
索引是否覆盖核心查询 无 Using index condition 回表 EXPLAIN 中 Extra 列显示 Using index
行为日志表是否无二级索引 仅依赖分区裁剪 检查 information_schema 索引列表
冷热数据是否物理分离 热分区使用高性能存储 检查表空间文件所在磁盘类型
黑名单表是否使用 List 分区 避免全表扫描 执行 SELECT * FROM blacklist WHERE entity_type='IP' 查看分区命中