设计社交网络的数据结构

1: 确定 Use Case 和 约束

Use Cases

  • User 搜索某人然后看到被搜索人的最短路径
  • Service 有高可用

约束和假设

状态假设
  1. Traffic 不是平均分布的
  • 一些被搜索者是更加受欢迎的,某些被搜索者只会被搜索一次
  • 图数据不适用与单个机器
  • 图的分布是轻量级的
  • 一亿个 User
  • 每个 User 平均有 50 个朋友
  • 十亿个搜索每个月

练习使用更传统的系统 - 不要使用 graph-specific solutions, 比如 GraphQL 或者 图数据库 Neo4j

计算使用量
  1. 50 亿 朋友关系
    • 一亿 users * 50 个 friends / user
  2. 每秒 400 个搜索请求

便利转换索引:

  • 每个月250 万秒
  • 1 个请求 / s = 250 万请求 / 月
  • 40 个请求 / s = 1 亿请求 / 月
  • 400 个请求 / s = 10 亿 请求 / 月

2:创建一个 High Level 设计

Design

3: 设计核心组件

Use Case: User 搜索某人,然后得到被搜索人的最短路径

没有上百万 User 和 数十亿 friend 关系的限制,我们可以解决最短路径问题通过使用 BFS 算法

class Graph(Graph):def shortest_path(self, source, dest):if source is None or dest is None:return Noneif source is dest:return [source.key]prev_node_keys = self._shortest_path(source, dest)if prev_node_keys is None:return Noneelse:path_ids = [dest.key]prev_node_key = prev_node_keys[dest.key]while prev_node_key is not None:path_ids.append(prev_node_key)prev_node_key = prev_node_keys[prev_node_key]return path_ids[::-1]def _shortest_path(self, source, dest):queue = deque()queue.append(source)prev_node_keys = {source.key: None}source.visit_state = State.visitedwhile queue:node = queue.popleft()if node is dest:return prev_node_keysprev_node = nodefor adj_node in node.adj_nodes.values():if adj_node.visit_state == State.unvisted:queue.append(adj_node)prev_node_keys[adj_node.key] = prev_node.keyadj_node.visit_state = State.visitedreturn Node

我们不能把所有的 User 都放在同一个机器里面,我们需要 分片的 User (通过 Person Server)
而且使用 Lookup Service 去访问他们

  1. Client 发送请求到 Web Server
  2. Web Server 转发请求到 Search API server
  3. Search API server 转发请求到 User Graph Service
  4. User Graph Service 做下面的事情:
    • 使用 Lookup Service 去寻找 Person Server, 当当前 User的info 被存储过后
    • 寻找合适的 Person Server去接收当前 User的 friend_ids 的 list
    • 运行 BFS 搜索(使用当前 User 作为 source, 而且当前 User的 friend_ids 作为 ids)
    • 从给定的 id 获取 adjacent_node
      • User Graph Service 将需要再次和 Lookup Service沟通,去决定哪一个 Person Service 存储 adjecent_node 数据(匹配给定的 id)

Lookup Service 实现:

class LookupService(object):def __init__(self):self.lookup = self._init_lookup()  # key: person_id, value: person_serverdef _init_lookup(self):...def lookup_person_server(self, person_id):return self.lookup[person_id]

Person Server 实现:

class PersonServer(object):def __init__(self):self.people = {}  # key: person_id, value: persondef add_person(self, person):...def people(self, ids):results = []for id in ids:if id in self.people:results.append(self.people[id])return results

Person 实现:

class Person(object):def __init__(self, id, name, friend_ids):self.id = idself.name = nameself.friend_ids = friend_ids

User Graph Service 实现:

class UserGraphService(object):def __init__(self, lookup_service):self.lookup_service = lookup_servicedef person(self, person_id):person_server = self.lookup_service.lookup_person_server(person_id)return person_server.people([person_id])def shortest_path(self, source_key, dest_key):if source_key is None or dest_key is None:return Noneif source_key is dest_key:return [source_key]prev_node_keys = self._shortest_path(source_key, dest_key)if prev_node_keys is None:return Noneelse:# Iterate through the path_ids backwards, starting at dest_keypath_ids = [dest_key]prev_node_key = prev_node_keys[dest_key]while prev_node_key is not None:path_ids.append(prev_node_key)prev_node_key = prev_node_keys[prev_node_key]# Reverse the list since we iterated backwardsreturn path_ids[::-1]def _shortest_path(self, source_key, dest_key, path):# Use the id to get the Personsource = self.person(source_key)# Update our bfs queuequeue = deque()queue.append(source)# prev_node_keys keeps track of each hop from# the source_key to the dest_keyprev_node_keys = {source_key: None}# We'll use visited_ids to keep track of which nodes we've# visited, which can be different from a typical bfs where# this can be stored in the node itselfvisited_ids = set()visited_ids.add(source.id)while queue:node = queue.popleft()if node.key is dest_key:return prev_node_keysprev_node = nodefor friend_id in node.friend_ids:if friend_id not in visited_ids:friend_node = self.person(friend_id)queue.append(friend_node)prev_node_keys[friend_id] = prev_node.keyvisited_ids.add(friend_id)return None

