【.com原创文章】如果要做营销活动,如何找到目标人群和用户特征?群体的筛选通常离不开用户画像。ImagefromPexels用户画像是根据用户特征、业务场景、用户行为等信息,构建一个带标签的用户模型。例如,消费者用户画像分为两类:属性和行为标签。在这两种标签中,一种是固定的,另一种是可变的。固定属性标签基本是买家的性别、年龄段、会员等级、消费水平、购买力等,可变行为标签基本包括买家的浏览、加入购物车、购买等行为。通过多年的建设,苏宁已经构建了完善的用户标签体系,覆盖零售、金融、体育等多个行业。同时搭建了标注服务平台,通过开放丰富的标注数据能力,为广告和推荐提供智能标注中台服务能力。数据量越来越大,千亿标签数据如何为6亿+用户做秒级用户画像?本文将带来用户画像技术的新进展和架构实践,介绍基于ClickHouse定制开发的标签平台,真正做到海量标签数据的快速导入和秒级用户画像查询分析,提供一套完整的标签系统。前期人群筛选到后期营销策略优化。业务场景介绍“双11”来了,假设需要发放1000万张家电券,那么我们首先需要根据标签筛选出符合条件的人群,人群数量在1000万左右,然后分析画像被选中的人看到是否满足预期的特征。如果人群符合特征,系统将一键生成用于营销的人群包(userid列表),并自动发布和营销。图1:预估人数业务流程用户选择标签以及标签之间的交差关系,圈出符合条件的人群,实时预估人群数量。例如选择:图2:创建人群上图中标签选择的意思是:“用户年龄范围25-36岁”,是一组“智能家居特征”,排除了具有最近30天花费不到10元。以集合运算表示的公式为:{{用户年龄25-36岁}∩{智能家居人群}}-{30天消费不足10元}技术难点包括:人群套餐数量大。每个众包都有比较大的用户群。系统实时输出计算结果,难度很大。ProfileanalysisWhenthenumberofusersmatchedwiththeplannednumberofcouponsisselected,itisnecessarytoanalyzethecharacteristicsofthecrowdtoseewhetherthecrowdmeetsthecharacteristicrequirements.用户画像表结构示例如下:将筛选后的团包与用户画像表相关联,详细分析关联画像的特征。还可以进一步对人像特征进行一些历史数据分析。我们之前的解决方案是将用户标签存储在ElasticSearch宽表中。宽表的结构是:一个用户附加了一堆标签的表结构。在向大而宽的表中插入数据时,需要等待业务数据准备好后才能运行关联表操作,然后将关联结果插入到ES中。经常遇到的是业务方的一个任务被延迟,导致插入到ES中的关联任务无法执行,运营商无法及时使用最新的画像数据。在ES中修改文档结构是比较繁重的操作,修改或删除标签比较耗时,ES的多维聚合性能比较差,ES的DSL语法对开发者不友好,所以我们从ES中替换了标签存储引擎到ClickHouse。ClickHouse是近年来备受关注的开源列式数据库,主要应用于数据分析(OLAP)领域。凭借其出色的查询性能,受到业界的青睐,各大厂商纷纷跟进并大规模使用。苏宁大数据引入并改造了ClickHouse,封装成丰富的Bitmap接口,支持标注平台的存储和分析。ClickHouse集成Bitmap我们在ClickHouse中集成了RoaringBitmap,实现了Bitmap计算功能集,贡献给了开源社区。userid以bitmap的形式压缩存储,crowdpacket的交差计算交给了一个高效的bitmap函数,节省了空间,提高了查询速度。图3:ClickHouse集成了Bitmap,围绕Bitmap对象实现了一套完整的计算功能。构造Bitmap对象有两种方法,一种是从聚合函数groupBitmap构造,另一种是从Array对象构造,或者将Bitmap对象转换成Array对象。ClickHouse的Array类型拥有大量的函数集,可以更方便的处理数据。上图中间部分是Bitmap的计算函数集,里面有丰富的位运算函数、聚合运算函数、求值运算函数。基于ClickHouse的新架构介绍架构图如下:图4:Label架构ClickHouseManager是我们自主研发的ClickHouse管理平台,负责集群管理、元数据管理、节点负载协调。Spark任务负责标签数据的生成和导入。当业务方的任务运行完毕后,会立即调用tag-generate生成标签数据文件,存储在HDFS中,然后在ClickHouse上执行从HDFS导入到ClickHouse的SQL语句。这样就完成了标签的制作工作。标签生产同时进行。假设某个业务方的数据没有准备好,不会影响其他业务的标签制作。用户画像平台通过Proxy向ClickHouse集群查询标签数据。在查询之前,需要将查询表达式转换成SQL。我们封装了这个逻辑,提供了一个通用的转换模块,叫做:to-ch-sql。业务层基本不用修改就可以查询ClickHouse。标签数据表的基本结构相对于ElasticSearch的存储结构,我们将标签存储改造成了行转列的存储。每个标签对应一个Bitmap对象。在Bitmap对象中存储userid集合:CREATETABLEch_label_string(labelnameString,--标签名称labelvalueString,--标签值uvAggregateFunction(groupBitmap,UInt64)--userid集合)ENGINE=AggregatingMergeTree()PARTITIONBYlabelnameORDERBY(labelname,labelvalue)SETTINGSindex_granularity=128;uv一个字段Bitmap类型,存储整型userid,每个userid用一个bit表示。主键索引(index_granularity)默认值为8192,可修改为128或其他值。由于Bitmap占用的存储空间比较大,应该修改为较小的值,以减少稀疏索引的读放大问题。根据标签值的数据类型,分为四种表:StringIntegerDoubleDateTagnameasPartition。通过这样的设计,可以更方便的增加或删除标签数据,只需要修改Partition的数据即可。分区管理都有相应的SQL语句,操作起来更方便。useridshard存储是在导入tag数据时根据useridshard导入的,每台机器只存储userid对应的tag数据。每台机器分别导入分片标签数据,实现数据并行导入。在我们的环境中,单机的导入性能大约是每秒150万条记录。基于标签过滤人员时,SQL只需要在单个分片上执行,中间结果不需要返回给查询节点。在进行“预估人数”计算时优势尤为明显:每个分片只需要返回满足条件的人数,对查询节点进行求和运算,然后将求和结果返回给客户端。充分挖掘ClickHouse分布式并行计算的能力。查询过程使用with语句计算crowd包的Bitmap对象,然后使用Bitmap函数计算交差。当需要计算的标签较多时,标签查询的SQL比较复杂。标签查询SQL被打包到分布式代理表的SQL中。分布式代理表本身不存储数据。代理表标识要查询的节点。在代理表标识的节点上执行标签查询SQL。然后将查询结果聚合到分布式代理表上。通过ClickHouse分布式表的特性,实现标签查询的colocate机制。图5:查询过程示例SQL如下:--本地查询代理CREATETABLEch_agent_user(agentnameString)ENGINE=MergeTree()PARTITIONBYagentnameORDERBY(agentname)SETTINGSindex_granularity=8192;--分布式代理表CREATETABLEch_agent_dist_userASch_agent_userENGINE=Distributed('cluster,test','test'ch_agent_user',cityHash64(agentname))--查询用户数SELECTsum(user_number)ASuser_numberFROMch_agent_dist_userRIGHTJOIN(WITH(SELECTgroupBitmapState(userid)ASusers0FROMch_label_stringWHERElabelname='T')ASusers0SELECT'agent'ASagentname,bitmapCardinality(users0)ASuser_number)USING(agentname)settingsenable_scalar_subquery_optimization=0;ch_agent_user表本身不存储数据。用with语句进行右连接查询时,查询结果以with语句的结果集为准,因为是右连接查询。将每个节点的查询结果返回给查询节点,由查询节点进行汇总计算。参数enable_scalar_subquery_optimization=0表示不优化with语句的查询结果,需要每个节点执行。默认情况下,ClickHouse中with语句的结果缓存为标量,查询节点的标量会分发到其他服务器。当发现标量已经存在时,不会在本地节点执行with语句。我们希望在每个节点都执行with语句,所以将这个参数设置为0。用户画像用户画像对性能要求比较高,平均查询响应时间不能大于5秒。用户可以在界面上随机选择人群,然后实时分析圈出人群的画像。用户画像技术经历了3次架构重构:①V1:大宽表模式最早的方案是创建一张以userid为主键的画像表,表中其他字段为画像的特征字段,以及圈出来的人和人像表在操作,然后分组操作。这种设计带来了两个严重的问题:在增加或删除特征字段时,需要修改人像表的表结构。当圈出的人数比较多时,涉及到大记录集的groupby操作性能较差。②V2:Bitmap方式将某个特征值下的userid集合作为一个Bitmap对象存储在一条记录中,一条记录的内容如下:用户选择的组的Bitmap对象与人像表的Bitmap对象进行AND运算,返回圆圈选择人群头像信息。通过这样的设计,基本满足了性能需求,平均耗时小于5秒。但是,对于一些大人群来说,人像的表现还是比较差,在10秒左右。画像表记录的数据量不大,但是计算时需要从磁盘反序列化画像表的Bitmap字段。一些Bitmap对象占用数百兆字节的空间,导致性能下降。③V3:Jointableengine方式ClickHouse的Jointableengine可以在内存中存储数据。插入数据时,先将数据写入内存,再刷入磁盘文件。当系统重新启动时,数据会自动加载回内存中。Join表引擎可以说是一个持久化的内存驻留表。我们将画像表的数据保存到Join表引擎中,画像表的Bitmap字段常驻内存。当圈出组的Bitmap对象进行AND操作时,内存中加载的两个Bitmap对象之间的数据计算非常快。通过本次优化,平均查询时间优化到1~2秒,千万级人物画像分析不超过5秒。总结通过ClickHouse对Bitmap功能的集成和Join表引擎的应用,对架构进行了一系列的优化,大大提升了标注平台的数据分析能力。新架构主要有以下优点:可以并行构建标签数据,加快标签数据的生产。HDFS文件同时导入ClickHouse,加快标签数据准备。查询请求平均响应时间小于2秒,复杂查询小于5秒。支持标签数据准实时更新。标签表达式和查询SQL对用户更加友好,提高了系统的可维护性。与ElasticSearch的配置相比,可以节省一半的硬件资源。未来规划:目前ClickHouse使用的是32位版本的RoaringBitmap,计划增加64位版本。ClickHouse查询的并发度低,增加了更智能的缓存层。支持离线生成ClickHouse数据文件,进一步提升标签准备速度。参考:ClickHouse官网:https://clickhouse.tech/ClickHouse中文社区:http://www.clickhouse.com.cn/位图PR:https://github.com/ClickHouse/ClickHouse/pull/4207作者:杨朝晖简介:苏宁科技集团大数据中心高级架构师,ClickHouseContributor。在OLAP和大规模分布式计算领域有深厚的技术积累,目前负责数据中心和标签平台相关的架构工作。编辑:陶佳龙征稿:如有意向投稿或寻求报道,请联系editor@51cto.com【原创稿件请注明原作者和出处为.com,合作网站转载】
