4. 商户数据模型设计:核心表结构与索引优化

在高并发评分场景下,数据模型是吞吐量与准确性的基石。本章围绕商户风险评分所需的五张核心表,从字段设计、分表策略到索引优化进行系统性讲解,确保模型在千万级商户、亿级流水下仍能保持毫秒级响应。

4.1 商户基础信息表(merchant_base)

该表存储商户静态属性,写入低频、读取高频,是评分引擎的“字典”。

字段名类型说明索引策略
merchant_idBIGINT UNSIGNED商户唯一标识,主键聚簇索引
merchant_nameVARCHAR(128)商户名称—
industry_codeSMALLINT行业分类编码二级索引
register_dateDATE注册日期二级索引
statusTINYINT0-正常 1-冻结 2-注销二级索引(过滤活跃商户)
risk_level_initTINYINT初始风险等级 1-5—
extra_jsonJSON扩展属性(法人、地址等)虚拟列 + 索引(按需)
设计要点:使用 extra_json 存储非结构化扩展字段,避免频繁 DDL;对 JSON 中高频查询字段(如法人身份证号)建立虚拟列索引,兼顾灵活性与查询性能。

4.2 交易流水表(transaction_flow)

交易流水是评分计算的核心数据源,写入量极大(日均千万~亿级),需按时间维度分表。

  • 分表策略:按 trans_date 做 RANGE 分区,每月一张物理表(transaction_flow_202501)。
  • 写入优化:使用批量 INSERT(每批 500~1000 行),关闭二级索引实时维护(先入表后建索引或使用在线 DDL)。
  • 查询模式:按商户 ID + 时间范围查询,避免全表扫描。
字段名类型说明索引策略
trans_idBIGINT UNSIGNED流水主键,自增聚簇索引
merchant_idBIGINT UNSIGNED商户 ID联合索引前缀
trans_timeDATETIME(3)交易时间(毫秒精度)联合索引第二列
trans_amountDECIMAL(18,2)交易金额—
trans_typeTINYINT1-消费 2-退款 3-充值—
trans_statusTINYINT0-成功 1-失败 2-风控拦截—
device_fingerprintVARCHAR(64)设备指纹二级索引(反欺诈查询)
-- 推荐索引:覆盖评分引擎最频繁的查询
ALTER TABLE transaction_flow_202501 ADD INDEX idx_merchant_time (merchant_id, trans_time);
ALTER TABLE transaction_flow_202501 ADD INDEX idx_device (device_fingerprint);

4.3 风险事件表(risk_event)

记录商户触发的各类风控事件(如频繁退款、异地登录、大额异常),用于特征衍生与历史回溯。

  • 写入特点:事件驱动写入,单次写入量小但并发高。
  • 保留周期:事件数据保留 180 天,超期归档至冷存储(如 HBase/OSS)。
  • 查询特点:按商户 ID 查询近期事件列表,用于实时评分特征计算。
字段名类型说明索引策略
event_idBIGINT UNSIGNED事件主键聚簇索引
merchant_idBIGINT UNSIGNED商户 ID联合索引前缀
event_typeSMALLINT事件类型编码联合索引第二列
event_timeDATETIME(3)事件发生时间联合索引第三列
event_detailVARCHAR(512)事件详情 JSON—
risk_score_impactDECIMAL(5,2)该事件对评分的扣分值—
-- 核心索引:按商户查询近期事件
ALTER TABLE risk_event ADD INDEX idx_merchant_type_time (merchant_id, event_type, event_time);

4.4 黑白名单表(risk_list)

黑白名单用于快速拦截或放行,要求极低延迟(< 5ms),通常结合缓存使用。

  • 存储策略:MySQL 作为持久化层 + Redis 作为缓存层(TTL 3600s)。
  • 名单类型:商户级黑名单、设备级黑名单、IP 级黑名单。
  • 过期机制:名单可设置过期时间,过期后自动失效。
