像LinkedIn这样的网站如何有效地在每个人的姓名旁边显示1/2/3级关系?

我最近通过回答一个简单的问题来糟糕的回答了一个面试问题:像LinkedIn这样的网站如何有效地向您显示每个在网页上显示的人的关系距离(第一/第二/第三)(例如,在人员search结果,工作人员列表在一家公司等)?

我得到了解决scheme的基本“技巧”:find“距离我”是一个常见的操作(例如,在一个页面上20x +,每个login会话100个),所以你可以做一部分“距离我X“,将其caching,然后多次重复使用该caching的部分结果,以使其他操作更便宜。 我也猜测,部分结果很可能是我的二级连接,因为“caching所有三级连接”在RAM和CPU中的成本太高。 </ EDIT>

但是,当试图将这种见解转化为解决scheme时,我想出了一个令人尴尬的答案,涉及到创build持久的caching,这个caching是网站上每个人的二级连接(这将会非常昂贵,而且维护复杂)莫名其妙地绕过布鲁姆filter ,这种方式几乎没有技术意义。 我不会在这样的答案之后雇用自己的!

后来,当我在没有面试压力的情况下思考这个问题时,我想出了一个更合理的答案。

  • build立一个非常快速的方式来获得每批用户ID的第一级连接(批量大小可达〜1000?)。 这可能意味着一个RAM专用服务器集群,它可以将整个networking的第一级连接caching在内存中。 幸运的是,有五千万会员x平均。 每个成员100个连接x每个成员4个字节ID = <25GBcaching在RAM中,这可以通过合理定价的硬件来实现。 而且每天的变化数量将在1%以下,所以保持caching最新不是太难。 (请注意,关系数据库可能是一个不好的select来实现这个caching,因为“大量的随机I / O”访问模式杀死了关系数据库的性能。)

  • 当用户login时,通过获取每个第一级连接的第一级连接来caching他的第二级连接,并且使用散列表(键=第二级ID,值=连接你的第一级连接的数组) 。 同样caching你的第一级连接,这样你就可以通过一次调用将你的第一级和第二级拉回远程caching服务器。 用户ID很容易分区,所以像memcached这样的分布式caching可能会很好地工作。

  • 对于任何用户ID,要查找它是否在您的“networking”中,以及它与您(第一,第二,第三)有什么关系,请执行以下操作:

    1. 如果ID在您的一级连接中,请停止。
    2. 试着在你的caching二级连接哈希表中查找ID。 如果find,返回连接你的连接数组。
    3. 获取ID的第一级连接,并为它们中的每一个重复步骤#2。 将所有结果汇总到一个数组中并返回。
    4. (EDIT)重构为一个批处理实现(“从我到N个不同的用户查找距离”),所以你可以从步骤#3获得所有的远程结果,而不必弥补N个远程调用。 </ EDIT>

但我相信有更好的答案。 你的是啥呢? 如果你想要额外的挑战,试着模拟一个inteview的情况(不能在网上查找解决scheme)。

请注意,这个问题是关于一个最佳的解决scheme,无论LinkedIn今天是如何实际做到的 ,在我写上自己的答案之后,我看了一下。

您可以利用小世界networking的公理来优化这种types的遍历。

小世界networking的特点是“集线器”代表了其他节点的非常密集的互连。 networking中的大多数节点通常要么在几跳之内连接到附近的拓扑结构(距离跳过1-4跳),要么通过一个或多个这样的集线器来路由。 这是小世界networking行为的主要原因之一。

有趣的是,二十世纪七十年代的技术将为此build模做一个公平的工作。 networking数据库模型有效地pipe理这种types的关系。

在临时查询或数据模型维护方面效率不高,因此不受关系数据模型的兴起。

如果你仔细想一下,在SQL中这样做可能会占用大量的处理器资源。

鉴于这一点,它将最终在各地使用,而且这个空间相对便宜…我build议使用Lucene(或Lucene.NET)根据您的语言偏好创build一个索引。 你可以这样做几件事情。

您可以创build树型数据结构,并根据您的需要recursion爬取您的索引,以查找所有父节点或子节点及其父节点或子节点。

或者你可以写出所有的关系,因为他们创造(空间是便宜的概念)。 这将是一次写入过程(你不会经常以任何方式更新)。 创build或撤销关系时,您可以将更新列入您的索引(队列,因为您不想打开写入单个请求…批量索引更新)。 然后你可以阅读这个非常扁平的结构来获得有问题的ID。

随着手中的ID(你从哪个searchtypes执行),你可以去数据库获取周围所需的信息。 然后caching你​​的输出,以进一步减less什么是一个非常快速的search,数据库查询,数据build设……但更快,如果它只是来自caching。

使用诸如Velocity,MemCached或MemCached Win32之类的function,在整个Web场中进行集中caching。

我不确定表的结构或系统的复杂性,但这里是一个使用recursionCTE的简单SQL Server示例:

DECLARE @People table (PersonID int, Name varchar(10)) DECLARE @Network table (PersonID int, NetworkedPersonID int) INSERT INTO @People VALUES (1,'AAA') INSERT INTO @People VALUES (2,'BBB') INSERT INTO @People VALUES (3,'CCC') INSERT INTO @People VALUES (4,'DDD') INSERT INTO @People VALUES (5,'EEE') INSERT INTO @People VALUES (6,'FFF') INSERT INTO @People VALUES (7,'GGG') INSERT INTO @People VALUES (8,'HHH') INSERT INTO @Network VALUES (1,2) INSERT INTO @Network VALUES (1,3) INSERT INTO @Network VALUES (2,5) INSERT INTO @Network VALUES (2,7) INSERT INTO @Network VALUES (4,8) INSERT INTO @Network VALUES (7,8) INSERT INTO @Network VALUES (7,3) INSERT INTO @Network VALUES (8,9) DECLARE @TargetPersonID int SET @TargetPersonID=1 ;WITH NetworkLevels AS ( SELECT NetworkedPersonID,1 AS NetworkLevel FROM @Network WHERE PersonID=@TargetPersonID UNION ALL SELECT n.NetworkedPersonID, l.NetworkLevel+1 FROM @Network n INNER JOIN NetworkLevels l ON n.PersonID=l.NetworkedPersonID WHERE l.NetworkLevel<=2 ) SELECT * FROM NetworkLevels 

OUTPUT:

 NetworkedPersonID NetworkLevel ----------------- ------------ 2 1 3 1 5 2 7 2 8 3 3 3 (6 row(s) affected) 

执行

 DistanceCategory(A,B): { 1, 2, 3+} 

使用连接是双向的事实。

将一级连接作为sorting列表存储在某些KV疼痛中:

 Key: [UserFromId,UserToId]. Value: UserToId 

伪代码:

 DistanceCategory(A,B) { if ( exists([A,B]) ) return 1; if ( firstCommonElement(getAll([A,B]), getAll([A,B])) != null ) return 2; return 3; } 

复杂度:O(C1 + C2)。 C1,C2 – 两个用户的连接数。

LinkedIn的数据是不是代表一个巨大的图表? 当一个人login时,系统会处理它的节点,然后通过在3个层次上进行广度优先遍历,系统会将这些节点保存为一个集合(以及哪个关卡信息),当一个人出现在网页上时,系统在这个节点集上进行查找,并给出关系距离。

这是我的猜测。 请随意指出,这是不切实际的。