一、背景
访客论分析是常见数据分析的一种,通过如上图(Google Analytics)以比较直观的方式展现用户达到网站后各条访问路径的流失情况,帮助网站优化减少流失率。
访客路径分析有如下几个关键点:
- 用户访问的路径通常有多级,默认展开包含着陆页在内的5级路径,支持往后每点击一次展开一级路径(最高支持到10级,再往后意义不大)。
- 每级只展示top 5访问数的网页,每级路径网页之间连接线表示跳转情况。
- 指标包含top 5网页的会话数、流失数和剩余网页的会话数。
通过上述分析,要实现访客路径分析需要完成如下几项工作:
- 计算每一级所有网页的会话总数。
- 计算每一级会话数top 5的网页。
- 计算每一级两两网页之间的跳转访问数。
本文提出一种基于druid的实现方案,将上述3个查询转化为druid中的Timeseries(求总数)、TopN(求前5)、GroupBy(求两两关联)查询。
二、技术方案
数据清洗(ETL)
将用户pv流水根据,聚合成一个session会话。session会话内用户的访问流水按时间排序,取前11个分别放于维度landing_page ~ path10,ETL处理后的数据表格示例如下:
host | landing_page | path1 | path2 | ... | path10 |
---|---|---|---|---|---|
www.xxx.com | /index.html | /a | /b | ... | /e |
www.xxx.com | /product.html | /c | /d | ... | null |
数据入Druid供查询,schema设计如下
{
"type" : "index_hadoop",
"spec" : {
"ioConfig" : {
"type" : "hadoop",
"inputSpec" : {
"type" : "static",
"paths" : ""
}
},
"dataSchema" : {
"dataSource" : "",
"granularitySpec" : {
"type" : "uniform",
"segmentGranularity" : {"type":"period","period":"P1D","timeZone":"Asia/Shanghai"},
"queryGranularity" : {"type":"period","period":"P1D","timeZone":"Asia/Shanghai"},
"intervals" : []
},
"parser" : {
"type" : "string",
"parseSpec" : {
"format" : "json",
"dimensionsSpec" : {
"dimensions": [
"host",
"landing_page",
"path1",
...
"path10"
]
},
"timestampSpec" : {
"format" : "auto",
"column" : "time"
}
}
},
"metricsSpec": [
{
"name": "count",
"type": "count"
}
]
},
"tuningConfig" : {
"type" : "hadoop",
"partitionsSpec" : {
"type" : "hashed",
"targetPartitionSize" : 5000000
},
"indexSpec" : {
"bitmap" : { "type" : "roaring"},
"dimensionCompression":"LZ4",
"metricCompression" : "LZ4",
"longEncoding" : "auto"
}
}
}
}
三、具体实践
查询语句示例
计算每一级所有网页的会话总数(默认展示前5级),过滤掉为null的情况(用户只访问到上一级就跳出)。
{
"queryType": "timeseries",
"dataSource": "visit_path_analysis",
"granularity": "all",
"filter": {
"type": "and",
"fields": [{"type": "selector", "dimension": "host", "value": "www.xxx.com"}]
},
"aggregations": [
{
"type": "filtered",
"filter": {
"type": "not",
"field": { "type": "selector", "dimension": "landing_page", "value": null }
},
"aggregator": { "type": "longSum", "name": "count0", "fieldName": "count" }
},
{
"type": "filtered",
"filter": {
"type": "not",
"field": { "type": "selector", "dimension": "path1", "value": null }
},
"aggregator": { "type": "longSum", "name": "count1", "fieldName": "count" }
},
{
"type": "filtered",
"filter": {
"type": "not",
"field": { "type": "selector", "dimension": "path2", "value": null }
},
"aggregator": { "type": "longSum", "name": "count2", "fieldName": "count" }
},
{
"type": "filtered",
"filter": {
"type": "not",
"field": { "type": "selector", "dimension": "path3", "value": null }
},
"aggregator": { "type": "longSum", "name": "count3", "fieldName": "count" }
},
{
"type": "filtered",
"filter": {
"type": "not",
"field": { "type": "selector", "dimension": "path4", "value": null }
},
"aggregator": { "type": "longSum", "name": "count4", "fieldName": "count" }
}
],
"intervals": []
}
计算每一级会话数top5的网页,过滤掉为null的情况(用户只访问到上一级就跳出)。
{
"queryType": "topN",
"dataSource": "visit_path_analysis",
"granularity": "all",
"dimension": "landing_page",
"filter": {
"type": "and",
"fields": [
{"type": "selector", "dimension": "host", "value": "www.xxx.com"},
{
"type": "not",
"field": { "type": "selector", "dimension": "landing_page", "value": null }
}
]
},
"threshold": 5,
"metric": {
"type": "numeric",
"metric": "count"
},
"aggregations": [{ "type": "longSum", "name": "count", "fieldName": "count" }],
"intervals": []
}
计算每一级两两网页之间的跳转访问数,后一级的null用来计算流水数。
{
"queryType": "groupBy",
"dataSource": "visit_path_analysis",
"granularity": "all",
"dimensions": ["landing_page", "path1"],
"filter": {
"type": "and",
"fields": [
{"type": "selector", "dimension": "host", "value": "www.xxx.com"},
{
"type": "in",
"dimension": "landing_page",
"values": ["/a", "/b", "/c", "/d", "e"]
},
{
"type": "in",
"dimension": "path1",
"values": ["/f", "/g", "/h", "/i", "/j", null]
}
]
},
"aggregations": [{ "type": "longSum", "name": "count", "fieldName": "count" }],
"intervals": []
}
四、总结分析
本文提出基于Druid来做访客路径分析的方案需由多个请求来完成。
计算每一级所有网页的会话总数和计算每一级会话数top5的网页,在默认展示的时候可以先并行向druid发起请求。获取每级总会话数后再减去top5的会话数就是剩余其他网页的会话数。
当得到每一级top5的路径后,只需要相邻两级路径做GroupBy查询即可获得转化数与流水数。
当需要展示往后一级路径流转时,只需要基于当前最后一级的top5与下一级别top5做GroupBy计算即可。
从数据分布来看,大部分流水集中在前几步,往后有数据级的差距。
该方案最大挑战来着对Druid的并发请求,一个页面展示会扩大为多个Druid并发语句请求。