当前位置: 首页 > 科技观察

全链路生产迁移与B-C端数据存储隔离

时间:2023-03-16 17:59:12 科技观察

1.业务背景榜经历了供应量快速增长、C端分发场景多样化等迭代。数据量和峰值流量增加了十倍、百倍。这必然会给数据库带来很大的存储压力,降低C端的查询性能。为满足未来各种复杂的定制规则和数亿级的数据选择,全面引导消费者的购物决策,得物商品列表生产迁移和B/C端数据存储隔离应运而生。2.概述作为C端核心导购场景之一,得物清单通过建立丰富的规则矩阵,为用户提供多维度的采购参考建议,帮助用户快速决策,完成业务细节的转换.目前涵盖畅销榜、新品榜、潮流榜、草种榜、好评榜、复购榜六大类。2.1“圈选”+“排序”为核心:通过圈条件选出一系列产品,然后根据排序规则进行排名,选出TOP20产品进入列表。圈出的产品范围包括品类、品牌、系列、标签等,通过[6种型号,N个指标因素]综合计算排序后的总分代表产品的综合竞争力。分数越大,产品的性能越好。基于上述算法模型,得物榜严格控制榜单商品质量,帮助用户根据自身需求快速决策。2.2还有更好的排序吗?通过优化列表排序逻辑,有针对性地提升列表受理效率。探索一套最优排序规则公式需要不断尝试,因此整个实验周期长,设计需要支持实验的快速推进。我们来看看目前列表的创建和制作是如何实现的。涉及的表:基本表:记录列表的基本信息;圈子商品情况表:记录list圈子商品的维度信息,包括categoryid、brandid、seriesid、labelid、productidscollection等;商品集合表:记录list下关联的top20productid,当前list的B/C侧流量都是通过该表查询;如何绑定带圈的产品范围?通过后台添加新列表时,人工列表绑定悦悦规则或手动直接配置productids集合,同步写入列表条件表;通过后台添加半自动列表时,绑定类目id,品牌id,系列id等维度规则,同步写入商品列表的条件表;自动列表通过预先设定的规则,批量写入产品列表的条件表;数据流?在商品后台的基础表中添加list的基本信息,将圈出的商品范围信息(category,brand,series,label,spuIds等)保存到圈出的商品条件表中,搜索圈出的商品条件每两小时定时从库表中获取数据,获取最新的数据刷入商品集合表。搜索数仓H+1/T+1转储:搜索离线数仓离线计算排序因子数据生成离线宽表;搜索离线产品排序引擎:控制列表由搜索产生。每2小时调度一次,扫描circle商品条件表中的全量数据,从wide表中筛选排序商品,排序结果通过DTS数据同步返回list商品采集表;搜索线下商品排序引擎:实验组列表是由商品排序引擎生成的,底层实现与搜索大致相同。3系统缺陷及解决方案3.1链路强耦合。Commodities/Search有双写列表商品表场景。由于搜索通过d??ts数据同步返回数据,数据相互重叠甚至主键冲突。这个可以通过id隔离暂时解决。3.2重新发明轮子目前,榜单产品的生产环节强烈依赖于搜索,产品的圈选和排序都是通过搜索实现的。搜索榜产品制作方式单一,无法满足榜单圈选/排序规则的定制化供应。作为得物的核心选品和投放平台,“老岳”已经具备了强大的产品梳理能力。3.2.1老岳指数体系海量选品指数维度(商品基本信息、活动信息、价格库存、流量转化等)支持各类业务,分钟级实时指数数据进行选品;odps离线索引数据:商品/交易索引进行计算生成离线宽表。实时交易指标:如活动期间的GMV、买家数量等交易数据。OfflineDUMP:离线数仓离线计算指标对应值,通过datawork同步任务将指标值同步到老约B端ES。实时DUMP:业务系统/实时数仓或其他对接方式,通过商品Feature(部分业务逻辑强的指标)、DBBinLog或其他方式准实时将数据通知老岳指数中心,然后老岳索引中心将数据发送给老岳索引中心,数据落到老岳B端ES。3.2.2老粤实时选品引擎执行引擎对选品结果进行分钟级更新。3.2.3老岳分拣中心支持个性化、统计字段、自定义权重比例等多维复杂升序和降序排序规则;还支持用户特征进行推荐算法的个性化排序。具体细节本文不做展开,重点介绍老岳指标体系、选品排序能力在本次迁移中的应用。针对第一个和第二个问题,我们提出列表向老岳的迁移——通过复用老岳已有的圈选/排序能力,完成列表产品的生产能力,将列表生产从搜索端迁移到老岳product另一方面,去除列表的底层能力,建立对搜索的依赖。3.3无法支持海量数据的高并发读取。未来的榜单需要支持品类下沉场景。“品类下沉”是指现有榜单以品类为维度,再以品牌、系列、标签等维度延伸榜单。例如:在畅销跑鞋榜单下,细分为耐克跑鞋、入门级跑鞋、透气跑鞋等,并展开更多维度,包括人群、款式等。结合任意维度和按照笛卡尔积生成方式成对分类,会产生数百万甚至数千万的海量数据,存储压力很大。同时,列表从生产到C端分发,经过列表产品划定、审批流程等状态控制后,C端分发成功的列表数量有限,每次查询都会触发实时过滤有效数据。随着列表数量的快速增长,必然会导致C端查询性能下降,如大key、索引失效等,存在性能风险。列表分布在store中,势必伴随着高并发阅读。针对这个问题,虽然可以尽可能优化sql,优化索引,缓存等,但是总会有达到极限的时候。海量数据的存储类型选择已经有很多案例了,如何选择存储类型呢?存储选型的目的还是为了我们的使用场景和用户服务,所以我们在选型前需要先回答一些业务指标&技术指标的问题,这样我们才能清楚的了解存储选型的应用环境:数据量和日增量datavolume:数据量不断增加,稳定在可控范围内;阅读和写作偏好:榜单状态和榜单商品不经常变动,多读少写;运行性能要求:并发高峰期业务明细,首页,低潮期如部分二级页面;查询复杂度:复杂条件查询、聚合查询、联合查询;其他性能要求:实时性要求低;结合列表业务的特点和海量数据、高并发的特点,可能的解决方案不局限于使用缓存。程序代码将数据直接保存在内存中,如ConcurrentHashMap、Caffeine等;或者使用Redis等缓存框架;数据库优化:数据库优化的方法有很多,常见的可以分为:数据库表结构优化、SQL语句优化、分区、表细分、索引优化、使用存储过程代替直接操作等;使用NoSql技术:HBASE、MongoDB等;使用搜索引擎技术:ElasticSearch等;在设计实践中,必须基于需求和业务驱动的架构,无论选择DB/NoSQL,都必须以需求为导向,最终的数据存储方案必须是各种权衡的综合设计:-分库分表:垂直切分适用于表内业务耦合,切分后单表数据量依然较大;水平切分点的关联查询性能较差;HBASE:列存储分布式数据库,适用于TB级数据的实时入库和快速随机访问场景,缺点是查询只能通过rowkey和range进行检索,不支持复杂查询;MongoDB:文档类型NoSql,适合非结构化数据存储,表结构可以随意改变,所以插入效率高,也不支持多表查询等复杂查询;ElasticSearch:写入性能低,实时性低,但是通过为所有字段添加索引可以支持复杂的聚合查询和条件查询。综合考虑,B/C端数据存储隔离成为目前性价比最高的解决方案。下面将介绍该做法的具体实施要点。4、技术实施点的整体改造将分为两个阶段:首先完成链路改造,即列表的生产迁移,等待数据校验通过第二阶段存储改造,即,B/C端数据存储隔离。4.1链接改造4.1.1兼职指标的创建兼职需要支持榜单的实验能力,所以在榜单控制组通用指标的基础上增加了实验组通用指标,并进行相应的排序保留实验组规则。支持同类型列表同时进行实验,实验结束后根据实验结果修改通用算法模型。实验指标可以在多次实验中复用,不会增加月度指标的数量。对照组的畅销榜评分指标(best_seller_score):根据7天内的销量和成交金额计算综合排名。soare_score):根据近7天销量、收藏等指标综合计算排序对好评率、好评数等指标进行综合计算排序。对照组复购榜单评分指标(rebuy_score):根据产品当年累计复购数量等指标综合计算排序。人数等指标综合计算排序实验组新品榜单评分指数(new_product_score_test):根据上线时间、点击量、收藏量、销量等指标计算指数综合计算排序测试组seed榜单评分指数(collect_score_test):根据当日新品销量等指标综合计算排序实验组好评指数(favorite_score_test):根据产品好评率和好评数量等指标综合计算排序复购榜单评分指数(rebuy_score_test):根据产品年度累计回购次数等指标综合计算排序规则(sort_new_product)=new_product_score*100%desc对照组趋势榜单排序规则(sort_soare)=soare_score*100%desc控制组种子列表排序规则(sort_collect)=collect_score*100%desc对照组热销榜排序规则(sort_favorite)=favorite_score*100%desc对照组回购榜排序规则(sort_rebuy)=rebuy_score*100%desc实验组热销榜排序规则(sort_best_selle)r_test)=best_seller_score_test*100%desc实验组新品榜单排序规则(sort_new_product_test)=new_product_score_test*100%desc实验组趋势榜单排序规则(sort_soare_test)=soare_score_test*100%desc实验组种子榜单排序规则(sort_collect_test)=collect_score_test*100%desc实验组好评榜排序规则(sort_favorite_test)=favorite_score_test*100%desc实验组回购榜单排序规则(sort_rebuy_test)=rebuy_score_test*100%desc4.1.3指标,排序规则同步ES进行后续选择排序{"name":"best_seller_score","type":"long"},{"name":"new_product_score","type":"long"},{"name":"soare_score","type":"long"},{"name":"favorite_score","type":"long"},{"name":"rebuy_score","type":"long"},{"name":"best_seller_score_test","type":"long"},{"name":"new_product_score_test","type":"long"},{"name":"soare_score_test","type":"long"},{"name":"collect_score_test","type":"long"}{"name":"favorite_score_test","type":"long"},{"name":"rebuy_score_test","type":"long"},{"name":"sort_best_seller","type":"long"},{"name":"sort_new_product","type":"long"},{"name":"sort_soare","type":"long"},{"name":"sort_collect","type":"long"},{"name":"sort_favorite","type":"long"},{"name":"sort_rebuy","type":"long"},{"name":"sort_best_seller_test","type":"long"},{"name":"sort_new_product_test","type":"long"},{"name":"sort_soare_test","type":"long"},{"name":"sort_collect_test","type":"long"},{"name":"sort_favorite_test","type":"long"},{"name":"sort_rebuy_test","type":"long"}转换后整个链路的数据流如图:listcreation:circleproduct条件写入老约选品规则数据表;排序规则:超时中心调用排序引擎更新每个商品的排序点并同步B端es;实时选品:圈品引擎扫描数据产品的分钟级执行的选品规则,从底表中取数据筛选合格产品,生成选品集id同步老粤B端es;完成绑定:Laoyuees将选品结果返回到list产品表,并将选品id返回到list基础表,完成list单id与选品id的绑定;商品更新:监听老月的选择集与老月集绑定的结果变化消息,所以查看列表下的商品等同于查看老月集下的商品。具体逻辑如下:是否可以复用老月存储结构,腾出空间独立存储列表项?首先根据B/C查询场景分为正向链接和反向链接。正向链接,即从listid获取老月集合id,根据老月集合id从老月es获取老月产品结果集,B端查询方式都是正向链接,所以老月可以复用月存储结构。反向链接是根据商品id从老月es获取老月集id,从老月集ids中查询列表id。由于老月记id暂时没有场景标记,只能遍历老月记id判断是否属于某个列表,查询成本极高。C端列表的核心分发场景业务满足了这个链接特性。当然,我们可以通过建立老月记场景标注系统,构建列表产品ES宽表来解决问题。有没有性价比更高的方案?4.2.2C端数据源-list集合mysql表考虑到目前业务规模和C端查询复杂度,考虑使用B/C端查询隔离来实现。老粤评选结果的变化、名单的显示/隐藏、批准/否决、生效/失效等都会影响名单的分发状态。归根结底,C端能分发的列表是非常有限的。为避免C端实时过滤大量数据,降低索引失效和大key的风险,原B/C端公共数据源列表集合表只存储可分发列表的商品数据可由C端查询,实现方式极其简单:当状态发生变化,悦悦结果集发生变化时,触发list集合表的实时更新/删除。整个改造过程无需改动C端代码,仅通过几行业务代码,将DB存储数据的量级降低到原来的40%,可以支撑数据量的稳定增长未来两到三年。4.3灰度设计与功能完整性相比,如何实现流畅稳定的切割流程是整个项目更为重要的一环。为了实现用户无感切换,减少切换过程中可能出现的故障对系统的影响,采用了多个灰度读写开关,保证了切换过程的流畅性和稳定性。.整体灰度分阶段逐步推进,采用“增量数据维护-数据验证-全量数据刷新-数据验证-灰度流切换-数据验证”的方式,每个阶段都有补偿策略或回滚计划,风险可以减少。如果可控性高,则先进行增量数据维护。增量数据check+fix通过后,刷新全量数据。全量数据check+fix完成后,实现灰度读取和切换流程,进行双读校验,一旦出现问题,开关关闭;writeswitch用于先写临时表,待临时表数据校验通过后再切换到主表。一旦出现故障或脏数据,启动预案,保证1小时内数据回滚。具体我们关注以下四点:4.3.1数据维护首先进行风险评估,需要绑定链表的老月集数量为N,以每个老月集的M个SPU为限,以及selectionresultset总数可以达到N*M,高写入流量会导致ES实例整体性能急剧下降。另一方面,数据恢复的成本极高。对于绑定错误,只能重新绑定新的老月合集。需要把旧的老月合集全部删除,再绑定新的老月合集。写两次的成本非常高。所以先进行增量数据维护,等增量数据check+fix通过后再批量刷新全量数据。4.3.2读/写截断读截断:读截断的核心是将B端数据源切换到老约ES。执行这一步的节点非常关键,需要在链路改造完成和存储改造开始之前进行。原因是如果先进行存储改造,再进行流切换,由于rank_list表只存储存储改造完成后的可分发列表,一旦流切换后出现问题,应该切换回老链接立马挂掉,原来B/C端公共数据源rank_list表中的数据不完整,根本不能满足B端的需求,不能快速止血。只能快速定位问题并修复才能上线。确定执行时间节点后,写入switch并进行双读检查,发现问题则切换回原链路。写入切流:关闭搜索更新列表产品开关后,将悦悦选择的结果全部写入列表集合表的风险较大。通过创建list集合临时表,临时表数据校验通过后切换到主表。4.3.3数据校验双读校验、不定期全量数据校验+修复、增量数据校验+修复、应用监控、日志告警埋点等;4.3.4数据校正针对灰度过程中可能出现的所有数据错误准备了相应的数据校正接口。再想想,如果在写入和切换过程中切换到主表后出现无法识别的数据错误,如何快速止血?在迁移过程中,我们暂时保留搜索和更新列表中商品的能力作为数据恢复计划,数据恢复可在一小时内完成。灰度截流的具体过程如下:经过两周的灰度化,已经去除对搜索的依赖,实现全链路闭环,并按照灰度计划,“截和停止”是通过切断开关和预先计划来确保的。上线期间零故障。5.总结综上所述,列表通过生产迁移彻底解决了列表底层能力支持的痛点。链路合并完成后,可以借助老悦产品的排序能力,降低未来各种复杂的定制化供应场景的维护成本。功能上线后,榜单各项业务迭代效率提升50%以上。并且通过B/C端数据存储隔离,表存储成本可降低60%,改造成本极低。在此基础上,思考是否还有更多的发力点:结合目前的系统现状和未来的可能性,结合业务规划,围绕用户对平台列表的期望,未来会向多维度(内容、brand、sku、spu等)列出通用生产引擎,在货源充足的基础上实现场景化个性化配送。以上是我们在德屋品单的生产、改造和探索实践中的一些经验和总结。希望能分享给大家,帮助到看到这篇文章的你们!