[关闭]
@yanglfyangl 2018-07-19T03:11:24.000000Z 字数 4341 阅读 683

支持引擎之 -- 关系引擎 RelationEngine

在这里的关系引擎的重点还是关系存储引擎,并不是关系查询引擎

底层存储引擎选择

  • 前期用Redis开发,功能调试
  • 上线前转成Pika+Redis模式

需要支持如下功能

  • 存储基本关系
  • 关系列举(尽量不要有上限)
  • 基本关系查找
  • 共同关系查找
  • WIR关系查找
  • 支持GEO查找
  • 支持一定的排行榜能力

目前不支持的

  • 事务
  • 超过2跳的查询(这部分最好用图来做)

基于关系的特点(可失败,由用户补偿),所以第一期暂时不支持事务。

接口定义

RelationEngine

  1. /*
  2. 为了让关系的命名更加合理,命名最好符合如下模式
  3. "node名" +"关系" + ”node名“
  4. */
  5. class NodeType:{
  6. public String User = "User"
  7. public String Circle = "Circle"
  8. public String Crowd = "Crowd"
  9. }
  10. class RelationEnum:{
  11. public String BeFriend = "ToBeFriends"
  12. public String UserJoinToCircle = "UserJoinToCircle"
  13. public String CrowdJoinToCircle = "CrowdJoinToCircle"
  14. }
  15. //如果是双向,则建立两个set,将关系全存下来。
  16. //如果是单向,则只存一方关系即可。
  17. class Direction:{
  18. public int Single = 0;
  19. public int Both = 1;
  20. }
  21. /*
  22. 我称为Who In Reation搜索,比如:
  23. 解释一下什么是Who In Relation搜索
  24. 1. "Who in Relation"并不是传统意义上的搜索,他还是通过key来找value的一种方式。
  25. 2. 主要支持的都是通过K-V的交并集等操作能做到的结果。
  26. 下面个例子:
  27. 想搜索我在某个圈子中的好友,可以这样做
  28. {
  29. whoID = 人的ID;
  30. whoRelation = RelationEnum.ToBeFriends
  31. inID = 圈子ID;
  32. inRelation = RelationEnum.UserJoinToCircle;
  33. }
  34. 实现方式是:
  35. 取出两个Set来,做交集操作。
  36. */
  37. class WIRCriteria:{
  38. String whoID;
  39. RelationEnum whoRelation;
  40. String inID;
  41. RelationEnum inRelation;
  42. }
  43. class RelationEngine:{
  44. /*
  45. 更新节点的信息。
  46. 1. 如果不存在,则创建;
  47. 2. 如果存在,则更新;
  48. */
  49. public bool updateNodeBasicInfo(String type,String nodeID, Map<String, String> info);
  50. /*
  51. 删除节点
  52. 为了达到性能尽可能高,删除节点时只做
  53. 1. 删除节点基本信息;
  54. 2. 删除本节点对应的关系;
  55. */
  56. public bool deleteNode(String nodeID);
  57. /*
  58. 建立关系
  59. 结果为在KV库中增加
  60. 1. Key为:
  61. A. 主动部分(比如加入群,是人主动)
  62. "rel_"+relation+"_ID_"+fromID+""
  63. 意思是:由这个fromID来drive了这样一个relation
  64. B. 包含/被动部分(比如加入群,是群)
  65. "rel_"+relation+"_ID_"+toID+""
  66. 意思是:某个toID中,包含了某个relation中加入了新值。
  67. 2. value格式为:Set
  68. */
  69. public Boolean joinRelation(String fromID, String toID, RelationEnum relation, Direction direction);
  70. /*
  71. 退出关系
  72. */
  73. public Boolean quiteRelation(String fromID, String toID, RelationEnum relation, RelationDirection direction);
  74. /*
  75. 获取关系列表
  76. */
  77. public List<String> getRelationList(String fromID, String toID, RelationEnum relation, String startID, int count);
  78. /*
  79. 获取两两共同关系,例如:共同好友,共同圈子,共同社群。。。
  80. */
  81. public List<String> getCommonRelations(String id1, String id2, RelationEnum relation);
  82. /*
  83. 获取WIR关系
  84. */
  85. public List<String> searchWIR(WIRCriteria cirteria);
  86. }

