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

京东APP百亿车-商品关系数据检索实践

时间:2023-03-14 17:35:12 科技观察

简介本文主要讲解京东百亿商品模型适配数据存储结构的存储结构设计以及如何实现适配接口的高性能查询。通过京东百亿级数据缓存架构设计实践案例,简要分析jimdb的位图(bitmap)功能和lua脚本在高性能场景下的应用。希望通过本文,读者能够对缓存的内部结构有一定的了解,能够以最小的内存使用代价灵活地将位图(bitmap)应用到各种高性能的实际场景中。名词解释:jimdb-basedonredis改造升级,京东中间件团队研发的一款高性能key-value数据库,大部分功能与redis保持一致。背景整个汽车行业的特殊性,对零部件具有很强的配套性。不同汽车使用的零配件(如:轮胎、机油、三滤、雨刷、火花塞等)规格型号不同。卖汽配的时候不像3C家电、服装。需要结合用户的具体车辆信息,推荐合适的配件。为此,京东构建了自己的人车档案模型,并通过算法清洗了数百亿模型-零件适配关系数据,最终形成了“人->车->货”的关系链接,解决了“人不识货”的问题。》的问题。具体使用场景如下:图1.1京东推荐商品图1.2京东弹窗推荐商品数据模型“人->车->货”关系的核心环节由人(京东用户)、乘用车和SKU由三部分组成,首先,用户可以在京东APP的商家搜索页和商家详情页的多个位置选择自己的车型信息进行绑定(例如:图2.1,京东商誉绑定车入口“+加车”按钮),建立“人车档案”数据。图2.1.京东商誉绑定车入口位置图2.2.车主位置京东搜索绑定车的入口数据量级百亿)。最后在购买商品时,京东的推荐系统可以通过用户自己绑定的车型推荐适合该车型的商品。具体商品适配到车辆模组el数据模型,见图2.3。术语解释:SKU-StockKeepingUnit(库存单位),KU指的是一种产品,每个型号都有一个SKU,方便电商品牌识别产品。例如:商品详情页面的每个型号/规格都会对应一个SKU。乘用车——包括轿车、小巴和9座以下的轻型客车。乘用车细分为轿车、MPV和SUV。图2.3京东商品适配模型数据模型缓存结构设计通过前面两部分的介绍,我们可以了解到整个商品搜索适配推荐有两个核心问题。第一,兼容模型的百亿级产品数据的存储结构设计,尽可能减少资源成本;其次,尚享在通过用户模型搜索兼容产品时,必须保证TP999的接口性能在毫秒级。在最终的技术选择中,使用了jimdb的位图功能来进行数据存储。名词解释:TP999-单位时间,99.9%的请求接口响应时间小于或等于该值。例如:1秒内1000个请求,第1000个请求按照响应时间从低到高排序,第999个响应时间为20ms。最后的TP999为20ms。3.1位图(bitmap)结构位图(bitmap)通过最小单位位设置为0或1,表示一个元素对应的值或状态。一位的值为0或1;也就是说,一个比特最多可以存储2个信息。比特:计算机内部存储数据的最小单位,例如:11001100是一个八位二进制数。字节(byte):计算机中处理数据的基本单位,习惯上用大写的B表示,1B(字节,字节)=8bit。图3.1位图(bitmap)内部结构3.2位图(bitmap)数据写入过程位图(bitmap)是基于jimdb的SDS(SimpleDynamicString)类型的一系列位操作,遵循jimdb的SDS特性,例如:bit地图(位图)最大长度为512M,最大存储容量为232位。下面是“big”string的SDS结构示例:图3.2.1SDS(simpledynamicstring)采用空间预分配策略保证性能:空间预分配用于优化string的增长操作安全数据表。当SDSAPI修改了一个SDS,需要扩展SDS的空间时,程序不仅会为SDS分配修改所需要的空间,还会为SDS额外分配未使用的空间。具体预分配流程图如下:图3.2.21)创建SDS简单字符串预分配空间为:offset/8+1。2)当剩余空间不足时,预分配空间过程。3.3压缩货车关系缓存。产品适配车型关系(百亿数据)。在product-car关系的缓存存储过程中,以productSKU为KEY,将fullmodelID的offset(偏移量用于减少Memory消耗)存储为一个VALUE值。完整的模型ID大约有几十万条数据。极端情况下,一个产品SKU可以适配几十万辆,很容易造成缓存大KEY的问题。为此,我们做了一个偏移量(对应全模型ID自增ID)切分处理。具体的,在以SKU为缓存key的基础上,增加一个segmentmarknumber作为新的KEY,每个offset会根据segmentrange对应一个segmentmarknumber。例如:offset1~50000,对应的缓存KEY为SKU+0;offset50001~100000,对应的缓存KEY为SKU+1,其他offset以此类推。车辆也不会缓存大的KEY。BitMap缓存结构的底层使用SDS简单字符串。为保证性能,采用了预分配空间的策略(如图3.2.2,2中“缓存BitMap内部存储流程图”中虚线圈出的部分),这样缓存的产品和大量的缓存空间是浪费在汽车相关的时候。为此,我们调整了offset存储顺序,先获取需要缓存的车型中最大的offset,并保证同一个缓存KEY第一次创建SDS简单字符串(如图3.2.2所示),》CacheBitMap内部存储在流程图1)中虚线圈出后,不再进行二次空间扩容,从而最大限度地提高缓存利用率,达到压缩空间的目的。缓存数据关系的过程为如下:3)设置segment的最大offset,保证后续新的offset不会扩容4)为segment设置一个更小的offset,完整的modelID是一个固定长度的7位数字,如果是作为偏移量,会消耗巨大的内存,所以使用对应的自增ID作为偏移量,bitmap最终缓存的产品SKU与车的适配关系缓存结构如下:3.3产品与汽车缓存结构图5)spuId用{}括起来表示缓存路由(Lua脚本中同一个请求,数据必须在同一个缓存分片,否则会丢失数据)。POP商品的spuId为SKU的商品ID,自营商品的spuId为SKU的MainSkuId。备注:自营商品的MainSkuId可能会发生变化,因此我们接入商品变更MQ消息,实时调整SKU和车适配关系的存储位置。京东页面上每个不同的规格/型号对应不同的SKU,但都对应同一个SpuId或MainSkuId。缓存架构设计货车关系数据量每天都在增长,需要支持集群水平/垂直扩展的缓存架构设计以满足业务发展和高可用。整个缓存架构体系主要由前端、京东养车产品与汽车的关系层、存储三部分组成。“商品与车辆关系缓存架构”层的核心包括:1、“集群路由”层实现集群的水平扩展,保证缓存容量跟得上数据量的增长。2、“分片路由”层确保搜索到的底层数据分片相同,避免数据丢失。“存储”层的核心包括:1、实现了缓存压缩,参见3.3压缩商品车辆关系的缓存。2、单元化实现跨地域容灾,保证大促系统的稳定性。具体的商品与汽车关系缓存架构如下:商品与汽车关系缓存架构图6)集群路由,按商品类型或商品编号(POP商品)路由到不同的缓存集群,方便横向扩展,每个集群受限于单个片段。分片超出限制问题。7)分片路由保证Lua脚本搜索数据的底层数据簇分片相同,避免数据丢失。自营产品和POP产品的路由分别为main_sku_id和product_id。8)自营商品缓存集群,单元化实现跨地域容灾,自研DRC(DataReplicationCenter)数据同步机制。9)POP商品缓存集群按商户ID拆分为两个子集群。高性能搜索是基于BitMap(位图)缓存的商品和汽车关系数据。业务详情调用接口内部实现采用Lua脚本,减少网络开销,保证整个接口的性能。搜索接口流程图如下:5.1商家详情搜索商品与汽车匹配关系流程图10)商家详情调用该接口时,必须传递两个参数。第一个参数是完整模型ID的列表,大约5个完整模型ID。第二个参数是产品SKU的列表,SKU的数量限制在200多个。最后将完整的型号ID和产品SKU结合起来,形成上千个产品和车辆的关系后,再去几十个数十亿个匹配关系来搜索它们是否匹配。如果不匹配,则返回匹配的产品,否则返回不合适的产品。5.2商业搜索商品与汽车匹配关系的Lua代码和Lua脚本,减少了应用服务器与缓存服务器的交互,减少了网络耗时,提高了搜索服务的性能。下面是Lua脚本的具体代码:5.3商家详情搜索产品与汽车适配关系界面表现基于以上缓存设计和Lua脚本的使用,整个界面T999小于13ms。具体接口性能监控如下:在总结整个缓存结构的设计时,使用BitMap(位图)来存储数据。分析SDS内部存储过程,通过存储过程机制避免预分配空间节点,最大化利用缓存空间,避免资源浪费。使用Lua脚本实现数据的自适应搜索,减少网络开销,进一步提高界面性能。希望这篇文章对大家以后设计类似的场景有所帮助和启发。