3. 风控数据源接入:从HDFS读取风控日志、从Kafka读取实时风控事件、从JDBC读取用户画像数据

风控实验的数据源通常呈现“批量 + 实时 + 维度”的三元结构。本章聚焦三种核心接入方式:HDFS 承载历史风控日志的批量读取,Kafka 提供实时风控事件的流式消费,JDBC 用于查询用户画像等维度表。三种方式在 Spark 中分别对应 spark.read、spark.readStream 和 spark.read.format("jdbc")。

实验前提:确保 Spark 集群已正确配置 HDFS 客户端、Kafka 客户端库(如 spark-sql-kafka-0-10)以及对应数据库的 JDBC 驱动(如 mysql-connector-java)。

3.1 从 HDFS 读取风控日志

风控日志通常以 Parquet、ORC 或压缩文本格式存储在 HDFS 上。Spark 通过 DataSource API 实现高效批量读取,支持分区裁剪和谓词下推。

3.1.1 典型读取方式

存储格式 读取方式 适用场景
Parquet spark.read.parquet("hdfs://...") 列式存储,压缩率高,风控日志首选
ORC spark.read.orc("hdfs://...") Hive 生态兼容,支持 ACID
JSON / CSV spark.read.json("hdfs://...") 调试或小规模数据
Text spark.read.text("hdfs://...") 原始日志行解析

3.1.2 代码示例:读取 Parquet 格式风控日志

// 读取 HDFS 上的风控日志(Parquet 格式)
val riskLogDF = spark.read
  .option("basePath", "/data/risk_logs")
  .parquet("/data/risk_logs/dt=2025-03-01/*.parquet")

riskLogDF.printSchema()
riskLogDF.createOrReplaceTempView("risk_logs")
性能要点:使用 basePath 配合分区目录(如 dt=)可让 Spark 自动推断分区列,避免全量扫描。

3.2 从 Kafka 读取实时风控事件

实时风控场景下,Kafka 作为事件总线承载交易、登录、设备指纹等流式数据。Spark Structured Streaming 通过 readStream 接口以微批或连续模式消费 Kafka 消息。

3.2.1 核心配置参数

参数 说明 推荐值
kafka.bootstrap.servers Kafka 集群地址 node1:9092,node2:9092
subscribe 订阅的 topic 列表 risk_event
startingOffsets 起始偏移量策略 earliest / latest
maxOffsetsPerTrigger 每批次最大消息数 10000
failOnDataLoss 数据丢失时是否失败 false(生产环境建议 true)

3.2.2 代码示例:消费实时风控事件

val kafkaStreamDF = spark.readStream
  .format("kafka")
  .option("kafka.bootstrap.servers", "node1:9092,node2:9092")
  .option("subscribe", "risk_event")
  .option("startingOffsets", "earliest")
  .option("maxOffsetsPerTrigger", 10000)
  .option("failOnDataLoss", "false")
  .load()

// 解析 Kafka 消息的 key/value(二进制格式需转字符串)
val parsedStreamDF = kafkaStreamDF
  .selectExpr("CAST(key AS STRING)", "CAST(value AS STRING)", "topic", "partition", "offset", "timestamp")

parsedStreamDF.printSchema()
注意:Kafka 消息的 key 和 value 默认是二进制类型,必须显式转换为 STRING 或使用 from_json 解析为结构化字段。

3.3 从 JDBC 读取用户画像数据

用户画像数据(如年龄、职业、历史逾期次数)通常存储在 MySQL、PostgreSQL 或 Oracle 中。Spark 通过 JDBC 连接器以分区并行方式读取,避免单点压力。

3.3.1 分区读取策略

参数 作用 示例
partitionColumn 分区列(通常为数字或日期) user_id
lowerBound 分区下界 1
upperBound 分区上界 10000000
numPartitions 并行分区数 10
fetchSize 每次拉取行数 10000

3.3.2 代码示例:读取用户画像表

val userProfileDF = spark.read
  .format("jdbc")
  .option("url", "jdbc:mysql://db_host:3306/risk_db?useSSL=false")
  .option("dbtable", "user_profile")
  .option("user", "risk_app")
  .option("password", "******")
  .option("driver", "com.mysql.cj.jdbc.Driver")
  .option("partitionColumn", "user_id")
  .option("lowerBound", 1)
  .option("upperBound", 10000000)
  .option("numPartitions", 10)
  .option("fetchSize", 10000)
  .load()

userProfileDF.createOrReplaceTempView("user_profile")
最佳实践:务必设置 partitionColumn 和 numPartitions,否则 Spark 会使用单线程读取,导致 OOM 或超时。

3.4 三种数据源接入架构总览

下图展示了风控实验数据接入的整体架构:HDFS 提供批量历史日志,Kafka 提供实时事件流,JDBC 提供维度画像,三者最终汇入 Spark 计算层进行特征加工与模型推理。

风控数据源接入架构总览 HDFS 风控日志 批量 / Parquet / ORC Kafka 实时事件 流式 / JSON / Avro JDBC 用户画像 维度 / MySQL / PG Spark 计算层 批量读取 + 流式消费 + 维度关联 特征加工 / 模型推理 / 结果存储 三种数据源统一接入 支持批流一体处理

3.5 三种接入方式对比总结

维度 HDFS 风控日志 Kafka 实时事件 JDBC 用户画像
数据性质 批量历史数据 实时流式数据 静态维度数据
读取方式 spark.read spark.readStream spark.read.format("jdbc")
典型格式 Parquet / ORC JSON / Avro 关系表行
分区策略 目录分区 + 文件拆分 Kafka 分区 + 微批 JDBC 分区列 + numPartitions
容错机制 文件级重试 偏移量管理 + checkpoint 连接池 + 重试
适用阶段 离线特征 / 回溯分析 实时决策 / 事件驱动 画像补充 / 维度扩展
实验建议:在同一个 SparkSession 中,可以同时注册 HDFS 表、Kafka 流表和 JDBC 表,然后通过 SQL 或 DataFrame API 进行关联分析,实现“批流一体”的风控特征计算。