我们可以使用 public REST API:

$ curl https://social.com/api/v1/friend_search?person_id=1234

Response:

{"person_id": "100","name": "foo","link": "https://social.com/foo",
},
{"person_id": "53","name": "bar","link": "https://social.com/bar",
},
{"person_id": "1234","name": "baz","link": "https://social.com/baz",
}

4: 扩展设计

Better Design

我们可以有以下优化点:

  • 存储完整或部分BFS遍历,以加速后续在内存缓存中的查找
  • 批处理计算然后存储完整或部分BFS遍历,加速在 NoSQL 数据库中的子序列查询
  • 减少机器跳跃通过批量查找朋友在同一个域名下的 Person Server
    • 通过Location分片的 Person Server去进一步的提高,正如朋友普遍都住的比较近
  • 在同一时刻做两个 BFS 搜索,一个从 source开始,另一个从 destination 开始,然后 merge 这量个 path
  • 人们从 BFS 开始搜索大量的 friend. 然后他们是更喜欢去减少 分页的数字(在当前用户和搜索结果)
  • 在询问用户是否要继续搜索之前,根据时间或跳数设置一个限制,因为在某些情况下,搜索可能需要相当长的时间
  • 使用Neo4j等图形数据库或GraphQL等特定于图形的查询语言(如果没有限制阻止使用图形数据库)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/634970.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

canvas绘制N角形,锯齿状

查看专栏目录 canvas实例应用100专栏,提供canvas的基础知识,高级动画,相关应用扩展等信息。canvas作为html的一部分,是图像图标地图可视化的一个重要的基础,学好了canvas,在其他的一些应用上将会起到非常重…

在线扒站网PHP源码-在线扒站工具网站源码

源码介绍 这是一款在线的网站模板下载程序,也就是我们常说的扒站工具,利用它我们可以很轻松的将别人的网站模板样式下载下来,这样就可以大大提高我们编写前端的速度了!注:扒取的任何站点不得用于商业、违法用途&#…

kafka参数配置参考和优化建议 —— 筑梦之路

对于Kafka的优化,可以从以下几个方面进行思考和优化: 硬件优化:使用高性能的硬件设备,包括高速磁盘、大内存和高性能网络设备,以提高Kafka集群的整体性能。 配置优化:调整Kafka的配置参数,包括…

免费的爬虫软件【2024最新】

在国际市场竞争日益激烈的背景下,国外网站的SEO排名直接关系到网站在搜索引擎中的曝光度和用户点击量。良好的SEO排名能够带来更多的有针对性的流量,提升网站的知名度和竞争力。 二、国外网站SEO排名的三种方法 关键词优化: 关键词优化是SEO…

Red Hat Enterprise Linux 9.3 安装图解

引导和开始安装 选择倒计时结束前,通过键盘上下键选择下图框选项,启动图形化安装过程。需要注意的不同主板默认或者自行配置的固件类型不一致,引导界面有所不同。也就是说使用UEFI和BIOS的安装引导界面是不同的,如图所示。若手动调…

ITK + ANT,无法显示三维

