一、项目背景:广告公司为何需要大数据分析系统?
在数字化广告时代,广告公司每天要处理海量数据 —— 从网易新闻客户端等媒体平台获取的 “用户 - 广告” 交互日志(如曝光、点击、转化)、广告竞价数据(如出价、成交价)、行业站点数据(如不同行业广告的投放效果)等。这些数据量级通常达到TB 级,传统 Excel、MySQL 等工具根本无法高效处理,更无法支撑 “精准投放策略优化”“作弊流量识别”“投放效果实时监控” 等核心需求。
举个具体场景:某广告公司为美妆品牌投放广告,需要知道 “哪些地域的用户点击率最高”“哪个时间段投放 ROI(投入产出比)最优”“是否存在同一用户短时间内频繁点击的作弊行为”—— 这些问题都需要通过大数据技术对海量日志进行离线分析与实时计算才能解答。
因此,本项目的核心目标是:基于 Hadoop 生态搭建一套广告数据分析系统,实现 “日志采集→数据处理→分析可视化” 全流程自动化,为广告公司提供高效、精准、实时的决策支持,解决 “数据处理慢、投放效果难评估、作弊流量难识别” 三大痛点。
二、核心技术栈:Hadoop 生态 + 前后端协同
系统围绕 “数据采集→存储→计算→分析→可视化” 全链路选型,兼顾 “海量数据处理能力” 与 “业务落地实用性”,具体技术栈如下:
| 技术层级 | 具体选型 | 核心作用 |
|---|---|---|
| 数据采集层 | Logstash + Kafka | Logstash 对接网易新闻客户端接口,实时采集 “用户 - 广告” 交互日志(如点击、曝光),过滤无效数据后,一份写入 Kafka(供实时计算),一份写入 HDFS(供离线分析);Kafka 作为消息队列,缓冲高并发日志,避免数据丢失。 |
| 数据存储层 | HDFS + Hive | HDFS(Hadoop 分布式文件系统)存储 TB 级原始日志与处理后数据,支持横向扩展(增加节点即可扩容);Hive 作为数据仓库,将 HDFS 中的结构化数据映射为 “表”,支持类 SQL(HiveSQL)查询,无需编写复杂 MapReduce 代码。 |
| 数据计算层 | MapReduce + Spark | MapReduce 用于离线批处理(如统计 “近 7 天各行业广告点击率”),适合处理大规模静态数据;Spark(含 Spark Streaming)用于实时计算(如识别 “1 小时内同一用户点击同一广告超 20 次的作弊流量”),计算速度比 MapReduce 快 10 倍。 |
| 任务调度层 | Airflow | 统一调度离线 / 实时计算任务(如每天凌晨 2 点执行 “前一天广告数据离线统计”、每秒执行 “实时作弊流量检测”),支持可视化监控任务状态,失败时自动发送告警邮件,避免人工运维遗漏。 |
| 分析与可视化层 | Druid + ECharts + Node.js | Druid 作为实时分析引擎,预加载 Hive 中的离线数据并构建索引,支持 “亿级数据查询延迟<4 秒”(满足前端可视化的实时性需求);ECharts 实现前端图表(如点击率走势图、地域投放效果热力图);Node.js 开发后端接口,对接 Druid 与前端,返回分析结果。 |
| 数据质量监控层 | 自定义规则引擎 | 基于 SQL 脚本定义监控规则(如 “某时段日志量突然下降 50% 需告警”“广告点击量为负数属于异常数据”),实时检测数据完整性、准确性,异常时通过邮件 / 短信通知开发人员。 |
三、系统核心设计:从需求到架构的落地
3.1 需求分析:明确系统要解决什么问题?
通过与广告公司运营人员、算法工程师沟通,梳理出3 大核心功能需求与2 大非功能需求:
3.1.1 功能需求(3 大模块)
| 功能模块 | 核心子功能 | 面向用户 | 业务价值 |
|---|---|---|---|
| 广告日志管理模块 | 1. 日志采集:实时采集网易新闻客户端广告日志;2. 数据质量监控:过滤重复 / 错误日志,检测数据延迟 / 异常 | 数据开发工程师 | 保证数据源质量,为后续分析提供 “干净” 的数据;避免因日志缺失 / 错误导致分析结果失真。 |
| 数据信息处理模块 | 1. 离线计算:统计近 7 天 / 30 天广告曝光、点击、收入、CTR(点击率);2. 实时计算:识别作弊流量(如短时间高频点击)、统计实时热门广告 Top10 | 数据开发工程师、算法工程师 | 离线计算支撑 “长期投放策略优化”(如月度 ROI 分析);实时计算支撑 “实时作弊拦截”(避免广告主浪费预算)。 |
| 数据统计分析模块 | 1. 数据加载:将离线 / 实时计算结果加载到 Druid;2. 在线分析:可视化展示各维度指标(地域、时段、行业);3. 数据下载:导出分析结果为 Excel | 运营人员、算法工程师 | 运营人员通过图表直观查看投放效果(如 “北京地区美妆广告 CTR 最高”);算法工程师下载数据用于模型调参(如优化广告推荐算法)。 |
3.1.2 非功能需求
- 性能需求:TB 级数据场景下,近 7 天数据查询延迟<4 秒(满足运营人员实时查看需求);
- 安全性需求:仅允许广告公司内部员工访问(通过 VPN + 账号密码双重验证),禁止外网访问,保护广告数据隐私(如广告主出价、用户行为数据)。
3.2 系统架构设计:五层架构全链路解耦
各层核心职责:
- 数据源层:网易新闻客户端等媒体平台,提供 “用户 - 广告” 交互日志、广告竞价数据;
- 数据采集层:Logstash 采集 + 过滤日志,Kafka 缓冲高并发数据;
- 数据存储与计算层:HDFS 存储数据,Hive 构建数据仓库,MapReduce/Spark 执行计算;
- 数据服务层:Druid 提供实时查询接口,Node.js 开发后端 API;
- 前端可视化层:ECharts 展示图表(如点击率走势图、地域投放效果),支持数据下载。
关键设计亮点:
- 离线 + 实时双链路:离线链路(HDFS+Hive+MapReduce)处理大规模静态数据(如月度分析),实时链路(Kafka+Spark Streaming)处理高并发动态数据(如作弊流量识别),兼顾 “深度分析” 与 “实时响应”;
- 数据质量监控嵌入全流程:从 Logstash 过滤无效日志,到自定义规则引擎检测数据延迟 / 异常,再到 Hive 表分区校验,每一步都确保数据 “干净、完整、及时”,避免 “垃圾数据进,垃圾结果出”。
3.3 数据库设计:Hive 表结构核心设计
系统采用 Hive 作为数据仓库,所有表均为外部表(删除表时仅删除元数据,HDFS 中原始数据保留,避免误删),并按 “时间戳” 分区(如dt=20231227),便于按日期查询。核心表结构如下:
1. 广告行业站点表(data_ctr_site_account_mn)
存储不同行业、不同站点的广告投放核心指标,用于分析 “行业差异”“站点效果”:
| 字段名 | 数据类型 | 说明 | 示例值 |
|---|---|---|---|
| account_id | String | 广告账号 ID(唯一标识) | "ad_2023_001" |
| site_id | String | 媒体站点 ID(如网易新闻) | "site_163_news" |
| industry_level1_id | String | 一级行业 ID(如 “美妆”) | "industry_beauty" |
| industry_level2_id | String | 二级行业 ID(如 “护肤”) | "industry_skincare" |
| impression_cnt | BigInt | 曝光次数 | 10000 |
| click_cnt | BigInt | 点击次数 | 500 |
| ctr | Double | 点击率(click_cnt/impression_cnt) | 0.05(5%) |
| income | Double | 广告收入(元) | 2000.0 |
| rpm | Double | 千次展示收益(income/impression_cnt*1000) | 200.0(每千次曝光赚 200 元) |
| dt | String | 分区字段(日期) | "20231227" |
2. 广告曝光点击统计表(data_imp_click_stat)
存储广告桶(Bucket)级别的曝光、点击数据,用于细粒度效果分析:
| 字段名 | 数据类型 | 说明 | 示例值 |
|---|---|---|---|
| bucket_no | String | 广告桶编号(用于数据分片) | "bucket_003" |
| pre_ctr | Double | 先验点击率(预测值) | 0.045(4.5%) |
| actual_ctr | Double | 实际点击率 | 0.05(5%) |
| click_cnt | BigInt | 点击次数 | 300 |
| impression_cnt | BigInt | 曝光次数 | 6000 |
| update_time | Timestamp | 数据更新时间 | "2023-12-27 18:30:00" |
3. 广告竞价请求表(bidrequest)
存储广告竞价过程数据,用于分析 “竞价策略效果”(如出价高低与成交率的关系):
| 字段名 | 数据类型 | 说明 | 示例值 |
|---|---|---|---|
| bid_request_id | String | 竞价请求 ID(唯一) | "bid_20231227_12345" |
| ad_id | String | 广告 ID | "ad_beauty_001" |
| advertiser_id | String | 广告主 ID | "adv_skincare_002" |
| bid_price | Double | 广告主出价(元) | 2.5 |
| win_price | Double | 最终成交价(元) | 2.2 |
| device_type | String | 设备类型(手机 / 平板) | "mobile" |
| network_type | String | 网络类型(4G/5G/WiFi) | "5G" |
| dt | String | 分区字段(日期) | "20231227" |
3.4 核心模块实现:从日志采集到可视化
3.4.1 模块 1:广告日志采集(Logstash + Kafka)
核心目标:实时采集网易新闻客户端的 “用户 - 广告” 交互日志,过滤无效数据,分发给 HDFS 与 Kafka。实现步骤:
-
Logstash 配置:
- Input 插件:使用
logstash-input-http对接网易新闻客户端的日志接口,接收 JSON 格式日志(包含user_id、ad_id、action(点击 / 曝光)、time等字段); - Filter 插件:使用
logstash-filter-json校验 JSON 格式,logstash-filter-ruby编写脚本过滤无效数据(如user_id为空、time格式错误的日志); - Output 插件:使用
logstash-output-webhdfs将过滤后的日志写入 HDFS(路径:/user/ad/data/logs/dt=%{+YYYYMMdd}),同时使用logstash-output-kafka写入 Kafka 主题ad_log_topic。
关键配置代码示例(logstash.conf):
input { http { port => 8080 # 监听网易新闻客户端日志推送端口 codec => json # 日志格式为JSON } } filter { if [action] not in ["click", "impression"] { drop {} # 过滤非点击/曝光的无效动作 } if [user_id] == "" { drop {} # 过滤user_id为空的日志 } date { match => ["time", "yyyy-MM-dd HH:mm:ss"] # 解析时间字段 target => "@timestamp" } } output { # 写入HDFS webhdfs { host => "hadoop-master" # HDFS主节点 port => 50070 path => "/user/ad/data/logs/dt=%{+YYYYMMdd}/ad_log_%{+HH}.log" user => "hadoop" } # 写入Kafka kafka { bootstrap_servers => "kafka-1:9092,kafka-2:9092" # Kafka集群地址 topic_id => "ad_log_topic" codec => json } } - Input 插件:使用
-
Kafka 集群部署:搭建 3 节点 Kafka 集群,配置
replication-factor=2(每个分区副本数为 2),partitions=8(主题分区数为 8),确保高可用与高并发处理(支持每秒 10 万条日志写入)。
3.4.2 模块 2:数据信息处理(离线 + 实时计算)
核心目标:离线计算 “长期投放效果”,实时识别 “作弊流量”,为后续分析提供数据支撑。
(1)离线计算(MapReduce + Hive)
场景:统计 “近 7 天各一级行业的 CTR、RPM(千次展示收益)”,支撑 “行业投放策略优化”。实现步骤:
-
Hive 表创建:基于 HDFS 中的日志数据,创建外部表
ad_imp_click_daily(按dt分区); -
HiveSQL 编写:通过 HiveSQL 实现统计逻辑,Hive 自动将 SQL 转换为 MapReduce 任务执行:
sql
SELECT industry_level1_id, # 一级行业ID SUM(impression_cnt) AS total_imp, # 总曝光 SUM(click_cnt) AS total_click, # 总点击 ROUND(SUM(click_cnt)/SUM(impression_cnt), 4) AS ctr, # 点击率 ROUND(SUM(income)/SUM(impression_cnt)*1000, 2) AS rpm # 千次展示收益 FROM ad_imp_click_daily WHERE dt BETWEEN '20231221' AND '20231227' # 近7天 GROUP BY industry_level1_id; -
Airflow 调度:在 Airflow 中创建 DAG(有向无环图),配置 “每天凌晨 2 点执行该 HiveSQL”,执行结果写入 Hive 表
ad_industry_7d_analysis,供后续可视化调用。
(2)实时计算(Spark Streaming + Kafka)
场景:识别 “1 小时内同一用户点击同一广告超 20 次的作弊流量”,避免广告主为无效点击付费。实现步骤:
-
Kafka 数据消费:Spark Streaming 作为 Kafka 消费者,订阅
ad_log_topic,批量拉取日志(批次间隔 5 秒); -
作弊逻辑判断:使用 Spark RDD API 编写逻辑:
- 按
user_id、ad_id分组,统计 “1 小时内的点击次数”; - 若点击次数>20,标记为 “作弊流量”,写入 Redis 黑名单(
key=user_id:ad_id,value=cheat);
- 按
-
实时拦截:广告投放系统实时查询 Redis,若用户 - 广告组合在黑名单中,拒绝展示该广告。
核心代码示例(Scala):
import org.apache.spark.streaming.kafka010._
import org.apache.spark.streaming.{Seconds, StreamingContext}
import redis.clients.jedis.Jedis
val ssc = new StreamingContext(sparkConf, Seconds(5)) // 5秒批次间隔
// 订阅Kafka主题ad_log_topic
val kafkaParams = Map[String, Object](
"bootstrap.servers" -> "kafka-1:9092,kafka-2:9092",
"key.deserializer" -> classOf[StringDeserializer],
"value.deserializer" -> classOf[StringDeserializer],
"group.id" -> "ad_cheat_detection_group", // 消费者组ID
"auto.offset.reset" -> "latest", // 从最新offset开始消费
"enable.auto.commit" -> (false: java.lang.Boolean) // 手动提交offset,避免重复消费
)
val topics = Array("ad_log_topic")
val kafkaDStream = KafkaUtils.createDirectStream[String, String](
ssc,
LocationStrategies.PreferConsistent,
ConsumerStrategies.Subscribe[String, String](topics, kafkaParams)
)
// 1. 解析Kafka日志(JSON格式),过滤出“点击”动作的日志
val clickLogDStream = kafkaDStream.map(record => {
val json = JSON.parseFull(record.value()).get.asInstanceOf[Map[String, Any]]
(
json("user_id").toString, // 用户ID
json("ad_id").toString, // 广告ID
json("time").toString // 点击时间(yyyy-MM-dd HH:mm:ss)
)
}).filter(_._3.nonEmpty) // 过滤时间为空的无效日志
// 2. 按(user_id, ad_id)分组,统计1小时内点击次数
val cheatDStream = clickLogDStream
.map { case (userId, adId, time) => ((userId, adId), 1) } // 映射为(键:用户-广告,值:1)
.reduceByKeyAndWindow(
_ + _, // 累加点击次数
Seconds(3600), // 窗口长度:1小时
Seconds(5) // 滑动间隔:5秒(与批次间隔一致)
)
.filter(_._2 > 20) // 点击次数>20,标记为作弊流量
// 3. 将作弊流量写入Redis黑名单(过期时间1小时)
cheatDStream.foreachRDD(rdd => {
rdd.foreachPartition(partition => {
val jedis = new Jedis("redis-master", 6379) // 连接Redis
partition.foreach { case ((userId, adId), count) =>
val key = s"cheat:${userId}:${adId}"
jedis.setex(key, 3600, count.toString) // 设置过期时间1小时
}
jedis.close()
})
// 手动提交Kafka offset
kafkaDStream.asInstanceOf[CanCommitOffsets].commitAsync()
})
ssc.start()
ssc.awaitTermination()
3.4.3 模块 3:数据统计分析与可视化(Druid + ECharts)
核心目标:将离线 / 实时计算结果以 “图表” 形式展示给运营人员,支持 “多维度查询” 与 “数据下载”,直观呈现广告投放效果。
(1)Druid 预加载数据(支撑实时查询)
Druid 是实时分析引擎,能实现 “亿级数据查询延迟<4 秒”,核心步骤:
- 数据导入:将 Hive 表
ad_industry_7d_analysis(近 7 天行业分析数据)通过 Druid 的Hadoop Index Task导入,按 “industry_level1_id(一级行业 ID)”“dt(日期)” 构建索引; - 查询配置:定义 Druid 查询接口(如
/druid/v2/sql),支持 SQL 查询(如 “查询美妆行业近 7 天每天的 CTR”),返回 JSON 格式结果。
(2)前端可视化(ECharts + Node.js)
前端页面基于 “HTML + Thymeleaf + ECharts” 开发,后端通过 Node.js 提供 API 接口,对接 Druid 与前端,核心功能包括:
-
多维度查询:运营人员可选择 “时间范围(如近 7 天 / 近 30 天)”“维度(如行业、地域、设备类型)”,点击 “查询” 后,前端通过 AJAX 调用 Node.js 接口(如
/api/ad/analysis),获取 Druid 返回的统计结果; -
图表展示:使用 ECharts 绘制 “点击率走势图”“各行业 RPM 对比柱状图”“地域投放效果热力图” 等,示例如下:
- 广告素材类型分析图(图 5.2):展示 “图片广告”“视频广告”“原生广告” 的点击率对比,帮助运营人员选择最优素材类型;
- 各渠道订单转换率图(图 5.3):对比 “网易新闻”“知乎”“微博” 等渠道的广告转换率,指导渠道投放预算分配;
-
数据下载:支持将查询结果导出为 Excel(如 “近 7 天美妆行业各时段投放数据”),便于运营人员做线下总结。
前端核心代码示例(ECharts 点击率走势图):
<div id="ctrTrendChart" style="width: 100%; height: 400px;"></div> <script> // 初始化ECharts实例 var myChart = echarts.init(document.getElementById('ctrTrendChart')); // 调用Node.js接口获取数据 $.ajax({ url: '/api/ad/ctrTrend', type: 'POST', data: JSON.stringify({ startTime: '20231221', endTime: '20231227', industryId: 'industry_beauty' // 美妆行业 }), contentType: 'application/json', success: function(data) { // 数据格式:{dates: ["20231221", ...], ctrs: [0.05, 0.052, ...]} var option = { title: { text: '美妆行业近7天点击率走势图' }, xAxis: { type: 'category', data: data.dates }, yAxis: { type: 'value', formatter: '{value}%' // 显示为百分比 }, series: [{ name: '点击率', type: 'line', data: data.ctrs.map(ctr => ctr * 100), // 转换为百分比 smooth: true // 平滑曲线 }] }; myChart.setOption(option); } }); </script>
四、系统测试:功能 + 性能双验证
为确保系统上线后稳定运行,从 “功能正确性” 与 “性能达标性” 两方面进行测试,测试环境与工具如下:
4.1 测试环境
| 环境类型 | 硬件配置 | 软件配置 |
|---|---|---|
| 服务器集群 | 3 台物理机(每台:CPU 32 核、内存 128GB、硬盘 2TB) | Ubuntu 18.04、Hadoop 3.2.2、Spark 3.1.2、Kafka 2.8.0、Redis 6.2.5 |
| 测试客户端 | 笔记本(CPU i7、内存 16GB) | Chrome 浏览器、Postman、Jmeter 5.4.3 |
4.2 功能测试
4.2.1 广告日志采集测试
验证 Logstash 能否正确采集、过滤日志,并写入 HDFS 与 Kafka:
- 测试步骤:1. 模拟网易新闻客户端发送 1000 条日志(含 50 条无效日志:
user_id为空、action为 “无效”);2. 查看 HDFS 目标路径是否生成日志文件,Kafka 主题是否有数据;3. 统计有效日志数量(应为 950 条)。 - 测试结果:HDFS 写入 950 条有效日志,Kafka 消费到 950 条数据,无效日志被正确过滤,测试通过。
4.2.2 广告在线分析测试
验证前端查询与图表展示是否正确:
- 测试步骤:1. 选择 “时间范围 20231221-20231227”“行业美妆”,点击查询;2. 对比前端展示的 “点击率” 与 Hive 表
ad_industry_7d_analysis中的数据;3. 导出 Excel,检查数据是否完整。 - 测试结果:前端图表数据与 Hive 表一致(如 20231225 美妆行业 CTR 为 5.2%),Excel 导出数据完整,测试通过。
4.3 非功能测试
4.3.1 安全性测试
验证系统是否仅对内部员工开放:
- 测试场景 1:使用外部网络(非公司内网)访问系统,观察是否能打开登录页;
- 测试场景 2:使用未授权账号(未由管理员分配)登录,观察是否提示 “无权限”;
- 测试结果:外部网络无法访问系统(需安装公司 VPN),未授权账号登录失败,测试通过。
4.3.2 性能测试(Jmeter)
验证系统在高并发查询下的响应时间:
- 测试场景:模拟 70 个用户(公司广告部门最大人数)同时查询 “近 7 天各行业 RPM”,统计平均响应时间;
- 测试结果:平均响应时间 2.8 秒(<4 秒的需求标准),95% 响应时间<3.5 秒,无请求失败,测试通过。
五、项目总结与经验复盘
5.1 项目成果
- 技术落地:基于 Hadoop 生态实现 “日志采集→数据处理→可视化” 全流程自动化,支持 TB 级数据处理,实时计算延迟<5 秒,离线查询延迟<4 秒,满足广告公司核心业务需求;
- 业务价值:上线后,广告投放策略优化效率提升 60%(如通过地域分析将美妆广告预算向 “一线城市” 倾斜,CTR 提升 15%),作弊流量识别率达 90%(避免广告主每月浪费约 10 万元无效点击费用);
- 可扩展性:系统架构模块化(如日志采集层可新增 Flume 对接更多媒体平台,计算层可新增 Flink 支持更实时的分析),便于后续迭代。
5.2 踩过的坑与解决方案
-
Kafka 数据积压
- 问题:高峰期(如晚间 8 点用户活跃时段)日志量突增,Kafka 分区消费速度跟不上,导致数据积压;
- 解决:将 Kafka 主题
ad_log_topic的分区数从 8 增加到 16,同时增加 Spark Streaming 消费者实例数(与分区数匹配),提升消费速度。
-
Druid 查询延迟高
- 问题:查询 “近 30 天全行业数据” 时,响应时间达 6 秒(超 4 秒需求);
- 解决:对 Druid 索引进行优化 —— 按 “
dt(日期)” 分桶,查询时仅扫描指定日期的桶;同时增加 Druid 服务器内存(从 64GB 提升到 128GB),减少磁盘 IO。
-
数据质量监控遗漏
- 问题:某次日志采集时,因网易新闻客户端接口异常,日志中 “
ad_id” 字段为空,未被过滤,导致后续分析数据错误; - 解决:在 Logstash Filter 中新增 “
ad_id非空校验”,同时在数据质量监控规则引擎中添加 “ad_id为空告警”,异常时实时通知开发人员。
- 问题:某次日志采集时,因网易新闻客户端接口异常,日志中 “
5.3 给学弟学妹的建议
- 优先跑通 “最小可行系统” 不要一开始就追求 “多平台对接”“复杂算法”,先实现 “单媒体日志采集→简单离线分析→基础图表” 的最小流程(如仅对接网易新闻客户端,统计点击率),再逐步扩展功能,避免因目标过大导致中途卡住。
- 重视数据质量,避免 “垃圾进垃圾出” 大数据系统中,数据质量比模型复杂度更重要 —— 一定要在采集、处理各环节加入 “过滤、校验、监控”(如 Logstash 过滤无效字段、Hive 表分区校验、自定义规则引擎告警),否则后续分析结果全是错误的。
- 多动手调试,少空想理论Hadoop、Spark 等分布式技术配置复杂(如 Kafka 分区与消费者组匹配、Spark 内存分配),很多问题只有实际部署才会遇到(如 HDFS 块损坏、YARN 资源不足),建议多在 Linux 环境中实操,遇到问题查官方文档或 Stack Overflow。
六、附:项目资源获取
完整项目资源包含:
- 代码:Logstash 配置文件、Spark Streaming 实时计算代码、Node.js 后端 API 代码、前端 ECharts 代码;
- 文档:环境搭建手册(Hadoop/Spark/Kafka 部署步骤)、数据库设计文档(Hive 表结构 SQL)、测试用例(功能 / 性能测试脚本);
- 演示视频:系统功能演示(日志采集→查询→可视化→下载)。
👉 关注,可获取完整代码与资源,助力你的 Hadoop 相关毕业设计落地。若有技术问题,欢迎在 Issues 区交流!