❝本系列每篇文章都是从一些实际生产实践需求出发,解决一些生产实践中的问题,抛砖引玉,以帮助小伙伴们解决一些实际生产问题。本篇为直播实时数据建设系列的第二篇,本篇主要介绍直播间画像实时维表建设的整个过程,如果对小伙伴有帮助的话,欢迎点赞 + 再看~ ❞
附上一篇。
生产实践 | 基于 Flink 的直播实时数据建设 (一)| 需求和架构篇
回顾上一节的「技术架构」图。
技术架构
从数据源到数据处理以及最后到数据汇部分,整个架构相对来说是比较好理解的。
但是大家的疑惑点可能就集中在三个维表的建设上,包含「主播用户画像维表,观众用户画像维表,直播间画像维表」。
技术架构
我们依然从以下几个角度的问题出发,通过分析场景,解答这几个问题来给大家介绍以上三个维表的建设过程。
首先简单介绍下,「实时 & 离线公共画像维表」中存储的内容就是一个实体的其他固有属性(比如用户实体的年龄等),在我理解这两个概念本身是抽象的概念,本文中介绍的「主播用户画像维表,观众用户画像维表,直播间画像维表」是其具体实现。
其他大佬的文章解释中会对「实时公共画像维表」 & 「离线公共画像维表」有更加深度的理解,这里我只说明我在直播实时数据建设过程中的理解~
区别
其实这两个词的区别从名字上就可以区分出来,实时公共画像维表和离线公共画像维表的最大区别就是数据建设和应用场景要求的「时效性」不同。
特点:
特点:
为什么架构图中的三类公共画像维表要按照实时和离线进行划分?为什么需要建设实时公共画像维表,离线公共画像维表不能满足需求?
这几个问题其实围绕着我们的直播实时数据建设以及应用的场景就可以展开解答。
接上篇技术架构图,其中直播实时数据需要建设的公共维表分为以下三类:
首先抛出结论:「直播间画像都是直播间的固有属性画像,直播间画像维表的建设过程是实时的」。
由于大多数直播的时长都在几小时不等,随着直播的开始,主播域观众的互动也随即产生,从而直播生产和消费的指标也开始产出,随着直播的结束,主播和观众的互动也就结束了,对应的直播生产和消费指标也就不存在了,因此直播间画像的所能提供给其他指标作为维表的价值也就快速消失了,所以直播间画像(标题,开播地址)的应用场景特点就是「时效性很强」。因此直播间画像维表对于直播生产消费指标的建设和应用来说,需要满足可实时建设、可实时查询获取的要求。
结论:「这类画像都是用户的固有属性画像,而非直播间固有属性,和直播间是非强相关的。主播 & 观众用户画像维表的建设过程可以是离线的」。
无论直播间的开播关播,直播过程中的生产消费,主播画像和观众画像基本上不会产生变动。(举例:大多数情况下,当已经判定一个用户的年龄段画像为 18 - 23 时,即使这个用户开了 10 场直播,或者这个用户观看了 10 场直播,其年龄段判定也基本不会有变化)。因此主播用户画像维表 & 观众用户画像维表对于直播生产消费指标的建设和应用来说,可以满足离线 t + 1 建设,提供数据服务进行实时获取的要求。
❝Notes: 主播 & 观众用户画像需要根据用户生产消费行为以及其他信息,使用到机器学习进行性别和年龄段等的用户画像信息判定产出。也有非常多的场景将这类画像进行实时建设,用于实时个性化推荐等。只不过本文的直播实时数据建设对于这两类画像的时效性要求较弱,所以采用了离线的方式进行建设。 ❞
直播间整个生命周期如图所示。
生命周期
实时画像维表的建设。上图中「红色」的字体为实时画像维表的建设和应用过程。
通过上文的分析,可以了解到直播间画像实时维表建设的要求如下:
因此组件选型就自然落在了高速缓存的范畴中,我们最后经过方案对比之后,选择了 redis 作为我们的实时维表的存储引擎。
使用了 redis 中的 hash 作为维表存储结构,其中直播间画像维度存储设计如下图。
维度存储
public class LiveStreamRealtimeDimBuilderJob {
public static void main(String[] args) throws Exception {
final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
DataStream<byte[]> source = SourceFactory.getSourceDataStream();
source.process(new ProcessFunction<byte[], String>() {
@Override
public void processElement(byte[] bytes, Context context, Collector<String> collector) throws Exception {
CommonModel c = CommonModel.parseFrom(bytes);
// 开播
if (c.isStartLiveStream()) {
RedisConfig
.get()
.hmset(c.getLiveStreamId()
, ImmutableMap.<String, String>builder()
.put("type", c.getType())
.put("client", c.getClient())
.put("title", c.getTitle())
.put("address", c.getAddress())
.build()
);
RedisConfig
.get()
.expire(c.getLiveStreamId(), 30 * 24 * 60 * 60);
} else if (c.isEndLiveStream()) {
// 关播
RedisConfig
.get()
.expire(c.getLiveStreamId(), 2 * 24 * 60 * 60);
}
}
});
env.execute();
}
@Data
public static class CommonModel {
private String liveStreamId; // 直播间 id
private String type; // 直播间类型
private String client; // 开播客户端
private String title; // 直播间标题
private String address; // 直播间开播地址
public static CommonModel parseFrom(byte[] bytes) {
// 逻辑根据业务逻辑判定
return null;
}
public boolean isStartLiveStream() {
// 逻辑根据业务逻辑判定
return false;
}
public boolean isEndLiveStream() {
// 逻辑根据业务逻辑判定
return false;
}
}
}
离线画像维表的建设。主要包含主播和观众的用户画像,性别,年龄等信息。如下图「蓝色」的字体为离线画像维表的应用过程。
生命周期
在产出直播间生产侧、消费侧实时数据时,使用主播 & 观众画像进行了画像维度填充。
其中离线画像维表的存储组件选型与实时相同,同为 redis,画像信息存储方式也是使用 redis hash 结构进行存储。
以 t + 1 离线的方式进行画像数据建设并进行数据同步,将建设好的全量主播和观众用户画像同步到 redis 高速缓存当中。
本文衔接上文,介绍了实时 & 离线公共画像建设的整个过程。提出几个建设的问题,以这几个问题出发,主要引出了一下三小节。
第一节简单介绍了实时 & 离线公共画像维表的概念。
第二节从数据应用场景的角度出发,介绍了为什么需要建设实时的公共画像维表。
第三节主要介绍了实时画像维表的建设过程以及详细的技术方案。
最后一节对本文进行了总结。
如果你也建设过实时画像维表,或者有相同的需求,欢迎留言或者留下你的文章链接,相互交流~