对实时推荐引擎来说,关系数据库已过时,图数据库才是王道!( 二 )


对实时推荐引擎来说,关系数据库已过时,图数据库才是王道!
文章图片
如你所见 , 实体与它们之间的关系清晰了然 。
与关系数据库相比 , 通过图数据库检查和深入了解数据的难度更低 , 速度更快 , 正是因为不同节点之间建立的这种关系网 。
推荐产品:SQL查询与Cypher查询
下面 , 我们根据上述数据模型创建一个查询 , 向某个用户推荐某个产品 。 我们的推荐基于以下信息:用户给予最高评分的产品 , 以及浏览相同产品后同样给出最高评分的其他用户 。 这也是推荐引擎可以使用的最简单查询之一 , 因为这个查询可以通过社区检测、计算皮尔逊相关系数和机器学习进行更深入的挖掘 。
这个SQL查询需要使用复杂的JOIN操作连接表 , 如下所示:selectB.*fromuserUser1joinratingRating1onUser1.user_id=Rating1.idandRating1.value=https://pcff.toutiao.jxnews.com.cn/p/20221124/5joinproductAonA.id=Rating1.product_idjoinratingRating2onRating2.product_id=A.idandRating2.value=5joinuserUser2onUser2.id=Rating2.user_idandUser2.id<>User1.idjoinratingRatingBonRatingB.user_id=User2.idandRatingB.value=5joinproductBonB.id=RatingB.product_idWHEREUser1.id=1;
JOIN操作很容易出错 , 而且速度很慢 , 计算量大 。 每个JOIN操作的时间复杂度为O(M*log(N)) , 其中M代表一个表中的记录数 , N代表另一个表中的记录数 , 这意味着我们需要扫描两个表中的所有行 , 并尝试通过唯一的键连接二者 。 随着推荐引擎中数据的增长 , 需要连接多个表的查询和分析将越来越复杂 , 关系数据库的速度也会越来越慢 。
每个图数据库都使用自己的查询语言 , 而在图数据库的世界中 , 最常用的语言是Cypher 。 获取相同结果的Cypher查询如下所示:MATCH(pA:PRODUCT)(pB:PRODUCT)MATCH(n2:USER{id:1})-[r3:Rated{"rating":5}]->(pb)WHEREn1.id!=n2.idRETURNpB;
在图中搜索节点的过程称为图遍历 , 图遍历的复杂度为O(K) , 其中K代表一个节点与其他节点的连接数 。 高度优化是无索引邻接概念的结果 , 这是图数据库最重要的概念之一 。 在查找图中的相邻节点时 , 图数据库会执行指针跳跃 , 即直接遍历内存 , 这是最快的查看关系的方式 。 为了直接遍历内存 , 关系会以物理RAM地址的形式存储起来 。 最重要的是 , 关系是在创建数据时创建的 , 而不是查询时 。
图数据库不必使用任何其他数据结构或索引 , 即可从任意节点跳至相邻节点 。 在设计推荐引擎时 , 用户和他们购买的产品之间的连接会作为固定的物理RAM地址保存起来 。 而将相关节点存储在相邻的内存地址内 , 可以进一步提升性能 , 从而最大限度地提高数据缓存到CPU的概率 。
研究表明 , 使用图数据库向相距三个连接的用户推荐产品的速度 , 比使用关系数据库快180倍以上 。
灵活性
关系数据库依赖于之前所创建的预定模式 , 一旦出现意外或计划外的状况 , 关系数据库的模式就无法灵活应对 。 但在推荐引擎起着关键作用的零售业务中 , 我们很难预测市场和平台的发展与变化 。
举个例子 , 假设有一家销售船只的公司 , 在现有数据之上构建了一个推荐引擎 。 有一天 , 你想扩大业务 , 开始销售捕鱼设备 。 如果你使用的是关系数据库 , 则需要重新考虑整个数据库 , 因为你必须严格遵守已有的数据模式 。 否则 , 任何不匹配模式的数据都无法存储 。 因此 , 如果原有模式不具有钓鱼线一个非常重要的属性——粗细(不是船只属性) , 则需要重新设计模式 。
为了降低工作量 , 你可以添加可应用到所有产品的所有属性 , 但其中一些属性将是NULL值 , 因为捕鱼设备没有发动机功率或船型等属性 , 而船只通常没有粗细等属性 。 但这样做的问题在于 , 首先会造成内存浪费 , 其次你还需要添加一个过滤器来过滤掉船只 , 或者要通过额外的检查来避免由NULL属性引起的问题 , 这势必会加剧代码的复杂性 。