【玩转大厂实际项目】之【元数据】

大家好,我是桐言无忌,当前是不务正业的攻城狮,信奉“实践出真知,生活更简单”,向往自由。签名

引子

经常使用今日头条APP浏览新闻的同学,会发现刷不了几下,就会有商品促销广告横亘在两则新闻中间。这些广告有拼多多、有京东、还有其他类型的商品,来诱导你点击购买。

京东头条.jpg

果然,“宇宙的尽头是带货”。但是,大家有没有想过,这些商品广告是怎么出现在你的手机屏幕上的,其他人的手机也能看到相同的广告吗?

要回答这个问题,我们先了解商品广告和新闻这两者的关系。资讯类APP是文章的生产者,不生产商品,就需要从其他厂商获取商品数据;商品生产厂呢(京东、拼多多、淘宝等)也希望依靠资讯APP的流量,来曝光商品,提高成交量。至于,这两者怎么算钱money,不是讨论的重点。

我们要讨论的,是设计一个系统可以对接N个商品生产厂,同时,把商品推给手机APP用户。

“青铜”系统玩法

整体系统简化来看,有三个方向(按八字诀“上北下南,左西右东”)

  1. 从北往南:CP数据,诸如京东电商、汽车之家、携程酒店等,经过系统的校验加工等逻辑,最终落盘到存储层;
  2. 往东:是web界面,方便系统运营人员手工管理商品数据,比如:给某些商品打个“大促”的标签;指定商品ID查询商品详情;
  3. 往西:根据召回策略、用户画像、模型等协同过滤,个性化的给端侧用户曝光这些商品数据。

元数据使用之前的系统设计.PNG

细心的同学,可能已经发现问题所在。

不同的CP,携带的商品数据是不同的,比如:京东电商(有店铺名称shop_title、商品价格price、主图片main_photo、店铺链接shop_url等),汽车之家(有车型model、发动机engine、座位数seat_capacity、折扣价dis_price等),携程酒店(有国家country、城市city、坐标location、星级star、评分review_score等)。

  1. 正因为各CP的商品数据是不同的,造成校验加工等逻辑都要理解特定字段的信息,独立于其他CP;web界面的展示也是要根据特定的字段信息进行展示;

    某一天,产品说“为了KPI考虑,要新增1个字段”,前后端都要适配修改代码;再有某一天,产品兴高采烈的说”又谈下一家CP“,意味着,要引入全新的CP商品数据,更是要对系统大动干戈,可谓“牵一发而动全身”;

    立马,分分钟,程序员拿个大刀,要砍了架构师和产品。打人啦打人啦.gif

  2. 存储层是一张大宽表,各CP的数据都存储在一张表里面,每增加1个新字段,其他CP都要感知,只是填个null,数据冗余严重;且随着商品数据增长,查询性能慢慢就下降了。大宽表.PNG

问题来了

新增1个字段,新增1个CP的时候,怎么搞,可以做到前后端解耦,改动代码尽可能的少呢?

停下来,我们想想是什么造成目前系统强耦合、难以扩展的局面呢?

是数据,是CP数据的差异,导致系统各环节都要理解具体字段的含义,从而做出相对应的响应。好了,“打蛇打七寸”,那有没有什么办法,让系统各环节不直接面对CP数据字段,问题是不是就可以迎刃而解了?

千呼万唤始出来

“不直接面对CP数据字段”,那就是间接呗,间接就是要找一个中间商做代理。哇,这不就是买卖二手房嘛。买家要买房,和一个一个卖家去谈,那不得把买家跑断腿,嘴巴磨秃噜皮呀。这时候,房产中介吭哧吭哧跑出来,跟许多卖家谈好,房子信息挂在我这,帮着你们卖,卖家很开心呀,不用操心了;买家也很高兴,直接在房产中介处挑房子,省时省力。

没有我卖不出的房子.PNG

那对于系统设计而言,这中间商又是誰呢?

元数据,千呼万唤始出来。

那什么是元数据?2021年Facebook都改名为“元宇宙”公司了,有了这个热度,相信大家理解“元”应该不难。简单来说,元数据就是描述数据的数据,如果你还不太理解元数据,点击传送门[^阮一峰老师讲解]。

用在我们系统中,元数据就是从CP众多数据字段中抽取出来的用于定义其特征、内容的结构化的数据。比如,(价格=100元、商品名称=手表、商品优惠劵=满50减2、描述=带显示屏的手表、店铺名称=江南第一店、好评率=98%等)这些元数据的内容值,就可以具象地表达出一个商品信息。

“史诗”系统玩法

有了元数据这么牛牪犇掰的中间商,我们可以重新设计系统。

