-
Notifications
You must be signed in to change notification settings - Fork 81
数据模型
ghost_sa 的目标用户群是DAU小于10万或每日数据量在1亿条以下的小微产品团队,整个数据模型和架构都是为了适应这些特点进行设计的。
这样的团队具有如下特点:
1.对成本敏感,不具备在物理上隔离开发测试生产环境的资源,也没有专职DBA或数据开发团队可以维护成套的大数据基础架构,对数据容量也较为敏感。
2.产品本身没有跑出来,总结不出稳定的用户和增长模型,存在随着业务探索,模型需要跟着业务灵活变化的需求。同时还有多个产品在一起跑,存在资源共享的情况。每一条数据都格外宝贵。
3.护城河浅,不能承受薅羊毛或竞品的技术攻击。又需要大量推广的动作,又雇不起具有技术背景的运营和市场。需要All in One的技术一体解决方案。
4.数据量小,对性能需求低。
所以ghost_sa在模型设计时,在成本,灵活性,性能之间,优先选择了成本和灵活性。性能从技术视角上是比较弱的。
1.project_list是整个项目的名单。shortcut系列可以脱离项目独立运行。access_control,recall_blacklist,status_code 是为了项目之间可以共享资源,scheduler_jobs 是为了可以在各项目之间统一协调资源。
2.所有功能表都具备记录项目的字段。在需要进行隔离的时候,可以从业务层面进行隔离。在不许要隔离的时候,可以一次查询一个索引解决问题。降低数据库压力。
1.{project},{project}_device,{project}_user,{project}_properties 四张表完成了跟踪的基本逻辑,这也是与神策官方列存储模式不同,实现低成本与灵活性的关键。
2.usergroup系列表都是用户分群的表,需要独立运行scheduler服务才能使用。
3.noti系列表都是推送相关的表,需要配置发送渠道并独立运行messenger服务。
Q:为什么神策是一张event表,鬼策的event表去哪了?
A:ghost_sa的{project}与神策的event表功能一致。区别在于存储方式,ghost_sa是行存储json存信息,神策是列存储大宽表。双方各自用适应自己库特性的方式节约存储空间。ghost_sa没有神策字段一旦确定,更改需要找客户技术支持的问题。当然,也失去了神策对字段类型的校验。换回来的是在不同的事件里,可以使用相同的字段名。更符合神策,事件,动作,属性的埋点模型。
Q:为什么神策就一张user表。ghost_sa增加了一个device表?
A:神策的user表实际上是ghost_sa的user表,拼接了device表。ghost_sa选择拆开,主要考虑了如下方面: 1.神策模型上提到的局限,通过user和device表拆开,可以低效率的解决。而不是不能解决。
2.处于1的理由,ghost_sa不预先洗好数据,这与ghost_sa的目标用户特性有关,这也是ghost_sa解决1提到局限的方法。 一旦用户不选择device作为分析路径,选择与event表做关联进行唯一性识别。user表的体积通常是device表的1/50。虽然这对分布式数据库来说影响不大,不过关联查询时仍然可以提高性能。
3.user表与device表或event表配合,可以灵活的区分设备视角或用户视角的分析。
Q:为什么user表的主键是区分lib的,但是device表的主键只有distinct_id?那用户更换设备之后不是找不到历史信息了?
A:回答这个问题我们需要先理解神策SDK的数据上报内容。为了方便理解,我们简化神策SDK上报的内容为distinct_id,lib,数据。其中,distinct_id在用户未登录的时候,使用的是匿名id,在用户登陆后(触发login事件),会使用用户的登录id。lib是上报数据的SDK名称。数据就是上报的具体内容,比如device表里的其他信息(其他信息的更新逻辑可以参考新增参数及设置指南,根据不同的配置,会有不同的更新逻辑以适应业务场景)。 我们假设用户A有2台手机M1,M2,2台电脑P1,P2。他们依次登录你的产品。那么device表和user表的记录如下。
device表:
distinct_id | lib | 数据 | created_at | updated_at |
---|---|---|---|---|
M1 | iOS | 根据device策略不同有差异 | M1第一次出现的时间 | M1登录前最后一个动作的时间 |
A | js | 根据device策略不同有差异 | A第一次登录的时间 | A最后一个动作的时间 |
M2 | iOS | 根据device策略不同有差异 | M2第一次出现的时间 | M2登录前最后一个动作的时间 |
P1 | js | 根据device策略不同有差异 | P1第一次出现的时间 | P1登录前最后一个动作的时间 |
p2 | js | 根据device策略不同有差异 | P2第一次出现的时间 | P2登录前最后一个动作的时间 |
user表:
distinct_id | lib | original_id | all_profile_json | created_at | updated_at |
---|---|---|---|---|---|
A | iOS | M1 | {} | M1第一次登录的时间 | M1最后一次登录的时间 |
A | iOS | M2 | {} | M2第一次登录的时间 | M2最后一次登录的时间 |
A | iOS | {iOS平台上A的用户信息} | iOS上首次更新用户信息的时间 | iOS上最后一次更新用户信息的时间 | |
A | js | P1 | {} | P1第一次登录的时间 | P1最后一次登录的时间 |
A | js | P2 | {} | P2第一次登录的时间 | P2最后一次登录的时间 |
A | js | {js平台上A的用户信息} | js上首次更新用户信息的时间 | js上最后一次更新用户信息的时间 |
因为神策SDK在关联用户信息的时候,并不传递original_id,所以如果产品支持多端登录,无法判断信息是哪台设备更新的。只能根据判断到用户和lib。
通过这两张表,几乎可以在不查询event表的情况下,快速获得用户关键时间点的各种信息,如:
场景 | 目的 | 方法 |
---|---|---|
用户全平台首次出现时间和信息 | 找全平台首次出现时间点 | user表根据created_at查到M1,再device表里查created_at和不带latest的信息。 |
用户首次登录时间和信息 | 找全平台首次激活的时间 | device表created_at和不带latest的信息。 |
用户最后产品使用时间和信息 | 用户生命周期管理 | device表updated_at和latest开头的信息。 |
用户最后一次登录的时间 | 产品登录策略设计,风控 | user表updated_at。 |
用户登录设备数量 | 风控,如VIP账号出租 | user表根据distinct_id数original_id去重。 |
同设备登录多个用户 | 风控,如刷量、爬虫 | user表根据original_id数disticnt_id去重。 |
用户换机频率 | 用户使用习惯,产品定位,推广间隔 | user表根据distinct_id或original_id算created_at的间隔。 |
账号异常登录 | 风控,如账号被盗 | user表根据distinct_id找到original_id,再到device表里找ip_city。如果device.updated最大的设备或当前distinct_id,与其他设备的ip_city中城市不一致,则账号异常登录可能性 |
Q:device表的内容更新机制是什么?
A:参考新增参数及设置指南
Q:event表既没有主键也没有独立索引,如何避免重复
A:参考去重原理,在去重的同时,保留全部信息,实现对去重效果的review,在神策调整sdk策略后,能及时发现差异,跟进迭代。
project_list <--项目列表,使用setup.py安装新项目时会自动创建,并增加条目。如果已存在,只增加条目。记录项目的创建日期和过期日期。过期的项目上报的数据会被丢弃(还没加这个功能)。项目兜底的access_control阈值也在这里维护。项目是否开启定时器任务也在这里控制。
shortcut <--短链列表,短链创建和解析依赖此表
shortcut_history <--短链解析历史,记录短链解析的记录和是否成功。
shortcut_read <--站外阅读记录,使用二维码生成接口显示二维码和站外跟踪图片加载跟踪图片时。会在这个表产生记录。
access_control <--接入控制存储的列表。在列表里的,达到条件后可以被接口抓到。
mobile_ad_list <--第三方广告跟踪创建的地址(用户自行创建后会在这里更新)
mobile_ad_src <--第三方广告跟踪的源地址(适配后会在这里更新)
recall_blacklist <--召回列表的分级控制黑名单。提供全局或限定的黑名单。降低整体召回成本和投诉风险。
recall_blacklist_history <--召回黑名单被命中的事件(可以解释没推送的原因,也可以用来算给公司降低了多少风险,你懂的)
recall_blacklist_reason <--召回黑名单入选原因(黑名单增加,修改的记录)
scheduler_jobs <--定时任务列表,支持优先级调度
status_code <--状态码字典
{project} <--埋点数据,每上报一条,都写入这个表。功能等同于神策的events表
{project}_device <--设备表,每上报一条数据,会根据数据里的内容更新表里的信息。上报的distinct_id和lib作为主键,同一个distinct_id在一个sdk里唯一。
{project}_user <--用户关联表,神策SDK里用了Profile相关操作时,会触发更新本表。distinct_id,lib,map_id,original_id作为主键,映射记录唯一。功能等同于神策的users表,并可以解决神策不能多对多关联的问题。配合device表使用,可以实现神策的users表的筛选功能。
{project}_properties <--埋点属性表,该表可以通过configs/admin.py里的use_properties值控制是否启用(默认开启)。该表用来存储每个event都包含多少个属性。这个对使用全埋点的用户比较有用,因为全埋是由神策SDK创建的字段。而自己设计埋点的用户,因为有埋点表,这个功能意义不大,可以关闭该表降低数据库负载。
{project}_usergroup_plan <--用户分群。scheduler程序会根据project表的开关,读取这个表,来生成计划任务到scheduler_jobs
{project}_usergroup_list <--用户分群列表。scheduler执行plan后,会创建list,并写入具体data
{project}_usergroup_data <--用户分群详情。scheduler执行后写入的具体数据。应用分群时也是取这一层。
{project}_noti_temple <--消息模板。该项目支持的推送模板
{project}_noti_group <--调用套模板方法后。无论是scheduler触发,trigger触发,还是通过接口触发套模板。都会先创建一个推送组,再写入推送数据。
{project}_noti <--具体的推送消息。messenger程序会逐条检查未发送的消息。确定到达定时后,会逐条发送(支持优先级管理)。