掌门教育自 2014 年正式转型在线教育以来,秉承“让教育共享智能,让学习高效快乐”的宗旨和愿景,经历云计算、大数据、人工智能、 AR/VR/MR 以及现今最火的 5G ,一直坚持用科技赋能教育。掌门教育的业务近几年得到了快速发展,特别是今年的疫情,使在线教育成为了新的风口, 也给掌门教育新的机遇。随着业务的不断扩大,特别是运营业务关系到公司整体收益,运营策略针对用户的精准投放,效果的实时监测,根据数据动态调整运营方案,所以需要运营方案小步快走,快速迭代上线,规则快速调整。从活动投放页面的 PV/UV 值统计,到数据带来 leads (名单)效果值,从活动用户参与率到名单转化效果等,无不依赖数据的支撑,特别是能快速看到数据报表,可配置化的统计分析看板,数据漏斗等就显得尤为重要。针对目前公司现状,以及前端埋点,后端各业务数据分散在各个业务数据库,业务数据实时聚合性的痛点,急需统一数据平台来整合一套完整的解决方案,提供前后端基础数据串联,数据统计报表分析,实时业务监控并且可实现可配置化,接入便捷等。Why?为什么不让 BI 大数据来做?大数据部门更加侧重于整个公司大盘数据的定制化分析和长期固定形式的统计报表,加上业务规则的灵活多变,如果业务系统侧规则一旦有变动,数据报表必须跟着变动,加上短平快类活动数据报表统计,开发周期和需要人力非常大,远远不合算。是对已经埋点的前端事件数据,后端业务数据,进行自动实时收集、拼接、分析、通过配置化提供实时报表,数据漏斗分析的系统。
1. 数据埋点
前端每个业务埋点首先需要在事件管理后台申请对应的事件码,然后通过对 h5 ,微信小程序, app 等前端的数据埋点。针对每个页面的不同触发事件,通过 eventKey 来区分不同的事件点。(数据收集维度:时间, eventKey , atcKey )' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
后端每个业务埋点首先需要在事件管理后台申请对应的事件码,并把对应的需要保存的数据字段一起设置好,再使用自研数据采集 sdk ,接入方可以自定义 json 串数据,通过公司消息中心推送,进行数据收集埋点,统一 BI-server 服务消费消息进行逻辑聚合和业务处理,最终落业务逻辑宽表和事件对应流水表。(数据收集维度:根据具体业务情况自定义实现)' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
2. 数据拼接
拼接理论:
本质上是多表 left join 。子表按照前后顺序进行连接,前一个子表的主键 + 后一个子表的外键,这样一层一层连接下去。' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
数据拼接存储流程:
现在是用一个 topic 去接收所有的消息,在消费端使用多线程消费。这样会发生消费处理的乱序问题。通过 opState 状态值来表示当前记录的完整程度,最后把有问题的记录通过定时器修复数据。现在是用一个 topic 去接收所有的消息,在消费端使用多线程消费。消息量大的时候,会发送消息消费并发的问题。通过对核心节点生成唯一键,并通过多次消息出错的消息来应对。一个节点被多个业务宽表使用。此时怎么判断处理发送过来的此节点的数据,应该放到哪个业务宽表中。1)通过查找每一个宽表,如果当前宽表查到了,则返回,否则一直向下查找。(效率差,目前已实现)
2)设置每一个宽表对应的属性值,通过属性值来查找具体的宽表。(效率高,目前未实现)
1)后台配置节点的字段,业务主键,字段的类型,怎么自动生成子表和唯一键索引?2)后台配置多个节点的串联关系,怎么自动生成宽表和唯一键索引、普通索引?3)通过获取对应的配置,动态拼接生成 ddl 语句生成子表、宽表以及对应的索引。通过解析器把对应的消息 eventKey 和子表、宽表解析到一个 map 中,进行快速定位消费消息。1)子表( mongodb , ttl 索引 1 年)根据前后子表主外键配置关联关系,存储多个子表的数据到对应的宽表中。' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
1)子表 T3 :是核心节点,数据触发插入宽表的节点。2)子表 T4,T5 :是可修改节点,数据可以直接在宽表中修改。3)子表 T1,T2 :是普通节点,必须通过 T3 往前找 T1、T2 子表中对应的数据,更新到宽表中。通过 eventKey 查找到对应的子表,根据业务主键判断,无则插入,有则修改。AggCreateTime datetime 创建时间AggUpdateTime datetime 更新时间1)当 T1、T2 普通节点的数据过来的时候,如果对应在宽表的记录不存在,则此数据不会进入宽表中。如果在宽表中有记录,则更新此宽表的记录。2)当 T3 核心节点的数据过来的时候,查找和它关联的上下游节点数据,如果对应在宽表的记录不存在,则把上下游数据插入宽表中。如果在宽表中有记录,则把上下游更新此宽表的记录。3)当 T4、T5 可修改节点的数据过来的时候,查找和它关联的上下游节点数据,如果对应在宽表的记录不存在,则此数据不会进入宽表中。如果在宽表中有记录,则把上下游更新此宽表的记录。当 opstate=0 时,全局定时器通过核心节点来再次处理关联的上下游节点数据。多个 1 :1 关系的节点,多个节点的主键和外键是一样时。此时多个节点的操作逻辑和第一个核心节点的逻辑是一样的。3. 基于本部门数据实战
' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
4. 前后端数据漏斗数据可配置化
' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
配置漏斗数据,可以根据各事件埋点(前端事件 UV /后端业务数据事件),按需筛选分步统计' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
好了,部分功能展示到此,系统内部宝藏功能静待挖掘。1)已有宽表数据累计、将来增加的多个宽表,数据量会很大,需要进行分表、分库,把冷数据备份到其它机器中,只保留需要实时查询的数据。多个宽表分到不同数据库中,提高查询效率。2)宽表的串联子节点的消息,在消息客户端通过 tag 标识, sharding 到固定的 queue 中。公共节点放到单独的队列中,减少同一张宽表的数据并发处理问题。欧俊,掌门资深后端架构师,运营中后台 leader ,关注前沿技术,负责整个运营中台设计和搭建。金仁舟,现就职于掌门运营中台,十多年互联网行业工作经验,擅长分布式、微服务、高并发、高性能、高可用系统的设计。特别鸣谢:张博勋,高丰鸣
---------- END ----------
加入掌门
欢迎大佬们加入掌门教育大家庭,一起畅谈技术,分享交流。在招职位有研发工程师/架构师( Web
前端/ Java
/ iOS
)、测试工程师/主管、音视频工程师/架构师( iOS
、安卓、 PC
端)、大数据开发工程师、运维工程师、 DBA
数据库管理员、算法工程师、逆向工程师( iOS
、安卓、 PC
端)。
投递信箱:zeying.shi@zhangmen.com 施老师。
' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
往期好文