使用元数据的系统设计.PNG

  1. 从北往南:不同CP数据经过校验加工等逻辑,不再是各CP独立开来;而是由元数据解释后,再流经过统一的校验加工等逻辑。
  2. 往东:web界面的展示,也不再需要理解各CP的具体字段;而是由去理解元数据的key和value之后,再去统一渲染页面,这样页面的展示就可以做到“千篇一致”。
  3. 往西:个性化推荐,不再从大宽表召回数据,而是从各CP的存储表去召回;那怎么知道是召回哪个CP的数据,也是由元数据定义

好了,有了这套新设计的系统,我们再回头看看,之前的痛点,有没有解决?

  • CP新增1个字段的话,只要在CP的存储表加1个字段,而其他CP的存储表不受影响;同时此CP的元数据定义也需要新增字段规则;
  • 系统新引入1个CP的话,只要创建此CP的元数据定义,和存储层表。

可见,有了中间商——元数据,前后端完全解耦,并且再接入一个CP的数据时,各环节代码改动较少。不错,不错,恭喜下自己,有了质的飞越。

元数据定义是怎么定义,才把各环节统一的呢?

元数据定义究竟怎么定义?

思路

就上面举例的业务系统,提供一种元数据定义设计思路,当然会有更优秀的元数据定义的设计,期待你们的评论。

元数据定义.PNG

  1. 商品类型就是CP的概念,每个CP的数据,最终是要把这些数据推荐给端侧用户的,那就是商品,我理解;

  2. 元数据类型,是有很多种的,但不管CP新增多少个,类型只有这么多,比如:文本、国家、数字、金额等;打个比方来加深理解,世界上人口这么多,但只有男人or女人2种而已。

  3. 元数据的查询操作类型,比如,价格price,端侧用户要查询符合[100,200]元之间的京东商品。对于写代码来说,就要写查询语句:select 京东商品信息 from 京东商品表 where price in [100,200];

    有了查询操作类型之后,查询语句就可以泛化成固定语句,查询不同的CP不同的字段,直接替换语句中的成分即可:select CP商品信息 from CP商品表 where 特定字段 查询类型 待匹配数据;。有同学会有疑问了,查询语句这么简单,没必要泛化吧?

    • 从Mysql上来看,确实不复杂。但存储层不是Mysql,是其他的,比如ElasticSearch、HDFS呢,它的API可不简单!
    • 另外,示例的语句只是单个字段,单个CP;而试想下,咱们作为APP用户,选购商品时,筛选条件可是复杂的多,所以有了查询操作类型之后,查询语句就可以泛化,少写很多代码的。
  4. 元数据的校验规则类型,比如,商品价格price的类型是金额,就意味着必须是浮点数,那么就配置金额的校验规则是:内容值必须是float型。

    这就意味着,不管是什么CP商品类型,不管是什么名称字段,只要元数据类型确定了,规则也就确定了。

    详情

    思路如此,元数据定义声明就有了。

    元数据定义详情.PNG 基于此份元数据定义声明,系统的各环节就可以统一业务逻辑,而不用理解具体CP的具体数据字段。这句话有点绕,没关系,咱们掰开来、揉碎了讲:

    举例:1条京东电商商品{title=精美的相册,price=50,shop_title(店铺名称)=半雨烟云杂货铺,score(评分)=10,等等},各环节是怎么应用元数据定义来完成系统运转的呢?

    1. 校验:这条数据进入系统,首先要确认属于哪个商品类型,再根据元数据定义声明来进行校验,符合校验规则的才能进入系统下一个环节。
      • title=精美的相册,“精美的相册”属性String,且小于64个字符,也就是说这个字段是满足的。
      • 倘若京东电商的商品有10个字段,但是元数据定义声明只有9个字段,也就是说明实际商品数据多了些字段,无法校验丢弃。
    2. 加工:可以基于元数据定义声明来包装原始的CP数据,加入业务自己的特色。
    3. web展示:不用关心是哪个CP的哪个字段,而是基于元数据类型来渲染,这样页面效果就是统一的。
      • 金额类型,可以渲染成“数字+币种”,例如10元,20美元;
      • 图片类型,可以渲染成左对齐、右对齐;
      • 经纬度类型,可以直接渲染成具体位置的3:2小方图。

告一段落,精彩继续

行文至此,码字不易。各位看官,若有些许启迪、收获,请点个赞,也欢迎大家评论。埋下个楔子,存储层怎么设计,才能支撑多样化查询、易扩展,且性能杠杠呢?下期再表。

Guess you like

Origin juejin.im/post/7061848805649432606