字段名类型说明索引策略
list_idBIGINT UNSIGNED主键聚簇索引
list_typeTINYINT1-黑名单 2-白名单联合索引前缀
target_typeTINYINT1-商户 2-设备 3-IP联合索引第二列
target_valueVARCHAR(64)具体值(商户ID/设备指纹/IP)联合索引第三列
expire_timeDATETIME过期时间,NULL 表示永久二级索引(清理过期数据)
reasonVARCHAR(256)加入原因—
-- 唯一索引:防止重复名单
ALTER TABLE risk_list ADD UNIQUE INDEX uk_target (list_type, target_type, target_value);

4.5 评分结果表(risk_score_result)

存储每次评分计算的输出结果,用于报表、监控与人工审核。

  • 写入频率:每次评分请求写入一条记录,写入量 = 评分 QPS。
  • 查询模式:按商户 ID 查询最新评分结果,或按时间范围拉取评分趋势。
  • 数据保留:保留 90 天,超期归档至分析型数据库(如 ClickHouse)。
字段名类型说明索引策略
result_idBIGINT UNSIGNED主键聚簇索引
merchant_idBIGINT UNSIGNED商户 ID联合索引前缀
score_timeDATETIME(3)评分时间联合索引第二列
final_scoreDECIMAL(5,2)最终风险评分 0-100—
risk_levelTINYINT风险等级 1-5二级索引
score_detail_jsonJSON各维度得分详情—
model_versionVARCHAR(32)评分模型版本号—
-- 覆盖索引:查询商户最新评分
ALTER TABLE risk_score_result ADD INDEX idx_merchant_score_time (merchant_id, score_time DESC);
索引优化避坑:不要对 score_detail_json 建立索引;不要在事务流水表上建立超过 5 个二级索引,否则写入性能会急剧下降。优先使用联合索引覆盖查询,避免回表。

4.6 数据模型整体架构图

下图展示了五张核心表之间的关联关系与数据流向,帮助理解评分引擎如何串联各表数据。

商户风险评分数据模型架构 商户基础信息表 merchant_base 交易流水表 transaction_flow 风险事件表 risk_event 黑白名单表 risk_list 评分结果表 risk_score_result 实时评分引擎 特征计算 → 模型推理 → 结果写入 数据流向说明 • 评分引擎读取 4 张源表 • 计算后写入评分结果表

4.7 索引优化核心原则

在高并发场景下,索引设计直接决定数据库吞吐能力。以下是必须遵守的优化原则:

  1. 覆盖索引优先:让索引包含查询所需的所有字段,避免回表。例如 idx_merchant_time 覆盖商户 ID + 时间范围查询。
  2. 联合索引最左匹配:将区分度最高的字段放在最左侧。商户 ID 作为查询入口,始终放在联合索引第一位。
  3. 避免冗余索引:删除长期未被使用的索引,减少写入开销。可通过 performance_schema 或慢查询日志分析。
  4. 索引下推(ICP):MySQL 5.6+ 自动启用,联合索引中无法完全匹配的查询条件会在存储引擎层过滤,减少回表行数。
  5. 分区裁剪:交易流水表按时间分区后,查询语句必须带上时间条件,让优化器直接裁剪无关分区。
实战案例:某支付平台将交易流水表二级索引从 7 个优化为 3 个联合索引后,写入 TPS 从 8,000 提升至 22,000,查询 P99 延迟从 120ms 降至 18ms。核心变化是删除了单字段索引 idx_trans_type 和 idx_trans_status,改用 idx_merchant_time 覆盖 90% 的评分查询。

4.8 分库分表策略

当单表数据量超过 5000 万行或写入 QPS 超过 1 万时,必须引入分库分表。推荐策略如下:

表名分片键分片策略分片数量
transaction_flowmerchant_id一致性哈希32 库 × 32 表
risk_eventmerchant_id一致性哈希16 库 × 16 表
risk_score_resultmerchant_id一致性哈希16 库 × 16 表
merchant_basemerchant_id哈希取模8 库(不拆分表)
risk_listtarget_value哈希取模8 库(不拆分表)
分片键选择:所有评分查询都基于 merchant_id,因此将其作为分片键可以保证同一商户的数据落在同一分片,避免跨分片 JOIN。黑白名单表按 target_value 分片,确保名单查询也能单分片完成。