GeoHandler (基于RelationEngine),基于关系的位置查询。

  1. /*
  2. 稍复杂些的查询逻辑用GEOCriteria
  3. 主要业务逻辑是
  4. 1. 通过BaseQuery和Relation各做一次搜索
  5. 2. count最大值为1000
  6. 3. 得到结果后做交集
  7. 4. 交集结果小于10时,但结果超过1000时,则再做一次搜索。
  8. */
  9. class GEOCriteria:{
  10. BaseQuery:{
  11. groupName
  12. lat
  13. lon
  14. radio
  15. }
  16. Relation:{
  17. String whoID;
  18. RelationEnum relation;
  19. }
  20. }
  1. /*
  2. 更新某个节点的 GPS相关信息
  3. 如果不设置分组,则是放到大组中。(比如默认查找人员,或圈子等,不需要设置分组名)
  4. */
  5. public bool updateNodeGeoInfo(String groupName, String nodeID, String lon, String lat);
  6. /*
  7. 获取某个组中按中心点和半径的”Node"列表
  8. */
  9. public List<String> searchByLocation(String groupName, String lon, String lat, double radio, int count)
  10. /*
  11. 按结果进行搜索
  12. */
  13. public List<String> searchByCriteria(GEOCriteria cirteria)

RankingHandler(基于RelationEngine)基于关系的排行榜

  1. /*
  2. 更新某个节点的 "Scroe"(分数) 相关信息
  3. */
  4. public bool updateNodeScroeInfo(String GroupName, String nodeID, score);
  5. 其它的与GeoHandler类似。。。

示例

创建用户

  1. //生成或从mysql获取用户ID
  2. RelationEngine engine = RelationEngine.getInstance();
  3. engine.updateNodeBasicInfo(NodeType.User, usrID, userInfoDTO);

创建圈子

  1. //生成或从库中获取圈子ID
  2. RelationEngine engine = RelationEngine.getInstance();
  3. engine.updateNodeBasicInfo(NodeType.Circle, cirID, userInfoDTO);

人加入圈子

  1. RelationEngine engine = RelationEngine.getInstance();
  2. engine.joinRelation(usrID, circleID, RelationEnum.UserJoinToCircle, Direction.Both)

查找我周围的...

  1. RelationEngine engine = RelationEngine.getInstance();
  2. engine.GetGeoHandler().updateNodeGeoInfo(userID, ..., "UserLocation");
  3. //查找当前GPS坐标周围的人
  4. List<String> ret = engine.GetGeoHandler().searchByLocation("UserLocation", 100);
  5. //查找当前GPS坐标周围的我的好友
  6. Criteria criteria = new Criteria()
  7. criteria.setBaseQuery();
  8. criteria.setRelationQuery();
  9. List<String> ret = engine.GetGeoHandler().searchByCriteria(criteria);

获取XXX排行榜...

  1. RelationEngine engine = RelationEngine.getInstance();
  2. engine.GetRankHandler().updateNodeRankInfo(userID, ..., "UserLocation");
  3. //获取某个排行榜的用户列表
  4. List<String> ret = engine.GetRankHandler().getListByRank("UserLocation", 100);
  5. //获取某个排行榜中某个ID所在的位置
  6. int ret = engine.GetRankHandler().getListByRank("UserLocation", myID);
  7. //获取我好友在某个排行榜中的排行
  8. Criteria criteria = new Criteria()
  9. criteria.setBaseQuery();
  10. criteria.setRelationQuery();
  11. //获取排行榜ID
  12. String rankID = engine.GetGeoHandler().queryRank(criteria);
  13. //获取某个排行榜中某个ID所在的位置,-1表示未上榜;-2表示排行榜已过期不存在了。
  14. int ret = engine.GetRankHandler().getListByRank(rankID, myID);

获取某个时间戳之后的关系变化

有很多类似这样的场景,比如

  • 获取我的好友列表,但不是整体的好友列表,而是某个时刻之后的好友列表。
  • 不光是好友列表,还有变化,比如说是删除了好友,好友信息更新了等等操作。
  • 还需要支持分页

如果不能支持,则对于移动端缓存数据,几乎就变成了不可能,特别是想支持很大数据量的情况。

所以,关系引擎需要支持类似的操作记录。

方法:

  • 每个releation都建立一个###_log 的zset, scroe为当前的时间戳。
  • 写入格式为:[ID_ACTION, 时间戳]
  • 对于所有的add, delete操作,写入入对列。
  • 对于update操作。分两种
    1. 全局性的node update更新,放到一个全局性的update更新 log中。(全局)
    1. 某个特殊关系的,直接写入关系对应的当中去。(局部)

需要按某个时间戳获取时

  • 将全局与局部的进行并集。
  • 找出前端时间戳之后的数据。
  • 将数据进行去重。
  • 进行后续操作。
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注