背景:之前用ANT保存ima格式的数据,选择的是保存所有的序列 用python将dicom转为nii的格式, import nibabel as nib import torch"""不管是nii还是nii.gz都是二维的,为啥呢"""fobj nib.load("…

Qt弹框展示

1.相关说明 文件选择弹框、目录选择弹框、保存文件弹框、颜色选择弹框、字体选择弹框、进度条弹框、输入对话框、标准消息框等 2.相关界面 3.相关代码 #include "widget.h" #include "ui_widget.h" #include <QFileDialog> #include <QProgressD…

Red Hat Enterprise Linux 6.10 安装图解

引导和开始安装 选择倒计时结束前&#xff0c;通过键盘上下键选择下图框选项&#xff0c;启动图形化安装过程。需要注意的不同主板默认或者自行配置的固件类型不一致&#xff0c;引导界面有所不同。也就是说使用UEFI和BIOS的安装引导界面是不同的&#xff0c;如图所示。若手动调…

go 语言(九)----struct

定义一个结构体 type Book struct {title stringauth string }结构体使用 package mainimport "fmt"//定义一个结构体 type Book struct {title stringauth string }func main() {var book1 Bookbook1.title "Golang"book1.auth "zhang3"fmt…

使用 TiUP 部署 TiDB 集群

TIDB优点 支持分布式且支持事务的关系型数据库&#xff0c;不用考虑分库分表 同时满足了可伸缩&#xff0c;高可用&#xff0c;关系型&#xff0c;支持事务。 基本上按官网的文档来就行了。 在线部署 以普通用户身份登录中控机。以 tidb 用户为例&#xff0c;后续安装 TiUP …

用git bash调用md5sum进行批量MD5计算

对于非常大的文件或者很重要的文件&#xff0c;在不稳定的网络环境下&#xff0c;可能文件的某些字节会损坏。此时&#xff0c;对文件计算MD5即可以校验其完整性。比如本次的 OpenStreetMap 导出包&#xff0c;我的学弟反馈通过网盘下载无法解压&#xff0c;并建议我增加每个文…

MOS管和IGBT管的定义与辨别

MOS管和IGBT管作为现代电子设备使用频率较高的新型电子器件&#xff0c;因此在电子电路中常常碰到也习以为常。可是MOS管和IGBT管由于外形及静态参数相似的很&#xff0c;有时在选择、判断、使用容易出差池。MOS管和IGBT管可靠的识别方法为选择、判断、使用扫清障碍&#xff01…

android使用相机 intent.resolveActivity returns null

问题 笔者使用java进行android开发&#xff0c;启动相机时 intent.resolveActivity returns null takePictureIntent.resolveActivity(getPackageManager()) null详细问题 笔者使用如下代码启动相机 // 启动相机SuppressLint("LongLogTag")private void dispatc…

【LGR-172-Div.4】洛谷入门赛 #19(A—H,c++详解!)

文章目录 【LGR-172-Div.4】洛谷入门赛 #19A.分饼干 I题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 样例 #2样例输入 #2样例输出 #2 提示样例解释 1样例解释 2数据范围与约定思路: 代码 B.分饼干 II题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 样例 #2样…

2024--Django平台开发-订单项目管理(十四)

day14 订单管理系统 1.关于登录 1.1 UI美化 页面美化&#xff0c;用BootStrap 自定义BooStrapForm类实现。 class BootStrapForm:exclude_filed_list []def __init__(self, *args, **kwargs):super().__init__(*args, **kwargs)# {title:对象,"percent":对象}fo…

电路复习总结

又到了个人最喜欢的电路复习环节&#xff0c;废话不多说一个个复习过来 1.电路基本概念 2.向量表示 现在在看美少女老师教学某一种正弦交流电的办法学的就很舒服 知道复数和极坐标还挺重要可以便于后面数值计算 2024年1月18日19:44:57 根本整理不出来什么东西i 在学习中逐…

测试 ASP.NET Core 中间件

正常情况下&#xff0c;中间件会在主程序入口统一进行实例化&#xff0c;这样如果想单独测试某一个中间件就很不方便&#xff0c;为了能测试单个中间件&#xff0c;可以使用 TestServer 单独测试。 这样便可以&#xff1a; 实例化只包含需要测试的组件的应用管道。发送自定义请…

龙芯+RT-Thread+LVGL实战笔记(30)——电子琴演奏

【写在前面】正值期末,笔者工作繁忙,因此本系列教程的更新频率有所放缓,还望订阅本专栏的朋友理解,请勿催更。笔者在此也简要声明几点: 有些硬件模块笔者并没有,如LED点阵、压力传感模块、RFID模块等,因此这些模块的相关任务暂时无法给出经过验证的代码。其实,教程进行…

排序之归并排序

在计算机科学中&#xff0c;排序是一种常见的操作。它用于将一组元素按照一定的顺序排列。归并排序是一种非常有效的排序算法&#xff0c;其时间复杂度为O(nlogn)&#xff0c;空间复杂度为O(n)。本文将详细介绍归并排序的工作原理和实现方法。 归并排序的工作原理 归并排序的…