风控指标体系:核心指标与数据采集

各位同学,咱们今天聊点实在的。做实时风控,你上来就写规则、上模型,那是空中楼阁。你得先搞清楚一件事——你在衡量什么?

说白了,风控体系的第一步,不是代码,是指标。没有指标,你连系统好不好用、规则有没有效都说不清楚。我见过太多团队,一上来就堆规则,结果上线后异常率飙升,但根本不知道是哪个环节出了问题。嗯,这就是典型的「指标缺失症」。

核心指标:PV、UV、转化率、异常率

实时风控里,有四个指标你绕不开。它们就像汽车的仪表盘,缺一个你都开不稳。

指标 全称 实时含义 典型用途
PV Page View / 事件次数 单位时间内事件发生的总次数 衡量流量规模、系统压力
UV Unique Visitor / 独立用户 单位时间内去重后的用户数 衡量用户覆盖、黑产团伙规模
转化率 Conversion Rate 目标事件数 / 总事件数 衡量业务健康度、规则误杀率
异常率 Anomaly Rate 被判定为异常的事件数 / 总事件数 衡量风控规则有效性、攻击强度

你可能会问:「PV 和 UV 不是运营指标吗?跟风控有什么关系?」

关系大了。举个例子:某个支付接口的 PV 在 5 秒内从 1000 飙到 10 万,但 UV 只从 800 涨到 900。这说明什么?说明同一批用户在疯狂刷接口——典型的撞库攻击特征。我当年在金融项目里就踩过这个坑,当时只看了 PV 没看 UV,结果被黑产用 200 个账号刷了 50 万次登录,直到数据库连接池被打爆才发现。

核心原则:PV 看规模,UV 看覆盖。两者结合,才能判断流量是「真用户」还是「假机器」。

指标计算口径:别让数字骗了你

指标定义清楚了,但计算口径不一致,照样白搭。我见过最离谱的事:同一个「转化率」,业务部门算的是「支付成功 / 下单」,风控部门算的是「通过风控 / 所有请求」,结果两个数字差了 30%,开会吵了一下午。

所以,计算口径必须统一。实时风控里,常见的口径问题有这几个:

  • 时间窗口:是滑动窗口还是固定窗口?比如「过去 5 分钟」和「当前整点 5 分钟」,结果可能差一倍。
  • 去重粒度:UV 是按设备 ID 去重,还是按用户 ID 去重?黑产可以伪造设备 ID,但用户 ID 通常更可靠。
  • 分母定义:异常率的分母是「所有请求」还是「通过基础校验的请求」?如果包含被限流的请求,异常率会被稀释。
  • 延迟容忍:实时计算允许多少延迟?5 秒内算「实时」,还是 1 分钟内也算?这直接影响指标准确性。

避坑指南:我曾经在一个项目中,因为时间窗口用了「固定整点窗口」,导致每分钟的异常率在整点切换时出现断崖式下跌。后来改成滑动窗口,才真正反映了实时趋势。记住:实时风控,一定要用滑动窗口。

埋点与数据采集:指标的源头

指标定义好了,口径统一了,接下来就是数据从哪来。埋点,就是风控系统的「眼睛」。

埋点分两种:

  • 前端埋点:在 App、H5、小程序等客户端采集用户行为。比如点击、滑动、页面停留时间。
  • 服务端埋点:在后端接口中采集业务数据。比如登录请求、下单请求、支付回调。

做实时风控,我个人习惯优先用服务端埋点。为什么?因为前端数据可以被篡改。黑产可以 hook App、伪造请求参数,你采集到的「用户行为」可能全是假的。服务端数据虽然也有伪造风险,但至少经过了你的业务逻辑校验,可信度高很多。

当然,前端埋点也不是完全没用。比如设备指纹、IP 归属地、浏览器特征,这些信息服务端拿不到,必须靠前端采集。我的建议是:前端埋点做辅助,服务端埋点做主力。

数据采集的实时链路

埋点数据怎么实时传到风控系统?这里有一条标准链路:

客户端/服务端 → 消息队列(Kafka) → 实时计算引擎(Flink) → 风控决策引擎 → 指标存储(Redis/ClickHouse)

每一步都有讲究:

  • 消息队列:必须保证高吞吐和低延迟。Kafka 是标配,分区数要根据 PV 预估来设置。我一般按每秒 10 万事件来设计分区。
  • 实时计算引擎:Flink 是主流。注意设置 checkpoint 和状态后端,防止任务重启后指标丢失。
  • 指标存储:实时指标用 Redis,历史指标用 ClickHouse。Redis 的 key 要设计好过期时间,不然内存会爆。

小技巧:埋点数据一定要加「事件时间」和「采集时间」两个字段。事件时间是用户操作的真实时间,采集时间是服务端收到数据的时间。两者差值超过 10 秒的数据,建议直接丢弃或标记为「延迟数据」。否则你的实时指标会被历史数据污染。

指标体系知识图谱

下面这张图,帮你把本章的核心逻辑串起来。建议保存下来,做架构设计时对照着看。

实时风控指标体系知识图谱 核心指标 PV(事件次数) UV(独立用户) 转化率 异常率 计算口径:时间窗口 | 去重粒度 | 分母定义 | 延迟容忍 埋点方式:前端埋点 + 服务端埋点 采集链路:Kafka → Flink → Redis/ClickHouse 核心原则:PV看规模,UV看覆盖,两者结合判断流量真伪 实时风控必须使用滑动窗口,前端埋点做辅助,服务端埋点做主力

埋点数据模型设计

最后,给你一个我常用的埋点数据模型模板。直接拿去用,少踩很多坑。

{
  "event_id": "uuid",                // 事件唯一ID,用于去重
  "event_type": "login",             // 事件类型:login、order、payment
  "event_time": 1700000000000,       // 事件发生时间(毫秒时间戳)
  "collect_time": 1700000000500,     // 服务端采集时间(毫秒时间戳)
  "user_id": "u_12345",             // 用户ID
  "device_id": "d_abcde",           // 设备ID
  "ip": "192.168.1.1",              // 客户端IP
  "ua": "Mozilla/5.0...",           // User-Agent
  "extra": {                        // 扩展字段,按业务需求添加
    "login_type": "password",
    "risk_level": "low"
  }
}

注意几个关键点:

  • event_id 必须全局唯一,用于消息队列的去重。Kafka 虽然保证分区内有序,但不保证全局不重复。
  • event_time 和 collect_time 的差值,就是你的「数据延迟」。我一般要求不超过 5 秒,超过的直接丢弃。
  • extra 字段用 JSON 格式,方便扩展。但注意不要塞太多字段,否则 Flink 的序列化开销会很大。

个人经验:埋点字段不是越多越好。我见过一个团队,一个埋点事件塞了 50 个字段,结果 Flink 任务的内存直接爆了。我的建议是:核心字段不超过 10 个,扩展字段不超过 5 个。如果业务需要更多信息,用关联查询去补,别一股脑全塞进消息里。