项目简介:
本项目主要用于互联网电商企业中,使用Spark技术开发的大数据统计分析平台,对电商网站的各种用户行为(访问行 为、购物行为、广告点击行为等)进行复杂的分析。用统计分析出来的数据,辅助公司中的PM(产品经理)、数据分析师以及管理人员分析现有产品的情况,并根据用户行为分析结果持续改进产品的设计,以及调整公司的战略和业务。最终达到用大数据技术来帮助提升公司的业绩、营业额以及市场占有率的目标。
项目主要采用目前大数据领域较为流行的Spark技术堆栈。采用了Spark技术生态栈中最常用的三个技术框架,Spark Core、Spark SQL和Spark Streaming,进行离线计算和实时计算业务模块的开发。实现了包括用户访问session分析、页面单跳转化率统计、热门商品离线统计、广告流量实时统计4个业务模块。
主要的功能和模块:
用户访问session分析:该模块主要是对用户访问session进行统计分析,包括session的聚合指标计算、按时间比例随机抽取session、获取每天点击、下单和购买排名前N的品类、并获取topN品类的点击量排名前N的session。该模块可以让产品经理、数据分析师以及企业管理层形象地看到各种条件下的具体用户行为以及统计指标,从而对公司的产品设计以及业务发展战略做出调整。主要使用Spark Core实现。
页面单跳转化率统计:该模块主要是计算关键页面之间的单步跳转转化率,涉及到页面切片算法以及页面流匹配算法。该模块可以让产品经理、数据分析师以及企业管理层看到各个关键页面之间的转化率,从而对网页布局,进行更好的优化设计。主要使用Spark Core实现。
热门商品离线统计:该模块主要实现每天统计出各个区域的top3热门商品。然后使用Oozie进行离线统计任务的定时调度;使用Zeppelin进行数据可视化的报表展示。该模块可以让企业管理层看到公司售卖的商品的整体情况,从而对公司的商品相关的战略进行调整。主要使用Spark SQL实现。
广告流量实时统计:该模块负责实时统计公司的广告流量,包括广告展现流量和广告点击流量。实现动态黑名单机制,以及黑名单过滤;实现滑动窗口内的各城市的广告展现流量和广告点击流量的统计;实现每个区域每个广告的点击流量实时统计;实现每个区域top3点击量的广告的统计。主要使用Spark Streaming实现。
架构图如下:
项目用到的算法
该项目用到的是:按照时间比例抽取session算法按照时间比例抽取session算法
正是因为各个时间段内的用户访问量的不同,那我们使用对应的数据做一个聚合统计分析,则显的不是很准确,举个例子,我们不能一个处于业务高峰期的数据流量去衡量处于业务低谷期的业务需求。
又因为我们要从全量session中抽取一部分的session,比如1000条的session,那么我不就不应该从业务量峰值中获取,也不能冲业务低谷值中获取,要想用抽取的这一部分数据去准去的衡量全量的数据,那么我们就应该每一个阶段,每一个部分都应该抽取相应比例的数据。
样本个数/容量:就是要抽取的事物的个数
样本:要抽取的事物组成的集体
总体:所有事物的整体
要从学校2000人中,抽取200个人,参加活动,要求每个方向都要有对人的学生,怎么才能做到想对公平的人员分配呢?
最简单的就是按照每个学科方向学员个数占总体的比重来进行抽取。
x就是应该抽取的人数(session数)
Session块的DAO层(数据访问层)
ISessionAggrStatDao接口:
import com.aura.bigdata.analysis.domain.session.SessionAggrStat;
import java.util.List;
public interface ISessionAggrStatDao {
void insert(SessionAggrStat sas);
void insertBatch(List<SessionAggrStat> list);
}
用来聚合统计用户在某一个页面上的停留时间,利用QueryRunner来实现查询
其中insert用于单条插入/更新,insertBatch用于List集合对象的插入更新。
public class SessionAggrStatDaoImpl implements ISessionAggrStatDao {
private QueryRunner qr = new QueryRunner(DBCPUtils.getDataSource());
private String sql = "INSERT INTO session_aggr_stat VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; //向表 session_aggr_stat 中插入数据
@Override
public void insert(SessionAggrStat sas) {
try {
//同时抛出异常
qr.update(sql, sas.getTask_id(), sas.getSession_count(),
sas.getVisit_len_1s_3s_ratio(), sas.getVisit_len_4s_6s_ratio(),
sas.getVisit_len_7s_9s_ratio(), sas.getVisit_len_10s_30s_ratio(),
sas.getVisit_len_30s_60s_ratio(), sas.getVisit_len_1m_3m_ratio(),
sas.getVisit_len_3m_10m_ratio(), sas.getVisit_len_10m_30m_ratio(),
sas.getVisit_len_30m_ratio(), sas.getStep_len_1_3_ratio(),
sas.getStep_len_4_6_ratio(), sas.getStep_len_7_9_ratio(),
sas.getStep_len_10_30_ratio(), sas.getStep_len_30_60_ratio(),
sas.getStep_len_60_ratio());
} catch (SQLException e) {
e.printStackTrace();
}
}
@Override
public void insertBatch(List<SessionAggrStat> list) {
//对象的个数保存在list里面,然后对list的对象进行操作
Object[][] params = new Object[list.size()][];
for (int i = 0; i < list.size(); i++) {
SessionAggrStat sas = list.get(i);
Object[] obj = {
sas.getTask_id(),sas.getSession_count(),
sas.getVisit_len_1s_3s_ratio(), sas.getVisit_len_4s_6s_ratio(),
sas.getVisit_len_7s_9s_ratio(), sas.getVisit_len_10s_30s_ratio(),
sas.getVisit_len_30s_60s_ratio(), sas.getVisit_len_1m_3m_ratio(),
sas.getVisit_len_3m_10m_ratio(), sas.getVisit_len_10m_30m_ratio(),
sas.getVisit_len_30m_ratio(), sas.getStep_len_1_3_ratio(),
sas.getStep_len_4_6_ratio(), sas.getStep_len_7_9_ratio(),
sas.getStep_len_10_30_ratio(), sas.getStep_len_30_60_ratio(),
sas.getStep_len_60_ratio()
};
params[i] = obj;
}
try {
qr.batch(sql, params);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
其结果储存到表session_aggr_stat当中
CREATE TABLE IF NOT EXISTS `session_aggr_stat` (
`task_id` int(11) NOT NULL,
`session_count` int(11) DEFAULT NULL,
`visit_len_1s_3s_ratio` double DEFAULT NULL,
`visit_len_4s_6s_ratio` double DEFAULT NULL,
`visit_len_7s_9s_ratio` double DEFAULT NULL,
`visit_len_10s_30s_ratio` double DEFAULT NULL,
`visit_len_30s_60s_ratio` double DEFAULT NULL,
`visit_len_1m_3m_ratio` double DEFAULT NULL,
`visit_len_3m_10m_ratio` double DEFAULT NULL,
`visit_len_10m_30m_ratio` double DEFAULT NULL,
`visit_len_30m_ratio` double DEFAULT NULL,
`step_len_1_3_ratio` double DEFAULT NULL,
`step_len_4_6_ratio` double DEFAULT NULL,
`step_len_7_9_ratio` double DEFAULT NULL,
`step_len_10_30_ratio` double DEFAULT NULL,
`step_len_30_60_ratio` double DEFAULT NULL,
`step_len_60_ratio` double DEFAULT NULL,
PRIMARY KEY (`task_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
类似的,还有
SessionDetailDaoImpl,用于更新和插入抽取session中的各项明细。
import com.aura.bigdata.analysis.dao.session.ISessionDetailDao;
import com.aura.bigdata.analysis.domain.session.SessionDetail;
import com.aura.bigdata.analysis.util.DBCPUtils;
import org.apache.commons.dbutils.QueryRunner;
import java.sql.SQLException;
import java.util.List;
public class SessionDetailDaoImpl implements ISessionDetailDao {
private QueryRunner qr = new QueryRunner(DBCPUtils.getDataSource());
String sql = "INSERT INTO session_detail VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
@Override
public void insert(SessionDetail entity) {
try {
qr.update(sql, entity.getTaskId(), entity.getUserId(), entity.getSessionId(),
entity.getPageId(), entity.getActionTime(), entity.getSearchKeyword(),
entity.getClickCategoryId(), entity.getClickProductId(), entity.getOrderCategoryIds(),
entity.getOrderProductIds(), entity.getPayCategoryIds(), entity.getPayProductIds());
} catch (SQLException e) {
e.printStackTrace();
}
}
@Override
public void insertBatch(List<SessionDetail> list) {
Object[][] params = new Object[list.size()][];
for (int i = 0; i < list.size(); i++) {
SessionDetail entity = list.get(i);
Object[] obj = {
entity.getTaskId(), entity.getUserId(), entity.getSessionId(),
entity.getPageId(), entity.getActionTime(), entity.getSearchKeyword(),
entity.getClickCategoryId(), entity.getClickProductId(), entity.getOrderCategoryIds(),
entity.getOrderProductIds(), entity.getPayCategoryIds(), entity.getPayProductIds()
};
params[i] = obj;
}
try {
qr.batch(sql, params);
} catch (