上面一篇我们介绍了表格存储新功能Stream, 下面我们展开说一些场景,看看有了Stream后,哪些我们常见的应用场景可以更高效的设计和实现。
直播用户行为分析和存储
场景描述
现在视频直播非常火热,假如我们使用TableStore记录用户的每一次进入房间和离开房间,房间内的操作记录等,并希望根据用户的最近的观看记录,更新直播推荐列表。给主播提供近期收看其直播的用户的属性特征,帮助主播优化直播内容迎合观众。
表结构设计
主键顺序 | 名称 | 类型 | 值 | 备注 |
---|---|---|---|---|
1 | partition_key | string | md5(user_id)前四位 | 为了负载均衡 |
2 | user_id | string/int | 用户id | 可以是字符串也可以是长整型数字 |
3 | room_id | string/int | 房间Id | 可以使字符串也可以是长整型数字 |
4 | timestamp | int | 时间戳 | 使用长整型,64位,足够保存毫秒级别的时间戳 |
数据存储示例
设计好表结构后,我们看下具体如何存储:
比如原始数据是:
2017/5/20 10:10:10的时候小王在进入房间001,主播5此时在房间1做直播
2017/5/20 10:12:30的时候小王在房间001点了赞
2017/5/20 10:15:06的时候小王在房间001送给主播鲜花
2017/5/20 10:15:16的时候小王在房间001关注了主播
2017/5/20 10:25:41的时候小王离开了房间001
part_key | user_id | room_id | timestamp | operation | actor_id | device | network |
---|---|---|---|---|---|---|---|
01f3 | 000001 | 001 | 1495246210 | 进入房间 | 005 | Iphone7 | 4G |
01f3 | 000001 | 001 | 1495246810 | 点赞 | 005 | Iphone7 | 4G |
01f3 | 000001 | 001 | 1495256810 | 鲜花 | 005 | Iphone7 | 4G |
01f3 | 000001 | 001 | 1495259810 | 关注主播 | 005 | Iphone7 | 4G |
01f3 | 000001 | 001 | 1495266810 | 退出房间 | 005 | Iphone7 | 4G |
主键
- part_key:第一个主键,分区建,主要是为了负载均衡,保证数据可以均匀分布在所有机器上,提高并发度和性能。如果业务主键user_id可以保证均匀分布,那么可以不需要这个主键。
- user_id:第二个主键,用户ID,可以是字符串也可以是数字,唯一标识一个用户。
- room_id:第三个主键,房间ID,每个直播房间我们可以认为有一个唯一的标识,可以是字符串也可以是数字。
- timestamp:第四个主键,时间戳,表示某一个时刻,单位可以是秒或者毫秒,用来表示用户产生操作的时间戳,记录了操作的时间戳,我们可以用来分析用户操作频率,或者和直播内容进行关联分析。
- 至此,上述四个主键可以唯一确定某一个用户在某一个时间点在某一个房间的操作数据。
属性列
- operation:操作类型,例如进入房间,离开房间,关注,购买,打赏等等。
- actor_id:直播人的id。也就是主播的id,一些特殊活动下,可能会变成一个主播列表。
- device:用户访问的设备类型。
除了上面提到的一些基本属性以外,我们也可以根据需求添加关注的属性,例如用户的访问设备mac地址,ip地址等。
数据分析需求
如果我们现在想做一些运营分析,例如:
- 最近10分钟有多少用户在房间内做了支付操作。
- 最近用户支付较多的房间主播有什么共同属性。
- 过去一天什么时间段,用户房间内操作最活跃。
- 对于某一个用户,如何根据他最近的房间操作,例如离开了什么样的房间,在什么样的房间会滞留,推荐后续的直播内容。
从上面的这些分析需求我们大体可以分为两类:
- 离线分析过去一段时间用户操作行为,例如上面的场景3
- 实时分析最近用户的行为,例如上面的场景1,2,4
如何获取增量数据
假设我们直接使用API根据时间来获取增量数据,那么我们需要先要得到所有的用户id以及房间id,然后根据时间进行读取。用户数乘以房间数可能会是一个非常大的量,那么我们的分析就难以保证实效性。有了增量通道,我们可以使用Stream Client,订阅实时的增量数据。在Stream Client实现代码把增量数据推送到流计算平台或者ODPS中,做定期的分析。
结构图如下:
商品订单系统
场景描述
假设,我们的系统已经使用表格存储记录每个用户的订单信息,现在我们希望根据订单信息进行分析,近期的热点商品,根据订单更新采购库存。
表结构设计
主键顺序 | 名称 | 类型 | 值 | 备注 |
---|---|---|---|---|
1 | partition_key | string | md5(user_id)前四位 | 为了负载均衡 |
2 | user_id | string/int | 用户id | 可以是字符串也可以是长整型数字 |
3 | timestamp | int | 时间戳 | 使用长整型,64位,足够保存毫秒级别的时间戳 |
4 | order_id | string/int | 订单Id | 可以使字符串也可以是长整型数字 |
数据存储示例
设计好表结构后,我们看下具体如何存储:
比如原始数据是:
2017/5/20 10:12:20小王下了订单,订单号10005,购买了商品5,数量2,单价15,使用支付宝支付30元。
part_key | user_id | timestamp | order_id | commodity_id | price | count | total | payment_type | status |
---|---|---|---|---|---|---|---|---|---|
01f3 | 000001 | 1495246210 | 10005 | 5 | 15 | 2 | 30 | alipay | finished |
主键
- part_key:第一个主键,分区建,主要是为了负载均衡,保证数据可以均匀分布在所有机器上,提高并发度和性能。如果业务主键user_id可以保证均匀分布,那么可以不需要这个主键。
- user_id:第二个主键,用户ID,可以是字符串也可以是数字,唯一标识一个用户。
- timestamp:第三个主键,时间戳,表示某一个时刻,单位可以是秒或者毫秒,用来表示用户订单的时间戳。在这里放置时间,是因为系统往往需要查询某个用户一段时间内的所有订单信息。
- order_id:第四个主键,订单号。
- 至此,上述四个主键可以唯一确定某一个用户在某一个时间点下的一个订单。
属性列
- commodity_id:购买商品的id。
- price:购买商品的单价。
- count:购买商品的总价。
- total:订单的总价。
- payment_type:用户支付类型。
- status:订单的状态。
数据消费需求
针对订单系统,我们需要一下功能:
- 用户可以快速查询过去一段时间的所有订单
- 当有用户下单后,我们需要更新我们的仓储信息,当库存少于一定数量后需要发起采购
- 分析用户的近期购买兴趣,做购买推荐
- 检测异常订单,例如某个用户短时间内大量都买一个产品
针对需求1,我们的表设计再结合表格存储的GetRange可以很方便的实现。
需求2,基于我们的增量可以很方便获取近期的订单,定期更新库存。表格存储即将发布的Stream对接FC(敬请期待),可以做到完全的无服务器触发整个流程,实现订单,库存的自动化管理。
需求3和4,如果希望依赖ODPS做离线分析,可以使用DATAX结合我们的Stream Reader插件将数据导入opds进行分析。如果希望接入其他流计算平台,可以使用Stream Client订阅增量数据。
基于Stream实现属性高效查询
场景描述
我们用表格存储存放商家,以及商品的信息,商品有较多的属性例如价格,产地,适合人群等等,我们希望可以对这些商品的属性做各种灵活的查询。例如,产地是杭州的价格50到100之间的商品,或者我们希望对属性中某个做模糊搜索,例如“茶”。
表结构设计
主键顺序 | 名称 | 类型 | 值 | 备注 |
---|---|---|---|---|
1 | partition_key | string | md5(user_id)前四位 | 为了负载均衡 |
2 | merchant_id | string/int | 商户id | 可以是字符串也可以是长整型数字 |
3 | commodity_id | string/int | 商品Id | 可以使字符串也可以是长整型数字 |
数据存储示例
设计好表结构后,我们看下具体如何存储:
比如原始数据是:
2017/5/20 商家1上架商品10005,商品产地杭州,名称西湖龙井茶叶,价格300,适合人群12岁以上, 属性描述如下:XXXXX
|part_key | merchant_id | commodity_id | location | price | age | property
|------- | ------- | ------- | ------- | ------- | ------- | ------- |
|01f3 | 000001 | 10005 | 杭州 | 300| above 12 | XXXXX|
为了实现灵活的查询我们可能需要借助一个搜索系统例如Elasticsearch,那我们在插入一条新的商品的时候需要双写表格存储和Elasticsearch。那我们架构是:
基于这样的架构,我们需要引入一个MQ,自己实现表格存储和ES的双写。现在有了Stream功能后,我们可以直接写入表格存储,把增量同步进入Elasticsearch。架构修改为如下:
一个更让人期待的功能是阿里云数据同步通道DTS正在集成表格存储到ES,届时用户只需要做一些配置就可以打通表格存储和ES,灵活的实现属性的查询搜索。我们也可以参考表格存储和ES场景分析和实践
总结
最后我们来总结一下有了Stream带来的好处和适用的场景,
Stream可以很方便的在以下场景中使用:
- 增量数据复制 DataX StreamReader
- 对接流计算,实时计算平台
- 对接函数计算
- 对接搜索
- 订阅增量数据
在使用表格存储这类水平扩展的分布式数据库的时候,我们需要让我们数据的分区键尽量分布均匀,避免写入尾部热点。所以我们无法使用时间做为分区键,但是如果我们的业务需要基于时间去读取消费数据,例如下图中,pk1,pk20和pk95等一些键值产生了新数据,我们需要跳着读区这些新数据,可能这样的key非常多,我们也很难得知哪些key产生的新数据。
为了获得这些增量数据,我们得依赖一个队列,对一个更新操作执行双写,这样会增加很多额外的系统依赖和成本。而Stream的天生基于Commitlog的特性彻底改变了这一点,数据库的内容是根据主键进行排序组织,而数据库日志是根据修改顺序排序(如下图所示)。所以我们可以很方便的连续读区到这些在数据库文件中跳跃的键值。