于springboot的多人聊天系统
系统使用技术:springboot前端技术:css、js等开发工具:idea数据库:mysql5.7项目介绍:该系统基于SpringBoot,采用Mysql数据库,通信协议WebSocket,使用Maven管理项目,功能点满足毕设/课设需要,也可根据需要在原有基础上二次开发。主要功能有用户模块:用户注册、用户登录,好友模块:添加好友、好友申请及处理,消息模块:获取消息记录。下面我们来看看功能。系统登录: 系统注册好友列表可以查看待添加好友、好友列表、以及陌生人等 好友添加可以选择陌生人添加好友消息发送发送消息给好友 接受消息接受来自好友的消息数据库以上就是基于Java的博客系统的部分功能展示,从整体上来看,界面设计简洁大方,交互友好,数据库设计也很合理,规模适中,比较适合毕业设计和课程设计的相关应用。
设计B/S 模式的即时通讯软件
1. 引言
目前,即时通讯市场已经从多家竞争时代走向垄断统治时代。大多数即时通讯软件已随着时间的推移逐渐退出竞争舞台。即时通讯软件的用户群也逐渐增多。这标志着即时通讯市场已逐渐走向成熟。
在即时通讯的发展过程,有两个很重大的特点:
1、交互的信息日益复杂:从早期的文字、文件、音频到现在日益流行的视频信息。
2、交互的群体日益普遍:从早期的专业人员到现在的人民大众以及企业级的需求。
人类对信息的实时性需求,注定即时通讯必将成为历史的潮流,他的广泛性将和移动通讯一样,不可逆转。通过即时通讯,我们可以在网络上,更加方面地交互信息,即时交互一切需要交互的信息。
随着未来互联网的发展完善,即时通讯将会应用到更多的领域,并且发挥重要的作用。
2. 系统分析
2.1 系统功能要求
下面将着重对系统功能和设计做详细介绍。主要包括以下四个模块 :用户注册/登录模块、好友列表及状态实时检测模块、单人聊天模块、群组聊天模块、好友查找/添加模块、退出模块。其中用户注册/登录模块包括用户注册、用户登录两个子模块;好友列表及状态实时检测模块包括好友列表更新、好友在线状态检测、新消息检测、状态设置四个子模块;单人聊天模块包括消息发送、消息更新两个子模块;群组聊天包括消息发送、消息更新、群组成员列表更新三个子模块。
(1) 用户注册/登录模块
① 用户注册
该模块实现用户的注册,用户填写完系统要求的资料后,系统赋予每个用户一个唯一的六位数帐号,并且将所有信息写入数据库。
② 用户登录
该模块实现用户的登录,在验证用户提交的用户名密码后,若验证通过,则将登录标识符设为已登录,若不通过,则返回登录界面。
(2) 好友列表及状态实时检测模块
① 好友列表更新
该模块实现隔一定时间向服务器请求数据,取得最新的好友列表并将最新内容显示在好友列表里面。
② 好友在线状态检测
该模块实现隔一定时间向服务器请求数据,取得好友是否在线标识,若用户在线,则显示彩色头像,若不在线,则显示灰色头像。
③ 新消息检测
该模块实现隔一定时间向服务器请求数据,取得用户最后已读信息时间,并对比即时消息发送时间,若为新消息,则发送该消息的好友头像闪动,否则,头像为静止状态。双击头像可打开与该用户的聊天窗口。
④ 状态设置
该模块实现用户可以通过点击单选按钮直接设置自己的在线或隐身状态。
3) 单人聊天模块
① 消息发送
该模块实现在聊天窗口输入文字,点击“发送”按钮或用Ctrl+回车组合键即可发送消息,可设置文字大小、颜色、粗体、斜体、下划线等样式。
② 消息更新
该模块实现隔一定时间向服务器请求数据,取得发给自己的即时消息,并显示在聊天窗口里。
(4) 群组聊天模块
① 消息发送
该模块实现在聊天窗口输入文字,点击“发送”按钮或用Ctrl+Enter 组合键即可发送消息,可设置文字大小、颜色、粗体、斜体、下划线等样式。
② 消息更新
该模块实现隔一定时间向服务器请求数据,取得即时群组聊天消息,并显示在聊天窗口里。
③ 群组成员列表更新
该模块实现隔一定时间向服务器请求数据,取得该群最新的成员列表,并显示在聊天窗口的成员列表里面。
(5) 好友查找/添加模块
该模块实现用户可通过输入用户号码或昵称等条件来搜索好友,并实现对搜索出的用户的资料查看和添加好友功能。
(6) 退出模块该模块实现在用户关闭在线聊天窗口或点击“退出”时将用户的状态设置为离线并且返回登录界面。
2.2 系统的可行性分析 制作系统前,在充分调研的基础上做出系统的开发目标,即系统建立后所要求达到的运行指标,这是系统开发和评价的依据。系统充分考虑了用户对软件功能的需求,为系统设计了一些实用关键的功能。不足之处是,系统目标不可能在短时期内就会实现的非常具体 , 它还将在以后的开发、维护过程中逐步被完善。但从总体上看,系统拥有较强的可行性。系统的目标确定后,从以下几个方面对系统的实现进行可行性分析:
2.2.1 技术可行性
系统用到的硬件、软件性能要求、环境条件都是能具体实现的,再加上对开发系统所用到的技术做了进一步的研究,遇到困难时得到了指导老师和同学的协助,所以说在技术方面是可行的。
2.2.2 经济可行性
相关技术书籍可以在学校图书馆借到或在书店买到,并且设计开发系统的电脑可以在机房免费使用,所以要完成该系统在经济上是可以承受的。
2.2.3 运行可行性
系统的主要功能开发完以后,对系统进行了调式,运行的结果基本能达到用户的要求,相信在以后进一步的完善中会得到令人更加满意的结果,尽管目前系统还有需要改进的地方,但是初步的结果表明运行方面是可行的。按上述几个方面进行可行性分析、研究后,得到结论:系统开发是可行的。
2.3 系统特点
运行速度快、效率高、界面友好、注重用户体验、操作方式与主流IM 软件类似;并且该系统不需要用户安装客户端,通过WEB 浏览器即可使用其所有功能[3]。
3. 系统总体设计
3.1 系统总体设计
基于B/S 模式的即时通讯软件的设计开发遵循一个原则,就是紧密地结合用户的需求和体验。该系统是在PHP+MySQL 环境下进行开发的,设计了多个模块,由Ajax 技术实现其数据的实时传输,利用客户端的动作触发JS 函数实现大部分功能。
3.1.1 系统结构图
在系统的概要设计阶段,最主要的就是确定系统的整体结构。确定了系统的总体结构,也就相当于确定了系统的总体目标,根据这个总体目标,再一一设计各子模块。系统总体结构图如图1:
4. 数据库设计
在设计应用程序之前,应先组织数据。通过建立数据库来管理数据,既能增加数据的可靠性,也便于系统的开发。创建数据库的优点:创建数据库是实现数据集成的有效手段,数据库按一定的结构集中了应用系统中的数据,使之更便于统一管理;可以定义数据词典的功能;允许在数据库中建立永久关系。
数据库设计的任务是确定系统所需的数据库。数据库是表的集合,通常一个系统只需一个数据库。数据库设计可分为逻辑设计与物理设计两个步骤。第一步确定数据库所包含的表及其字段。第二步确定表的具体结构,即确定字段的名称、类型及宽度;此外还要确定索引,为建立表的关联准备条件。
4.1 数据库逻辑设计
即时通讯软件要处理大量的信息或数据,为了使信息的存取、管理工作更加方便、安全、快捷,特别为该系统设计了一个数据库,并且设置了许多数据库表,目的是为了节省服务器空间,便于用户的查询和管理。 本系统的数据库表为:users(用户信息表)、friendlist(好友信息表)、groups(群组信息表)、grouplist(群组成员信息表)、chatlist(个人聊天信息表)、readflag( 已读信息标识表)、glist(群组 聊天信息表)。这些表中都存在一个主键id,id 是唯一的,不能重复,这样可以保证数据录入的准确性,系统中的id 可自动增加的,每设置即添加完一条记录后,id 会自动加1,避免在输入时会重复输入同一个id,造成数据的丢失。同时建立外键以实现数据完整性的约束。
4.2 数据库的创建与连接
在上面的章节里只做了数据的收集、整理和加工。并没有对数据库进行操作 ,而应用程序一般都与数据库有着密切的联系,所有表的创建都要在数据库中保存才能被程序调用。因此,数据库管理在应用程序开发的初级阶段是非常关键的。下面对数据库进行操作。通过MySQL 的第三方管理软件phpMyAdmin 建立数据库,命名为my_db,创建完数据库后,便可以在创建的数据库中进行所需的数据表的创建、编辑、删除等操作。由于系统绝大部分模块都需要操作数据库,因此将连接数据库的代码封装到一个php 文件中,需要链接数据库的模块直接引用该文件即可,减少代码冗余。该文件定义的连接数据库的函数是:
function DBconnect(){
$conn=mysql_connect('localhost:3306','root','password');
if($conn) mysql_select_db(my_bd);//my_db 为数据库名称
return $conn;
}
5. 结论
开发一套即时通讯方案需要投入大量的人力物力,如果是公司商用的话,可以考虑商用的开发包,如(Anychat ) http://www.anychat.cn/ 可以节约开发时间,缩短项目开发周期;节省开发费用;减少人力资源投入平台自主开发,目前Anychat正在做Android平台的移植,远期将支持Symbian、iPhone等平台。利用AnyChat SDK的跨平台特性,实现嵌入式硬件设备(ARM平台)与Internet上的Windows平台实现语音、视频的交互。现已支持Windows、Unix、Linux(x86)、Linux(ARM)以及Windows Mobile平台,实现嵌入式硬件设备(ARM平台)与Internet上的Windows平台实现语音、视频的交互。AnyChat SDK的跨平台特性。 随着开发工具的改进和数据库管理技术的提高。相信即时通讯软件也将不断向前发展,将更加成熟!
本文转自 fanxiaojun 51CTO博客,原文链接:http://blog.51cto.com/2343338/460405,如需转载请自行联系原作者
基于springboot的简易聊天系统
系统使用技术:springboot前端技术:thymeleaf、js、css、layui开发工具:idea数据库:mysql5.7项目介绍:该系统基于springboot,mysql数据库,页面美观,可以在此基础上进行二次开发。下面我们来看看部分相关功能。系统登录:聊天可以选择某个好友进行聊天,聊天内容可以是文字、表情、图片,或者带有格式的文字等。添加好友可以搜索好友名称进行添加好友数据库以上就是部分功能展示,系统适合二次开发,界面设计简洁大方,交互友好,比较适合毕业设计和课程设计的相关应用。好了,今天就到这儿吧,小伙伴们点赞、收藏、评论,一键三连走起呀,下期见~~
关系型数据库中好友关系的设计
接到需求,设计一群注册用户的好友关系,各自要能查询到自己的好友列表。最早想过用图数据库来进行好友关系存储,但身边没有成熟的案例,网上的资料也比较少。所以还是决定采用传统关系型数据库MySQL来进行设计。 好友关系,如果简单设计成一张表的话,随着注册用户的增多,好友关系势必会呈指数级增加,当系统中用户为10个人时,那么完全添加好友的话,关系数据(假设A和B是好友只有一条数据)则为(9+8+...+1)即55条;当系统中注册用户的数量增长到1000个人时,关系数据最大值为(999+998+...+1)即499500。所以如何设计系统,让用户快速查询到好友关系,是个难点。 这里给出一种思路:根据注册用户的某一个字段(如用户ID),将它的所有好友关系集中存放到一张表中,而不同的用户,会根据这个ID的不同,将它们的好友关系分别散列在不同的数据表中。这样达到了将数据表数据分散,减轻单表压力的目的。但是,按照这个思路设计时,好友的关系必须是双向的,否则A和B的关系,到底是以A的ID进行散列还是B的ID进行散列呢?这样一来,好友的关系数增加了1倍,但是如果散列的足够均衡,这个结果也是可以接受的。市面上很多好友关系在进行设计时,也是采用双向的方式,即A加了B,B同意之后,即建立了双向好友关系,当A删除B的好友关系时,B查询自己的好友列表时,会发现A还在,只有B再删除A,双向的好友关系才会消除。 接下来的工作是如何将这个散列方案实施下来。这里有两个问题,一个是在初期规划时,我们可能并不知道这个社交群体最终的规模,即无法在初期就创建固定数量的关系表,这个表一定是当容量达到一定规模时动态增长的;第二个是,在追求每张表的数据均衡时,我们还要考虑一种情况,即如果初期存入的一批用户被散列到某张表中(即该批用户指向的好友关系存在该表中),而恰好该批用户的好友关系增长速度远远超过其他批次的用户,造成该表数据急速增长的情况,该如何处理?在此背景下,我们来考虑这个被散列计算的字段该怎么设计。 对于以上问题,一个简单的思路如下:假设现在有t_a,t_b,t_c,t_d四张表,都用于存放关系,在它们远没有达到存储规划上限时,我们可以就简单根据现有的各自表的容量,来决定注册用户该字段计算出来该指向哪张表。比如a,b,c现在容量都达到了1000条,而d表的容量才100条,那么在注册用户时,我们就将用户该字段设计成计算结果指向d表。我们还可以设计一个容量因子,在单表数量超过预设上限*该容量因子(如0.7)时,即停止将新用户的字段值设计成计算结果指向该表。另外,如果我们设定一个好友关系上限,如最多500个好友,那么预设的表数据上限除以这个数量限制可以得到这张表最多存放多少个不同的用户,这种做法可以保证这些关系表数量不会异常增长到超过我们预设的上限。 但是,如果我们的系统允许用户无节制的增加好友,那么当用户量不断增长时,上述方案就无法在系统规模不断增长之后还能保证单表关系数量不会突破限制。此时,限制一个用户所有的好友关系在某个单表的方案已经不现实,但我们还是应该尽量这么做,对于单个用户在单表中设置一个关系数量上限,当他的好友关系超过这个数量时,把它存在另外一张表中。那么,如何知道这个用户是否图破了单表存储的设定上限,拥有多张表存储了好友关系,我们可以在注册用户的字段时加一个标记字段,那么查到该用户即可知道这个信息,对于已经突破了单表设定上限的,我们可以再加一张表-突破关系存放表,某用户突破一次,那么在该表中即存一条数据,其中一个字段的值与该用户原先的散列计算值匹配起来可以确定这次突破后存放的新表名称。这样既可快速定位到这些新表的名称,从而查询出所有的好友关系,另外,好友关系表中对单表单用户的好友关系数量做出了限制,所以当有N个表存放了该用户的数据时,那么好友的数量即,(N-1)*固定容量+第N张表的好友数量,所以统计好友总数也不复杂,只需要知道第N张表的数量即可推算出总数。 暂时想到这么多,欢迎评论留言补充思路或者其它拓展点。
社交系统中用户好友关系数据库设计
在社交类系统中,用户与用户的好友关系的设计必不可少,那么如何设计好友的数据库至关重要,本篇文章带大家学习一下相关的设计方案。基础分析第一步,有一张用户表,表内包含用户的基本信息,比如账号、姓名、性别等信息。这里用tb_user表示用户信息表。ID 用户名1 张三2 李四3 王五4 赵六第二步,需要将用户与用户直接建立好友关系。这里有两种情况:单向好友关系、互为好友关系。- 单向好友关系就是张三在李四的好友列表中,但李四没有在张三的好友列表中;- 互为好友关系,如果张三和李四为好友,则双方都在彼此的好友列表中;好友关系设计无论上面两种关系的哪一种,好友关系表都可以使用下面的设计,表tb_friend:ID user_id friend_id1 1 22 1 3示例中,张三拥有李四和王五两个好友。单向好友模式如果是单向好友模式,那么两个人互为好友关系则插入的数据应该是这样:ID user_id friend_id1 1 22 2 1也就是张三是李四的好友,李四也是张三的好友。此时使用sql语句查询时只用限定user_id作为条件即可查询出用户的好友列表:select * from tb_friend where user_id = 11互为好友关系因为是互为好友关系,则只需要插入一条数据即可。对应的查询语句为:select * from tb_friend where user_id = 1 or friend_id = 112当然也可以使用UNION ALL来实现:select friend_id as friends from tb_friend where user_id = 1UNION ALL --使用UNION ALL,因为不存在重复的select user_id as friends from tb_friend where friend_id = 1123注意事项:- user_id1–>friend_id2和user_id2–>friend_id1是相同的记录,不需要重复插入;- 为了快速判断两个人是不是好友,可在程序层插入数据前添加一个限制user_id1 < user_id2;- 可加入缓存层(Redis或Memcached)来提高性能;- 可从数据库层限制(user_id,friend_id)不可重复;加入分组如果好友数量比较多,关系比较复杂,可引入好友分组,可进行如下改造:ID user_id friend_id user_group friend_group1 1 2 好友 同学2 1 3 同学 同学在数据库中添加了user_group,当前user给friend设置的分组,friend_group是当前user的朋友对其设置的分组类别。于是,查询好友列表的SQL如下:select friend_id as friends ,user_group as my_group from tb_friends where user_id = 1UNION ALLselect user_id as friends , friend_group as my_group from friend_id = 1小结至此社交系统中好友关系的设计及SQL语句使用基本完成。可根据具体的业务情况进行修改。在查询除好友的id列表之后就可以进行好友信息的查询。此处需要注意的是如果用in语句来查询会有不走索引、sql语句大小限制、性能等问题,可考虑使用左连接进行查询。
亿级规模的 Feed 流系统,如何轻松设计?
阿里妹导读:互联网进入移动互联网时代,最具代表性的产品就是各种信息流,像是朋友圈、微博、头条等。这些移动化联网时代的新产品在过去几年间借着智能手机的风高速成长。这些产品都是Feed流类型产品,由于Feed流一般是按照时间“从上往下流动”,非常适合在移动设备端浏览,最终这一类应用就脱颖而出,迅速抢占了上一代产品的市场空间。简介
Feed流是Feed + 流,Feed的本意是饲料,Feed流的本意就是有人一直在往一个地方投递新鲜的饲料,如果需要饲料,只需要盯着投递点就可以了,这样就能源源不断获取到新鲜的饲料。 在信息学里面,Feed其实是一个信息单元,比如一条朋友圈状态、一条微博、一条咨询或一条短视频等,所以Feed流就是不停更新的信息单元,只要关注某些发布者就能获取到源源不断的新鲜信息,我们的用户也就可以在移动设备上逐条去浏览这些信息单元。
当前最流行的Feed流产品有微博、微信朋友圈、头条的资讯推荐、快手抖音的视频推荐等,还有一些变种,比如私信、通知等,这些系统都是Feed流系统,接下来我们会介绍如何设计一个Feed流系统架构。
Feed流系统特点
Feed流本质上是一个数据流,是将 “N个发布者的信息单元” 通过 “关注关系” 传送给 “M个接收者”。
Feed流系统是一个数据流系统,所以我们核心要看数据。从数据层面看,数据分为三类,分别是:
发布者的数据:发布者产生数据,然后数据需要按照发布者组织,需要根据发布者查到所有数据,比如微博的个人页面、朋友圈的个人相册等。
关注关系:系统中个体间的关系,微博中是关注,是单向流,朋友圈是好友,是双向流。不管是单向还是双向,当发布者发布一条信息时,该条信息的流动永远是单向的。
接收者的数据:从不同发布者那里获取到的数据,然后通过某种顺序(一般为时间)组织在一起,比如微博的首页、朋友圈首页等。这些数据具有时间热度属性,越新的数据越有价值,越新的数据就要排在最前面。
针对这三类数据,我们可以有如下定义:
存储库:存储发布者的数据,永久保存。
关注表:用户关系表,永久保存。
同步库:存储接收者的时间热度数据,只需要保留最近一段时间的数据即可。
设计Feed流系统时最核心的是确定清楚产品层面的定义,需要考虑的因素包括:
产品用户规模:用户规模在十万、千万、十亿级时,设计难度和侧重点会不同。
关注关系(单向、双写):如果是双向,那么就不会有大V,否则会有大V存在。
上述是选择数据存储系统最核心的几个考虑点,除此之外,还有一些需要考虑的:
如何实现Meta和Feed内容搜索?
虽然Feed流系统本身可以不需要搜索,但是一个Feed流产品必须要有搜索,否则信息发现难度会加大,用户留存率会大幅下降。
Feed流的顺序是时间还是其他分数,比如个人的喜好程度?
双向关系时由于关系很紧密,一定是按时间排序,就算一个关系很紧密的人发了一条空消息或者低价值消息,那我们也会需要关注了解的。
单向关系时,那么可能就会存在大V,大V的粉丝数量理论极限就是整个系统的用户数,有一些产品会让所有用户都默认关注产品负责人,这种产品中,该负责人就是最大的大V,粉丝数就是用户规模。
接下来,我们看看整个Feed流系统如何设计。
Feed流系统设计
1. 产品定义
第一步,我们首先需要定义产品,我们要做的产品是哪一种类型,常见的类型有:
微博类
朋友圈类
抖音类
私信类
接着,再详细看一下这几类产品的异同:
上述对比中,只对比各类产品最核心、或者最根本特点,其他次要的不考虑。比如微博中互相关注后就是双向关注了,但是这个不是微博的立命之本,只是补充,无法撼动根本。
从上面表格可以看出来,主要分为两种区分:
关注关系是单向还是双向:
如果是单向,那么可能就会存在大V效应,同时时效性可以低一些,比如到分钟级别;
如果是双向,那就是好友,好友的数量有限,那么就不会有大V,因为每个人的精力有限,他不可能主动加几千万的好友,这时候因为关系更精密,时效性要求会更高,需要都秒级别。
排序是时间还是推荐:
用户对feed流最容易接受的就是时间,目前大部分都是时间。
但是有一些场景,是从全网数据里面根据用户的喜好给用户推荐和用户喜好度最匹配的内容,这个时候就需要用推荐了,这种情况一般也会省略掉关注了,相对于关注了全网所有用户,比如抖音、头条等。
确定了产品类型后,还需要继续确定的是系统设计目标:需要支持的最大用户数是多少?十万、百万、千万还是亿?
用户数很少的时候,就比较简单,这里我们主要考虑 亿级用户 的情况,因为如果系统能支持亿级,那么其他量级也能支持。为了支持亿级规模的用户,主要子系统选型时需要考虑水平扩展能力以及一些子系统的可用性和可靠性了,因为系统大了后,任何一个子系统的不稳定都很容易波及整个系统。
2. 存储
我们先来看看最重要的存储,不管是哪种同步模式,在存储上都是一样的,我们定义用户消息的存储为存储库。存储库主要满足三个需求:
可靠存储用户发送的消息,不能丢失。否则就找不到自己曾经发布到朋友圈状态了。
读取某个人发布过的所有消息,比如个人主页等。
数据永久保存。
所以,存储库最重要的特征就是两点:
数据可靠、不丢失。
由于数据要永久保存,数据会一直增长,所以要易于水平扩展。
综上,可以选为存储库的系统大概有两类:
对于可靠性,分布式NoSQL的可靠性要高于关系型数据库,这个可能有违很多人的认知。主要是关系型数据库发展很长时间了,且很成熟了,数据放在上面大家放心,而分布式NoSQL数据库发展晚,使用的并不多,不太信任。但是,分布式NoSQL需要存储的数据量更多,对数据可靠性的要求也加严格,所以一般都是存储三份,可靠性会更高。目前在一些云厂商中的关系型数据库因为采用了和分布式NoSQL类似的方式,所以可靠性也得到了大幅提高。
水平扩展能力:对于分布式NoSQL数据库,数据天然是分布在多台机器上,当一台机器上的数据量增大后,可以通过自动分裂两部分,然后将其中一半的数据迁移到另一台机器上去,这样就做到了线性扩展。而关系型数据库需要在扩容时再次分库分表。所以,结论是:
如果是自建系统,且不具备分布式NoSQL数据库运维能力,且数据规模不大,那么可以使用MySQL,这样可以撑一段时间。
如果是基于云服务,那么就用分布式NoSQL,比如Tablestore或Bigtable。
如果数据规模很大,那么也要用分布式NoSQL,否则就是走上一条不归路。
如果使用Tablestore,那么存储库表设计结构如下:
到此,我们确定了存储库的选型,那么系统架构的轮廓有了:
3. 同步
系统规模和产品类型,以及存储系统确定后,我们可以确定同步方式,常见的方式有三种:
推模式(也叫写扩散):和名字一样,就是一种推的方式,发送者发送了一个消息后,立即将这个消息推送给接收者,但是接收者此时不一定在线,那么就需要有一个地方存储这个数据,这个存储的地方我们称为:同步库。推模式也叫写扩散的原因是,一个消息需要发送个多个粉丝,那么这条消息就会复制多份,写放大,所以也叫写扩散。这种模式下,对同步库的要求就是写入能力极强和稳定。读取的时候因为消息已经发到接收者的收件箱了,只需要读一次自己的收件箱即可,读请求的量极小,所以对读的QPS需求不大。归纳下,推模式中对同步库的要求只有一个:写入能力强。
拉模式(也叫读扩散):这种是一种拉的方式,发送者发送了一条消息后,这条消息不会立即推送给粉丝,而是写入自己的发件箱,当粉丝上线后再去自己关注者的发件箱里面去读取,一条消息的写入只有一次,但是读取最多会和粉丝数一样,读会放大,所以也叫读扩散。拉模式的读写比例刚好和写扩散相反,那么对系统的要求是:读取能力强。另外这里还有一个误区,很多人在最开始设计feed流系统时,首先想到的是拉模式,因为这种和用户的使用体感是一样的,但是在系统设计上这种方式有不少痛点,最大的是每个粉丝需要记录自己上次读到了关注者的哪条消息,如果有1000个关注者,那么这个人需要记录1000个位置信息,这个量和关注量成正比的,远比用户数要大的多,这里要特别注意,虽然在产品前期数据量少的时候这种方式可以应付,但是量大了后就会事倍功半,得不偿失,切记切记。
推拉结合模式:推模式在单向关系中,因为存在大V,那么一条消息可能会扩散几百万次,但是这些用户中可能有一半多是僵尸,永远不会上线,那么就存在资源浪费。而拉模式下,在系统架构上会很复杂,同时需要记录的位置信息是天量,不好解决,尤其是用户量多了后会成为第一个故障点。基于此,所以有了推拉结合模式,大部分用户的消息都是写扩散,只有大V是读扩散,这样既控制了资源浪费,又减少了系统设计复杂度。但是整体设计复杂度还是要比推模式复杂。
用图表对比:
介绍完同步模式中所有场景和模式后,我们归纳下:
如果产品中是双向关系,那么就采用推模式。
如果产品中是单向关系,且用户数少于1000万,那么也采用推模式,足够了。
如果产品是单向关系,单用户数大于1000万,那么采用推拉结合模式,这时候可以从推模式演进过来,不需要额外重新推翻重做。永远不要只用拉模式。
如果是一个初创企业,先用推模式,快速把系统设计出来,然后让产品去验证、迭代,等客户数大幅上涨到1000万后,再考虑升级为推拉集合模式。
如果是按推荐排序,那么是另外的考虑了,架构会完全不一样,这个后面专门文章介绍。
如果选择了Tablestore,那么同步库表设计结构如下:
确定了同步库的架构如下:
4. 元数据
前面介绍了同步和存储后,整个Feed流系统的基础功能完成了,但是对于一个完整Feed流产品而言,还缺元数据部分,接下来,我们看元数据如何处理:Feed流系统中的元数据主要包括:
用户详情和列表。
关注或好友关系。
推送session池。
我们接下来逐一来看。
4.1 用户详情和列表
主要是用户的详情,包括用户的各种自定义属性和系统附加的属性,这部分的要求只需要根据用户ID查询到就可以了。
可以采用的分布式NoSQL系统或者关系型数据库都可以。
如果使用NoSQL数据库Tablestore,那么用户详情表设计结构如下:
4.2 关注或好友关系
这部分是存储关系,查询的时候需要支持查询关注列表或者粉丝列表,或者直接好友列表,这里就需要根据多个属性列查询需要索引能力,这里,存储系统也可以采用两类,关系型、分布式NoSQL数据库。
如果已经有了关系型数据库了,且数据量较少,则选择关系型数据库,比如MySQL等。
如果数据量比较大,这个时候就有两种选择:
需要分布式事务,可以采用支持分布式事务的系统,比如分布式关系型数据库。
使用具有索引的系统,比如云上的Tablestore,更简单,吞吐更高,扩容能力也一并解决了。
如果使用Tablestore,那么关注关系表设计结构如下:
Table:user_relation_table
多元索引Schema:
查询的时候:
如果需要查询某个人的粉丝列表:使用TermQuery查询固定user_id,且按照timestamp排序。
如果需要查询某个人的关注列表:使用TermQuery查询固定follow_user_id,且按照timestamp排序。
当前数据写入Table后,需要5~10秒钟延迟后会在多元索引中查询到,未来会优化到2秒以内。
除了使用多元索引外,还可以使用GlobalIndex。
4.3 推送session池
思考一个问题,发送者将消息发送后,接收者如何知道自己有新消息来了?客户端周期性去刷新?如果是这样子,那么系统的读请求压力会随着客户端增长而增长,这时候就会有一个风险,比如平时的设备在线率是20%~30%,突然某天平台爆发了一个热点消息,大量休眠设备登陆,这个时候就会出现“查询风暴”,一下子就把系统打垮了,所有的用户都不能用了。
解决这个问题的一个思路是,在服务端维护一个推送session池,这个里面记录哪些用户在线,然后当用户A发送了一条消息给用户B后,服务端在写入存储库和同步库后,再通知一下session池中的用户B的session,告诉他:你有新消息了。然后session-B再去读消息,然后有消息后将消息推送给客户端。或者有消息后给客户端推送一下有消息了,客户端再去拉。
这个session池使用在同步中,但是本质还是一个元数据,一般只需要存在于内存中即可,但是考虑到failover情况,那就需要持久化,这部分数据由于只需要指定单Key查询,用分布式NoSQL或关系型数据库都可以,一般复用当前的系统即可。
如果使用Tablestore,那么session表设计结构如下:
5. 评论
除了私信类型外,其他的feed流类型中,都有评论功能,评论的属性和存储库差不多,但是多了一层关系:被评论的消息,所以只要将评论按照被被评论消息分组组织即可,然后查询时也是一个范围查询就行。这种查询方式很简单,用不到关系型数据库中复杂的事务、join等功能,很适合用分布式NoSQL数据库来存储。
所以,一般的选择方式就是:
如果系统中已经有了分布式NoSQL数据库,比如Tablestore、Bigtable等,那么直接用这些即可。
如果没有上述系统,那么如果有MySQL等关系型数据库,那就选关系型数据库即可。
如果选择了Tablestore,那么“评论表”设计结构如下:
如果需要搜索评论内容,那么对这张表建立多元索引即可。
6. 赞
最近几年,“赞”或“like”功能很流行,赞功能的实现和评论类似,只是比评论少了一个内容,所以选择方式和评论一样。
如果选择了Tablestore,那么“赞表”设计结构同评论表,这里就不再赘述了。系统架构中加了元数据系统后的架构如下:
搜索
到此,我们已经介绍完了Feed流系统的主题架构,Feed流系统算是完成了。但是Feed流产品上还未结束,对于所有的feed流产品都需要有搜索能力,比如下面场景:
微博中的搜索用户。
搜索微博内容。
微信中搜索好友等。
这些内容搜索只需要字符匹配到即可,不需要非常复杂的相关性算法,所以只需要有能支持分词的检索功能即可,所以一般有两种做法:
使用搜索引擎,将存储库的内容和用户信息表内容推送给搜索系统,搜索的时候直接访问搜索系统。
使用具备全文检索能力的数据库,比如最新版的MySQL、MongoDB或者Tablestore。
所以,选择的原则如下:
如果存储库使用了MySQL或者Tablestore,那么直接选择这两个系统就可以了。
如果整个系统都没使用MySQL、Tablestore,且已经使用了搜索系统,那么可以直接复用搜索系统,其他场景都不应该再额外加一个搜索系统进来,徒添复杂度。
如果使用Tablestore,那么只需要在相应表上建立多元索引即可:
如果需要对用户名支持搜索,那么需要对user_table建立多元索引,其中的nick_name需要是Text类型,且单字分词。
如果需要对Feed流内容支持搜索,那么需要对存储库表:store_table建立多元索引,这样就能直接对Feed流内容进行各种复杂查询了,包括多条件筛选、全文检索等。
系统架构中加了搜索功能后的架构如下:
8. 排序
目前的Feed流系统中的排序方式有两种,一种是时间,一种是分数。
我们常用的微博、朋友圈、私信这些都是时间线类型的,因为这些产品定义中,需要我们主动关注某些人后才会看到这些人发表的内容,这个时候,最重要的是实时性,而不是发布质量,就算关注人发布了一条垃圾信息,我们也会被动看到。这种类型的产品适用于按照时间线排序。这一篇我们介绍的架构都是基于时间类型的。
另外一种是不需要关注任何人,我们能看到的都是系统希望我们看到的,系统在后台会分析我们的每个人的爱好,然后给每个人推送差异化的、各自喜欢的内容,这一种的架构和基于时间的完全不一样,我们在后续的推荐类型中专门介绍。
9. 删除Feed内容
在Feed流应用中有一个问题,就是如果用户删除了之前发表的内容,系统该如何处理?因为系统里面有写扩散,那么删除的时候是不是也要写扩散一遍?这样的话,删除就不及时了,很难应对法律法规要求的快速删除。
针对这个问题,我们在之前设计的时候,同步表中只有消息ID,没有消息内容,在用户读取的时候需要到存储库中去读消息内容,那么我们可以直接删除存储库中的这一条消息,这样用户读取的时候使用消息ID是读不到数据的,也就相当于删除的内容,而且删除速度会很快。除了直接删除外,另外一种办法是逻辑删除,对于删除的feed内容,只做标记,当查询到带有标记的数据时就认为删除了。
10. 更新Feed内容
更新和删除Feed处理逻辑一样,如果使用了支持多版本的存储系统,比如Tablestore,那么也可以支持编辑版本,和现在的微博一样。
11. 总结
上面介绍了不同子功能的特点和系统要求,能满足需求的系统主要有两类,一类是阿里云的Tablestore单系统,一类是开源组件组成的组合系统。
开源组件组成的组合系统:包括MySQL、Redis、HBase等,这些系统单个都不能解决Feed流系统中遇到的问题,需要组合在一起,各司其职才能完成一个Feed流系统,适用于热衷开源系统,人多且喜欢运维操作的团队。
Tablestore单系统:只使用Tablestore单个系统就能解决上述的所有问题,这时候肯定有人要问?你是不是在吹牛? 这里不是吹牛,Tablestore在三年前就已经开始重视Feed流类型业务,之前也发表过多篇文章介绍,功能上也在专门为Feed流系统特别定制设计,所以到今天,只使用Tablestore一款产品,是可以满足上述需求的。选择Tablestore做Feed流系统的用户具有以下一些特征:
产品设计目标规模大,千万级或亿级。
不喜欢运维,喜欢专注于开发。
高效率团队,希望尽快将产品实现落地。
希望一劳永逸,未来系统随着用户规模增长可以自动扩容。
希望能按量付费,用户少的时候费用低,等用户增长起来后费用在跟随用户数增长。
如果具有上述四个特征的任何一个,那么都是适合于用Tablestore。
架构实践
上面我们介绍了Feed流系统的设计理论,具体到不同的类型中,会有不同的侧重点,下面会逐一介绍。
朋友圈
朋友圈是一种典型的Feed流系统,关系是双写关系,关系有上限,排序按照时间,如果有个人持续产生垃圾内容,那就只能屏蔽掉TA,这一种类型就是典型的写扩散模型。
微博
微博也是一种非常典型的Feed流系统,但不同于朋友圈,关系是单向的,那么也就会产生大V,这个时候就需要读写扩散模式,用读扩散解决大V问题。同时,微博也是主动关注类型的产品,所以排序也只能是时间,如果按照推荐排序,那么效果就会比较差。
头条
头条是最近几年快速崛起的一款应用,在原有微博的Feed流系统上产生了进化,用户不需要主动关注其他人,只要初始浏览一些内容后,系统就会自动判断出你的喜好,然后后面再根据你的喜好给你推荐你可能会喜好的内容,训练时间长了后,推送的内容都会是你最喜欢看的。
私信
私信也算是一种简单的Feed流系统,或者也可以认为是一种变相的IM,都是单对单的,没有群。
原文发布时间为:2019-08-26作者: 少强本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
COSCon'19 | 如何设计新一代的图数据库 Nebula
11 月 2 号 - 11 月 3 号,以“大爱无疆,开源无界”为主题的 2019 中国开源年会(COSCon'19)正式启动,大会以开源治理、国际接轨、社区发展和开源项目为切入点同全球开源爱好者们共同交流开源。
作为图数据库技术的代表,Nebula Graph 总监——吴敏在本次大会上将会讲述了大规模分布式图数据库设计思考和实践。在信息爆发式增长和内容平台遍地开花的信息时代,图数据库在当中扮演了什么样的角色?同传统数据库相比,图数据库又有什么优势?图数据库开发需要哪些新技术?就此,开源社特访吴敏来分享下图数据库主题内容,从图数据 Nebula 的研发开始,就传统数据库面临的挑战,开源模式的优势,Nebula 的社区开展和产品规划等问题进行深入解析。
About Nebula 总监--吴敏
开源社:Hi,吴敏,先和大家介绍下自己。
大家好,我是吴敏,VEsoft 总监,博士毕业于浙江大学。 曾就职于阿里云、蚂蚁金服,从事分布式图数据库以及云存储相关工作。
开源社:谈谈您在 COSCon'19 上的分享话题。
随着抖音、小红书等社交内容平台的爆红诞生了一种基于社交关系网路的推荐需求,而以垂直领域作为切入点的知识图谱过去两年的“爆火”,传统数据库在处理社交推荐、风控、知识图谱方面的性能缺陷,图数据库的研发应运而生。
本演讲开篇将陈述图数据库行业现状,让你对图数据库存储的数据及对场景有所了解,再从开源的分布式图数据库 Nebula Graph 切入深度讲解大规模分布式图数据库应该如何设计存储、计算及架构,最后讲述开源对图数据库开发的影响。
内容大纲
图数据库概述及应用
Nebula Graph 设计介绍
技术细节
开源社区及服务
开源社:哪些人可以应该了解这个内容?
对图数据库有兴趣,或是有推荐、风控、知识图谱等业务场景需求的人。
Nebula 研发之旅
开源社:为什么给图数据库取名 Nebula ?
Nebula 是星云的意思,很大嘛,也是漫威宇宙里面漂亮的星云小姐姐。对了,Nebula的发音是:[ˈnɛbjələ]
开源社:现在数据库领域百花齐放,国产的 OceanBase 和 TiDB 都发展得不错,为什么还要研发 Nebula 这样的图数据库?
OceanBase、TiDB 这类 NewSQL 最近发展势头很强劲,他们的出现更多的是对传统单机的关系型数据库在可用性的补充。Nebula 聚焦在图数据库这一领域,也是近年来在数据库各分支中增长最为快速的领域。图数据库使用图(或者网)的方式很直接、自然的表达现实世界的关系: 用节点来表示实体,边来表示关联关系,everything is connected。能高效的提供图检索,提供专业的分析算法、工具,比如 ShortestPath、PageRank、标签传播等等。
开源社:图数据库应用场景有哪些?
典型的应用场景有社交网络,金融风控,推荐引擎,知识图谱等。
社交网络,比如,推荐一条最短路径让我结识迪纳热巴,还可以加上筛选条件,路径中的每个人都是单身女性。
金融风控场景,比如,去查一个信用卡反套现的网络。很典型的一个场景,A 转账到 B,B 转账到 C,C 又转回给 A 即是一个典型的闭环。对于这样的闭环,这类查询在图数据库大规模应用之前,大部分都是采用离线计算的方式去查找,但是离线场景很难去控制当前发生的这笔交易。一个信用卡交易或者在线贷款,整个作业流程很长,而在反套现这块的审核时间又限制在毫秒级,这就是图数据库非常大的一个应用场景。
在推荐算法中,为某个人推荐他的好友。现在的方案是去找好友的好友,判断好友的好友有没有可能成为某人的新好友,这当中涉及好友关系的亲密度,抵达好友的好友的最短路径等。业务方可能会用 MySQL 等传统数据库或是 HBase 来存各类好友关系,然后通过多个串行的 Key-Value 来做查询,但这在线上场景是很难满足性能要求的。
知识图谱这些年非常火,知识图谱结合自然语言的形式在金融,医疗,互联网等众多领域被广泛使用,常见的有语音助手、聊天机器人、智能问答等应用场景。而图数据库存储的数据结构完全适配知识图谱数据,图谱中的实体对应图数据库的点,实体与实体的关系对应图数据库的边,拿 Nebula 为例,Nebula Graph Schema 采用属性图,点边上的属性对应图谱实体和关系中的属性,边的方向表示了关系的方向,边上的标记表示了关系的类型。
再说到最近国内非常火的区块链场景,由于区块链上的所有行为都是公开被记录的又是不可篡改的,因此所有的交易行为,不管是历史数据,还是大概每几分钟产生的新 block,都可以对 DAT 文件解析后导入到图数据库和 GNN 中做分析。例如我们都听说在一些数字货币场景下,洗钱、盗窃、团伙、操纵市场的各类事情很多,通过图的手段包括可以帮助我们挖掘里面的非法行为。
开源社:作为图数据库,有参考借鉴了哪些数据库吗?哪些方面是 Nebula 有特点的设计?
Nebula 是完全自主研发的数据库,它主要有以下的技术特点
存储计算分离
对于 Nebula Graph 来讲,有这么几个技术特点:第一个就是采用了存储计算分离的架构,主要好处就是为了上云或者说弹性,方便单独扩容。业务水位总是很难预测的,一段时间存储不够了,有些时候计算不够了。在云上或者使用容器技术,计算存储分离的架构运维起来会比较方便,成本也更好控制。大家使用 HBase 那么久,这方面的感触肯定很多。
查询语言 nGQL
Nebula Graph 的第二个技术特点是它的查询语言,我们称为 nGQL,比较接近 SQL。唯一大一点的语法差异就是 不用嵌套 (embedding)。大家都知道嵌套的 SQL,读起来是非常痛苦的,要从里向外读。另外,由于图这块目前并没有统一的国际标准,这对整个行业的发展并不是好事,用户的学习成本很高。目前有个 ISO / IEC 组织在准备图语言的国际标准,我们也在积极兼容标准。
支持多种后端存储
第三个特点就是 Nebula Graph 支持多种后端存储,除了原生的引擎外,也支持 HBase。因为很多用户,对 HBase 已经相当熟悉了,并不希望多一套存储架构。从架构上来说,Nebula Graph 是完全对等的分布式系统。
计算下推
和 HBase 的 CoProcessor 一样,Nebula Graph 支持数据计算下推。数据过滤,包括一些简单的聚合运算,能够在存储层就做掉,这样对于性能来讲能提升会非常大。
多租户
多租户,Nebula Graph是通过多 Space 来实现的。Space 是物理隔离。
索引
除了图查询外,还有很常见的一种场景是全局的属性查询。这个和 MySQL 一样,要提升性能的主要办法是为属性建立索引 ,这个也是 Nebula Graph 原生支持的功能。
图算法
最后的技术特点就是关于图算法方面。这里的算法和全图计算不太一样,更多是一个子图的计算,比如最短路径。大家知道数据库通常有 OLTP 和 OLAP 两种差异很大的场景,当然现在有很多 HTAP 方面的努力。那对于图数据库来说也是类似,我们在设计 Nebula Graph 的时候,做了一些权衡。我们认为全图的计算,比如 Page Rank,LPA,它的技术挑战和 OLTP 的挑战和对应的设计相差很大。所以 Nebula 的查询引擎主要针对 OLTP 类的场景。那么,对于 OLAP 类的计算需求,我们的考虑是通过支持和 Spark 的相互访问,来支持 Spark 上图计算,比如 graphX。这块工作正在开发中,应该在最近一两个月会发布。
开源社:为什么会考虑存储计算分离的架构呢?
存储计算分离是个很热的话题。我们将存储模块和 Query Engine 层分开主要有以下考虑。
成本的原因。存储和计算对计算机资源要求不一样,存储依赖 I/O,计算对 CPU 和内存的要求更高,业务在不同的应用或者发展时期,需要不同的存储空间和计算能力配比,存储和计算的耦合会使得机器的选型会比较复杂,存储计算分离的架构,使得 storage 的 scale out/in 更容易。
存储层抽象出来可以给计算带来新的选择,比如对接 Pregel, Spark GraphX 这些计算引擎。通常来说,图计算对于存储的要求是吞吐量优先的,而在线查询是时延优先的。通过把存储层分离出来,不管是开发的时候(做 QoS )还是运维的时候(单独集群部署),都会更容易一些。
在云计算场景下,能实现真正的弹性计算。
开源社:作为一个分布式数据库,是如何保障数据一致性的?
我们使用 Raft 协议,Raft 一致性协议使得 share-nothing 的 kv 有一致性保障。为什么选择 Raft?相对于 Paxos,Raft 更加有利于工程化实现。Nebula 存储层 Raft 使用 Multi-Raft 的模型,多个 replica 上的同一个 partition 组成一个 Raft 组,同一个集群内存在互相独立 Raft 组,在一致性保障的同时,提高了系统的并发能力。
开源社:在数据库的优化方面,Nebula 做了哪些?
Nebula 在数据优化方面主要做了以下工作:
异步和并发执行:由于 IO 和网络均为长时延操作,Nebula Graph 采用异步及并发操作。此外,为避免一些大query 的长尾影响,为每个 query 设置单独的资源池以保证服务质量 QoS。
计算下沉:为避免存储层将过多数据回传到计算层,占用宝贵带宽,条件过滤等算子会随查询条件一同下发到存储层节点。
数据库系统的优化与数据的物理存储方式以及数据的分布息息相关。而且随着业务的发展,数据分布是会发生变化的,一开始设计的索引和数据存储或者分区会慢慢变得不是最优的,这就需要系统能够做一些动态的调整。我们 storage 支持 scale out/in, load balance。系统的调整会带来 overhead,这是需要权衡考虑的问题。
开源社:现在市面上已有一些图数据库,Nebula 考虑兼容部分数据库让已有的用户无缝切到 Nebula 吗?
Nebula 有 CSV、HDFS 批量 数据导入工具。用户可以将数仓的数据导入到 Nebula。也提供 C++,Java,Golang,Python 的客户端。另外对于市面上已有的一些产品,现在也正在开发将它的数据格式直接解析为 Nebula 的数据格式,这样就可以非常方便的迁移,包括查询语言层面的兼容。
开源社:水平伸缩能够支持多大的规模?
存储层 share-nothing 的架构,理论上支持无限加机器。
开源社:Nebula 最新的版本 RC1 支持最短路径和全路径算法,可以具体讲下这块的实现,及以后的研发规划吗?
目前实现较为简单,基于双向搜索,返回点边组合的路径。未来规划是计划在执行计划与优化器都完成后,完善对路径的支持,包括实现 match,支持双向 bfs、双向 dijkstra、allpair(全路径),kshortest 等。当然我们欢迎社区的同学们都参与完善 Nebula 的路径算法。
开源社:使用 Nebula 之前,用户应该做哪些准备工作?
对于刚开始使用图数据库的用户,我们提供了详细的文档;
对于已经在使用其他图数据库,想要试试 Nebula 的用户,我们提供了数据导入等工具,有疑问或者任何问题,欢迎在 GitHub 上给我们提 issue,我们的工程师会在第一时间为您解答。
Nebula 和开源
开源社:作为一个企业级产品,为什么 Nebula 一开始就选择了走开源路线?
如果没 Linux,现在互联网的格局也不会是今天这样。我们想要建立图数据库的社区,做出更好的图数据库产品,也希望更多对 Nebula,对图数据库感兴趣的同学成为社区的贡献者,一起努力,共同建立一个互助互利的社区。
开源社:在开源的过程中,有遇到什么困难吗?
很多人都想为开源做一份力,但会被开源项目的门槛“劝退”,尤其是 Nebula 是一个即使耕耘在数据库领域多年的数据库专家,如果对图数据库的不够了解的话,都会感叹“高大上”的一个项目。但技术是为业务服务的,所以 Nebula 力求自己的文档让你即使你对图数据库一无所知,通过 Nebula 的文档也能够了解到图数据库及其应用场景。
开源社:在开源社区搭建这块,有什么可以和开源社小伙伴们分享的吗?
开源项目最重要的是生态的搭建,Nebula Graph 刚开源半年在社区搭建这块只能说略有心得,仅供大家参考 :) 开源社区运营主要从下面几个方面展开
简洁明了的文档:一个好的文档能让使用者快速同产品拉近距离,Nebula 的文档从“让非技术人做技术事”的出发,力求即使你是一个不懂技术的人也可以按照文档部署 Nebula,玩起来——用 Nebula 完成简单的 CRUD,如果开源社的小伙伴阅读过 Nebula 文档觉得哪里有更改意见,欢迎联系我们;
实时的反馈回复:用户的反馈,我们会第一时间进行回复,在 GitHub 的 issue 及用户交流群里进行回复;
同用户直接对话:在线上,Nebula 在各大技术平台同图数据库和 Nebula 爱好者们进行交流,包括 Nebula 架构设计、用户使用实操等系列文章;在线下,我们也开展了主题 Meetup 同各地爱好者交流图数据库技术及 Nebula 的开发心得;
社区用户体系:在 Nebula 的 GitHub 上,现阶段你可以看到 3 种用户,User、Contributor、Committer,User 通过向 Nebula 提 issue / pr 或者投稿等方式成为 Contributor,Contributor 再进阶成为 Committer。配合 Nebula 开展的各类社区活动,eg:捉虫活动,帮助社区用户完成角色“升级”;
最后,打个小广告:欢迎大家来参与到 Nebula 的建设中,为开源贡献一份力 :)
程序员寄语
开源社: 作为资深数据库从业人员,怎样让自己的眼界更加开阔,怎么获取这个领域的最前沿信息?
多看看论文,看看开源分布式系统的设计以及源代码;多关注数据库的的会议,比如,SIGMOD, VLDB,关注学术界的最新成果;多关注业界相关公司的发展和动态,比如 OsceanBase,TiDB。
Nebula 有话说
以上为开源社对图数据库 Nebula 总监——吴敏的采访,欢迎你关注 Nebula GitHub:github.com/vesoft-inc/nebula 了解 Nebula 最新动态或添加 Nebula 小助手为好友进图数据库技术交流群交流,小助手微信号:NebulaGraphbot
推荐阅读
图数据库 Nebula Graph 的数据模型和系统架构设计
Nebula Graph 在 HBaseCon Asia2019 的分享实录
Vol.03 nMeetup | 图数据库综述与 Nebula 在图数据库设计的实践
朋友圈不知你看到的那么简单,千万Feed流系统的存储技术解密
摘要:阿里巴巴高级技术专家木洛在2018云栖大会·深圳峰会中就Feed流的概念介绍、概念架构以及TableStore场景的Timeline模型等方面的内容做了深入的分析。本文带领大家一起了解并学习如何通过存储系统的性能特性,通过Feed流中的消息存储和推送机制支撑起千万Feed流的并发。
数十款阿里云产品限时折扣中,赶紧点击这里,领劵开始云上实践吧!
以下内容根据演讲嘉宾PPT以及视频整理而成(云栖社区做了不修改原意的编辑)。
本次的分享主要围绕以下三个方面:
一、概念介绍
二、Feed流系统架构
三、TableStore Timeline
一、概念介绍
下图为比较抽象的Feed流系统,系统被分为Feed,Feed流以及Feed订阅三个层次。Feed可以看作消息体,消息体就像我们发送的信息,平时发布的动态,新闻App中的一条新闻,或者推荐的内容,我们将这些统称为Feed。Feed产生之后,通过Feed流被推送到Feed订阅端,这个过程就是典型的Feed三层系统。Feed是一种实时消息,由于消息是实时产生,实时消费,实时推送的,因此满足实时性是关键。另外消息来自于很多不同的消息源,消息的产生属于海量级别。Feed流是实时推送的有序的可扩散的消息流,Feed产生之后,我们通常会将这些Feed推送给Feed的订阅端,推送的过程中需要满足有序,可扩散的原则。消息顺序有时间线类型,即按照消息产生的时间对消息进行排序;也有Rank类型,这种类型主要出现在推荐信息中,根据某些算法或消息所处的类别找到最相关的订阅端。一个消息可能是由单个消息体产生的,而这个消息体可以扩散到很多的订阅端,从消息产生到消息消费产生巨大的读写比。
下图展示了常见的Feed应用,朋友圈中的Feed就是每个人发布的动态,动态通过朋友圈的Feed流扩散到好友,同时每个人也可以在朋友圈中订阅到好友发布的动态。微博也是较为常见的Feed应用。Pinterest是国外关于图片的社交系统,与其它Feed系统不同的是,Pinterest的Feed就是图片。雪球的动态广场是一个讨论组,大家可以在讨论组里发布消息,讨论组的人也可以订阅组内消息。
二、Feed流系统架构
下面为大家举例介绍主要Feed流整体架构的实现。在讲架构实现的时候,本文会先介绍一个简单且为大家所熟知的案例。如下图所示,朋友圈是大家常用的社交平台,首先定义某个功能实现需要满足什么需求。第一点是朋友圈中的动态:在消息发送方面,消息会存到个人相册,每个人能在个人相册中看到自己发布的所有动态,并且这些动态是永久保存的,我们可以翻到N多年前自己发布的消息,所以这些消息有一个很长时间的保存状态;另外消息会扩散到好友的朋友圈,消息保存在持有好友关系的朋友圈;同时我们的朋友圈也保存着好友发的动态,作为消息保存。朋友圈中不仅仅只有发布的动态,也可能有一些广告,这些广告是由广告系统推荐过来的。以上是朋友圈简单的Feed流功能需求分析。总结功能需求如下图中所示:
人与人之间组建好友关系,朋友圈用于查看好友圈内的人发送的所有消息,按照动态发布的时间进行时间排序;广告系统可向朋友圈内插入广告消息,这是一个rank的消息;个人相册用于查看个人发送的所有消息。并且,个人相册和朋友圈是有所区别的,个人相册中需要永久保存个人发布的动态,但是朋友圈可以只看最近一段时间的消息,这点在我们之后设计消息系统时,会有所区别。
针对上文所说的朋友圈场景,下文为大家介绍一个简单的Feed流系统架构大概会分为哪几个模块。如下图所示,首先会设计一个端,用来发送消息或者接收消息,这就是消息的入口。其次,会有接入层,我们通常把它叫做消息服务器,这些消息服务器通常是由一组无状态的服务器组成的,主要职能是接收消息,处理消息以及当端读消息时的同步消息。接入层算是比较简单的实现,通常都是一些API网关,提供特定消息处理的API,并且涉及到消息协议中的特定实现,包括数据加密,优化的通信等。最核心的是后台服务,如下图所示,把后台服务简单化为两块,第一块是消息系统,消息系统是整个Feed流架构中最核心的部分,负责对消息进行持久化,个人的消息会先持久化都自己的库中;其次负责对消息进行同步,将消息同步给自己的好友或者抽取某些好友订阅自己的消息。广告系统会找到和广告最关联的人群,向这些人推送消息。消息系统下面最核心的是两个库的设计,一个是消息存储库,另外一个是消息同步库。这两个库的区别在于,消息存储库往往是用于永久存储所有人发布的所有消息;消息同步用来将一条消息向该同步的人同步。因为广告系统需要将广告推动给关联的人群,所以广告系统会和消息同步库打交道,同时,由于广告是不需要永久存储的,所以不需要和消息存储打交道。下图中最核心的是消息系统的设计,它的设计与实现和Feed流中的消息发送,订阅以及特征非常相关。
接下来我们看一下,当我们设计一个消息系统时,一定要足够了解系统的场景,了解在这个场景里面,这个消息系统需要满足什么要求。
如下图所示,将要求归为三:
类,第一类我们需要了解Feed流系统中消息的特征是什么:第一个特征是典型的读多写少,读多写少的含义就是我们在朋友圈中通常是读动态,很少人去发消息,读写比大概为100:1;第二个消息模型比较简单,就是消息体,或者消息,消息里面可能会有发送人,接收人,消息时间等属性。在消息确定时模型中的数据已经确定了,而且消息与消息之间是没有关联的;第三个是弱关系型数据,就是消息不会和其他数据产生关联,只是流式产生的一条条消息;第四个是波峰波谷式访问,指消息系统承载的流量呈现出波峰波谷式访问的特征,因为Feed流系统通常属于社交类系统,社交系统的活跃度取决于人群的活跃度,人群的活跃度会有很明显的波峰波谷。上述四种特征对于我们以后设计消息系统时选择消息存储的数据库是至关重要的,也具有参考意义。
第二类是关于消息存储,个人相册是需要永久存储的,存储量随着时间的推移会越来越大。因为消息的读写是在线的,不可能用一个本地存储,所以消息存储要求拥有在线永久存储的能力。同时,朋友圈中的动态需要保证高可靠性,不能轻易丢失。当应用达到很大的用户量时存储的消息容量会很巨大,处在PB级,属于万亿行的规模。
第三类关于消息同步,消息的产生与推送都对消息库产生很大的压力,需要能够应对海量消息;需要实现实时同步,多端同步;同步模型包括写扩散和读扩散。下面举一个例子理解一下写扩散和读扩散。一个人有100个好友,他在朋友圈中发布了一条动态,动态是如何同步到100个好友的朋友圈的呢?解决方案有两种,第一,每个人都有一个收件箱,向每个人的收件箱中投递一个消息。这种方案的好处在于,因为每个人都有一个收件箱,所有我们只需要查看收件箱,就可以看到别人给我传递的消息,对读有很大的优化,相对而言缺点是消息的写操作是需要扩散的,扩散的比例就是好友的个数,以上是写扩散的优劣。第二,采用读扩散,读扩散的好处在于数据只需要写一份,比如说将消息存储在我们自己的库中就行了,坏处是当我们需要拉取新的消息时,假设要获取100个好友的新信息,我们就需要从100个人那边拉取新消息,这对系统产生较大的读压力。以上是写扩散和读扩散的主要区别。
实际上,Feed流系统读写比例是100:1,读操作是非常多的,在这种数据特征下,如果我们继续采用读扩散,将会把读写比拉的更大,有可能将100:1拉到1000:1,这会导致在设计消息系统容量时,取读或者写的最高上限,上限拉高之后,对系统要求的能力也会变高,对系统的成本要求也越高。这时,如果采用写扩散,将把写的比例拉高,使读写比取得均衡,对系统的预留资源要,所以在Feed流系统中通常采用写扩散这个模式。但是,写扩散有一个缺点,写会扩散很多份,当朋友圈中有1万个好友,写扩散缺点会很明显,因此可以采用混合模式进行优化,例如大部分消息采用写扩散,对于扩散比例很高的消息可以采用读扩散。有一个优化方式,
在了解了消息的特征,存储和同步后,我们接下来就要选择合适的数据库,下图展示了NoSQL的解决方案。NoSQL的解决方案关注容量,服务能力,分布式支持,售卖模式以及数据模型。在容量方面,传统关系型数据库的容量在TB级别,而NoSQL采用分布式,所以容量在10PB级别。服务能力方面,十万TPS对应于千万TPS。分布式支持方面,对于这样海量数据的系统来说,必须采用分布式,在图中,分库分表对原生支持。售卖模式方面,按规格计费对按量计费,由于Feed流系统是一个具有波峰波谷式特征的系统,按量模式的售卖模式能帮助节约很多成本。数据模型方面,关系型对弱关系型,弱关系型在Feed流系统下也是满足需求的。
三、TableStore Timeline
TableStore为了简化Feed流系统的开发,之后就推出了TableStore Timeline。下图是Timeline简单逻辑模型。如下图所示,消息会有一个发送端(A),消息会有订阅端(B1,B2,B3),消息同步通过队列,是一个虚拟的队列,消息需要同步给谁,就将消息发到队列中。队列提供的功能有以下几个:
每个消息都有一个顺序ID,用来完成消息的同步,由于每个接收端同步的速度是不同的,每个接收端会保留自己的同步位点,在进行消息同步时,只需要通过同步位点,到消息列表中随机定位,定位之后拉取所有需要同步的消息,这是一个简单的模型。对于每个人的收件箱以及发件箱,都是一个独立的Timeline,在底层数据库中存储的内容就变成了每个人的收件箱以及发件箱,如果有1000万的用户,数据库中就需要存储1000万的timeline,在这种情况下,数据库就要达到千亿万亿的规模。
下面我们一起看下,基于Timeline的消息系统的架构实现,如下图所示。图中有几条关键路径,首先是消息的写操作,消息是如何写的,写完如何做同步。;第二个是消息的读。对于消息的写,首先端上发送一个消息到消息服务器;通常消息服务器为了异步化批量处理消息,都会将消息写进消息队列;消息队列中的消息会进行专门的消息处理;每个消息上传到服务器之后,将消息存储到个人相册中,个人相册存储在消息存储库中,其中存储着每个人的timeline,每个人读自己对应的timeline就可以读取自己的动态;在存储timeline之后,需要将消息同步到自己所有的好友,采取写扩散的消息同步模型;最后,选定所有的好友,将自己的消息发送到所有好友的收件箱,因此消息同步库中存储的是所有好友的收件箱。同步消息通常会看两个地方,一个是个人相册,从消息存储库中根据同步位点拉取内容。同时,我们只需要从消息同步库中拉取自己的收件箱,就可以看到还有发送的所有消息。消息存储库与消息同步库有不同的要求,消息存储库要求PB级数据低成本永久存储,消息同步库需要提供高并发及低延迟读,另外,需要存储每个用户的收件箱数据,数据生命周期为半年。TableStore提供的功能为容量型提供低成本存储,高性能提供低延迟读,以及PB级存储,毫秒级延迟。
下图展示了Timeline性能的实验分析结果,可以看到在同步100万扩散写时,比如说我将消息发给100万个人,在8核机器上,只需要3.7s的时间。在16核机器上,并发读取数据能达到4万QPS。
本文由云栖志愿小组沈金凤整理,编辑百见
Feed流系统设计-总纲
简介
差不多十年前,随着功能机的淘汰和智能机的普及,互联网开始进入移动互联网时代,最具代表性的产品就是微博、微信,以及后来的今日头条、快手等。这些移动化联网时代的新产品在过去几年间借着智能手机的风高速成长。
这些产品都是Feed流类型产品,由于Feed流一般是按照时间“从上往下流动”,非常适合在移动设备端浏览,最终这一类应用就脱颖而出,迅速抢占了上一代产品的市场空间。
Feed流是Feed + 流,Feed的本意是饲料,Feed流的本意就是有人一直在往一个地方投递新鲜的饲料,如果需要饲料,只需要盯着投递点就可以了,这样就能源源不断获取到新鲜的饲料。 在信息学里面,Feed其实是一个信息单元,比如一条朋友圈状态、一条微博、一条咨询或一条短视频等,所以Feed流就是不停更新的信息单元,只要关注某些发布者就能获取到源源不断的新鲜信息,我们的用户也就可以在移动设备上逐条去浏览这些信息单元。
当前最流行的Feed流产品有微博、微信朋友圈、头条的资讯推荐、快手抖音的视频推荐等,还有一些变种,比如私信、通知等,这些系统都是Feed流系统,接下来我们会介绍如何设计一个Feed流系统架构。
Feed流系统特点
Feed流本质上是一个数据流,是将 “N个发布者的信息单元” 通过 “关注关系” 传送给 “M个接收者”。
Feed流系统是一个数据流系统,所以我们核心要看数据。从数据层面看,数据分为三类,分别是:
发布者的数据:发布者产生数据,然后数据需要按照发布者组织,需要根据发布者查到所有数据,比如微博的个人页面、朋友圈的个人相册等。
关注关系:系统中个体间的关系,微博中是关注,是单向流,朋友圈是好友,是双向流。不管是单向还是双向,当发布者发布一条信息时,该条信息的流动永远是单向的。
接收者的数据:从不同发布者那里获取到的数据,然后通过某种顺序(一般为时间)组织在一起,比如微博的首页、朋友圈首页等。这些数据具有时间热度属性,越新的数据越有价值,越新的数据就要排在最前面。
针对这三类数据,我们可以有如下定义:
存储库:存储发布者的数据,永久保存。
关注表:用户关系表,永久保存。
同步库:存储接收者的时间热度数据,只需要保留最近一段时间的数据即可。
设计Feed流系统时最核心的是确定清楚产品层面的定义,需要考虑的因素包括:
产品用户规模:用户规模在十万、千万、十亿级时,设计难度和侧重点会不同。
关注关系(单向、双写):如果是双向,那么就不会有大V,否则会有大V存在。上述是选择数据存储系统最核心的几个考虑点,除此之外,还有一些需要考虑的:
如何实现Meta和Feed内容搜索?
虽然Feed流系统本身可以不需要搜索,但是一个Feed流产品必须要有搜索,否则信息发现难度会加大,用户留存率会大幅下降。
Feed流的顺序是时间还是其他分数,比如个人的喜好程度?
双向关系时由于关系很紧密,一定是按时间排序,就算一个关系很紧密的人发了一条空消息或者低价值消息,那我们也会需要关注了解的。
单向关系时,那么可能就会存在大V,大V的粉丝数量理论极限就是整个系统的用户数,有一些产品会让所有用户都默认关注产品负责人,这种产品中,该负责人就是最大的大V,粉丝数就是用户规模。接下来,我们看看整个Feed流系统如何设计。
Feed流系统设计
上一节,我们提前思考了Feed流系统的几个关键点,接下来,在这一节,我们自顶向下来设计一个Feed流系统。
1. 产品定义
第一步,我们首先需要定义产品,我们要做的产品是哪一种类型,常见的类型有:
微博类
朋友圈类
抖音类
私信类
接着,再详细看一下这几类产品的异同:
类型
关注关系
是否有大V
时效性
排序
微博类
单向
有
秒~分
时间
抖音类
单向/无
有
秒~分
推荐
朋友圈类
双向
无
秒
时间
私信类
双向
无
秒
时间
上述对比中,只对比各类产品最核心、或者最根本特点,其他次要的不考虑。比如微博中互相关注后就是双向关注了,但是这个不是微博的立命之本,只是补充,无法撼动根本。
从上面表格可以看出来,主要分为两种区分:
关注关系是单向还是双向:
如果是单向,那么可能就会存在大V效应,同时时效性可以低一些,比如到分钟级别;
如果是双向,那就是好友,好友的数量有限,那么就不会有大V,因为每个人的精力有限,他不可能主动加几千万的好友,这时候因为关系更精密,时效性要求会更高,需要都秒级别。
排序是时间还是推荐:
用户对feed流最容易接受的就是时间,目前大部分都是时间。
但是有一些场景,是从全网数据里面根据用户的喜好给用户推荐和用户喜好度最匹配的内容,这个时候就需要用推荐了,这种情况一般也会省略掉关注了,相对于关注了全网所有用户,比如抖音、头条等。确定了产品类型后,还需要继续确定的是系统设计目标:需要支持的最大用户数是多少?十万、百万、千万还是亿?
用户数很少的时候,就比较简单,这里我们主要考虑 亿级用户 的情况,因为如果系统能支持亿级,那么其他量级也能支持。为了支持亿级规模的用户,主要子系统选型时需要考虑水平扩展能力以及一些子系统的可用性和可靠性了,因为系统大了后,任何一个子系统的不稳定都很容易波及整个系统。
2. 存储
我们先来看看最重要的存储,不管是哪种同步模式,在存储上都是一样的,我们定义用户消息的存储为存储库。存储库主要满足三个需求:
可靠存储用户发送的消息,不能丢失。否则就找不到自己曾经发布到朋友圈状态了。
读取某个人发布过的所有消息,比如个人主页等。
数据永久保存。
所以,存储库最重要的特征就是两点:
数据可靠、不丢失。
由于数据要永久保存,数据会一直增长,所以要易于水平扩展。
综上,可以选为存储库的系统大概有两类:
特点
分布式NoSQL
关系型数据库(分库分表)
可靠性
极高
高
水平扩展能力
线性
需要改造
水平扩展速度
毫秒
无
常见系统
Tablestore、Bigtable
MySQL、PostgreSQL
对于可靠性,分布式NoSQL的可靠性要高于关系型数据库,这个可能有违很多人的认知。主要是关系型数据库发展很长时间了,且很成熟了,数据放在上面大家放心,而分布式NoSQL数据库发展晚,使用的并不多,不太信任。但是,分布式NoSQL需要存储的数据量更多,对数据可靠性的要求也加严格,所以一般都是存储三份,可靠性会更高。目前在一些云厂商中的关系型数据库因为采用了和分布式NoSQL类似的方式,所以可靠性也得到了大幅提高。
水平扩展能力:对于分布式NoSQL数据库,数据天然是分布在多台机器上,当一台机器上的数据量增大后,可以通过自动分裂两部分,然后将其中一半的数据迁移到另一台机器上去,这样就做到了线性扩展。而关系型数据库需要在扩容时再次分库分表。
所以,结论是:
如果是自建系统,且不具备分布式NoSQL数据库运维能力,且数据规模不大,那么可以使用MySQL,这样可以撑一段时间。
如果是基于云服务,那么就用分布式NoSQL,比如Tablestore或Bigtable。
如果数据规模很大,那么也要用分布式NoSQL,否则就是走上一条不归路。
如果使用Tablestore,那么存储库表设计结构如下:
主键列
第一列主键
第二列主键
属性列
属性列
列名
user_id
message_id
content
other
解释
消息发送者用户ID
消息顺序ID,可以使用timestamp。
内容
其他内容
到此,我们确定了存储库的选型,那么系统架构的轮廓有了:
3. 同步
系统规模和产品类型,以及存储系统确定后,我们可以确定同步方式,常见的方式有三种:
推模式(也叫写扩散):和名字一样,就是一种推的方式,发送者发送了一个消息后,立即将这个消息推送给接收者,但是接收者此时不一定在线,那么就需要有一个地方存储这个数据,这个存储的地方我们称为:同步库。推模式也叫写扩散的原因是,一个消息需要发送个多个粉丝,那么这条消息就会复制多份,写放大,所以也叫写扩散。这种模式下,对同步库的要求就是写入能力极强和稳定。读取的时候因为消息已经发到接收者的收件箱了,只需要读一次自己的收件箱即可,读请求的量极小,所以对读的QPS需求不大。归纳下,推模式中对同步库的要求只有一个:写入能力强。
拉模式(也叫读扩散):这种是一种拉的方式,发送者发送了一条消息后,这条消息不会立即推送给粉丝,而是写入自己的发件箱,当粉丝上线后再去自己关注者的发件箱里面去读取,一条消息的写入只有一次,但是读取最多会和粉丝数一样,读会放大,所以也叫读扩散。拉模式的读写比例刚好和写扩散相反,那么对系统的要求是:读取能力强。另外这里还有一个误区,很多人在最开始设计feed流系统时,首先想到的是拉模式,因为这种和用户的使用体感是一样的,但是在系统设计上这种方式有不少痛点,最大的是每个粉丝需要记录自己上次读到了关注者的哪条消息,如果有1000个关注者,那么这个人需要记录1000个位置信息,这个量和关注量成正比的,远比用户数要大的多,这里要特别注意,虽然在产品前期数据量少的时候这种方式可以应付,但是量大了后就会事倍功半,得不偿失,切记切记。
推拉结合模式:推模式在单向关系中,因为存在大V,那么一条消息可能会扩散几百万次,但是这些用户中可能有一半多是僵尸,永远不会上线,那么就存在资源浪费。而拉模式下,在系统架构上会很复杂,同时需要记录的位置信息是天量,不好解决,尤其是用户量多了后会成为第一个故障点。基于此,所以有了推拉结合模式,大部分用户的消息都是写扩散,只有大V是读扩散,这样既控制了资源浪费,又减少了系统设计复杂度。但是整体设计复杂度还是要比推模式复杂。
用图表对比:
类型
推模式
拉模式
推拉结合模式
写放大
高
无
中
读放大
无
高
中
用户读取延时
毫秒
秒
秒
读写比例
1:99
99:1
~50:50
系统要求
写能力强
读能力强
读写都适中
常见系统
Tablestore、Bigtable等LSM架构的分布式NoSQL
Redis、memcache等缓存系统或搜索系统(推荐排序场景)
两者结合
架构复杂度
简单
复杂
更复杂
介绍完同步模式中所有场景和模式后,我们归纳下:
如果产品中是双向关系,那么就采用推模式。
如果产品中是单向关系,且用户数少于1000万,那么也采用推模式,足够了。
如果产品是单向关系,单用户数大于1000万,那么采用推拉结合模式,这时候可以从推模式演进过来,不需要额外重新推翻重做。
永远不要只用拉模式。
如果是一个初创企业,先用推模式,快速把系统设计出来,然后让产品去验证、迭代,等客户数大幅上涨到1000万后,再考虑升级为推拉集合模式。
如果是按推荐排序,那么是另外的考虑了,架构会完全不一样,这个后面专门文章介绍。
如果选择了Tablestore,那么同步库表设计结构如下:
主键列
第一列主键
第二列主键
属性列
属性列
属性列
列名
user_id
sequence_id
sender_id
message_id
other
解释
消息接收者用户ID
消息顺序ID,可以使用timestamp + send_user_id,也可以直接使用Tablestore的自增列。
发送者的用户ID
store_table中的message_id列的值,也就是消息ID。通过sender_id和message_id可以到store_table中查询到消息内容
其他内容,同步库中不需要包括消息内容。
确定了同步库的架构如下:
4. 元数据
前面介绍了同步和存储后,整个Feed流系统的基础功能完成了,但是对于一个完整Feed流产品而言,还缺元数据部分,接下来,我们看元数据如何处理:
Feed流系统中的元数据主要包括:
用户详情和列表。
关注或好友关系。
推送session池。
我们接下来逐一来看。
4.1 用户详情和列表
主要是用户的详情,包括用户的各种自定义属性和系统附加的属性,这部分的要求只需要根据用户ID查询到就可以了。
可以采用的分布式NoSQL系统或者关系型数据库都可以。
如果使用NoSQL数据库Tablestore,那么用户详情表设计结构如下:
主键顺序
第一列主键
属性列-1
属性列-2
......
字段名
user_id
nick_name
gender
other
备注
主键列,用于唯一确定一个用户
用户昵称,用户自定义属性
用户性别,用户自定义属性
其他属性,包括用户自定义属性列和系统附加属性列。Tablestore是FreeSchema类型的,可以随时在任何一行增加新列而不影响原有数据。
4.2 关注或好友关系
这部分是存储关系,查询的时候需要支持查询关注列表或者粉丝列表,或者直接好友列表,这里就需要根据多个属性列查询需要索引能力,这里,存储系统也可以采用两类,关系型、分布式NoSQL数据库。
如果已经有了关系型数据库了,且数据量较少,则选择关系型数据库,比如MySQL等。
如果数据量比较大,这个时候就有两种选择:
需要分布式事务,可以采用支持分布式事务的系统,比如分布式关系型数据库。
使用具有索引的系统,比如云上的Tablestore,更简单,吞吐更高,扩容能力也一并解决了。
如果使用Tablestore,那么关注关系表设计结构如下:
Table:user_relation_table
主键顺序
第一列主键
第一列主键
属性列
属性列
Table字段名
user_id
follow_user_id
timestamp
other
备注
用户ID
粉丝用户ID
关注时间
其他属性列
多元索引的索引结构:
Table字段名
user_id
follow_user_id
timestamp
是否Index
是
是
是
是否enableSortAndAgg
是
是
是
是否store
是
是
是
查询的时候:
如果需要查询某个人的粉丝列表:使用TermQuery查询固定user_id,且按照timestamp排序。
如果需要查询某个人的关注列表:使用TermQuery查询固定follow_user_id,且按照timestamp排序。
当前数据写入Table后,需要5~10秒钟延迟后会在多元索引中查询到,未来会优化到2秒以内。
除了使用多元索引外,还可以使用GlobalIndex。
4.3 推送session池
思考一个问题,发送者将消息发送后,接收者如何知道自己有新消息来了?客户端周期性去刷新?如果是这样子,那么系统的读请求压力会随着客户端增长而增长,这时候就会有一个风险,比如平时的设备在线率是20%~30%,突然某天平台爆发了一个热点消息,大量休眠设备登陆,这个时候就会出现“查询风暴”,一下子就把系统打垮了,所有的用户都不能用了。
解决这个问题的一个思路是,在服务端维护一个推送session池,这个里面记录哪些用户在线,然后当用户A发送了一条消息给用户B后,服务端在写入存储库和同步库后,再通知一下session池中的用户B的session,告诉他:你有新消息了。然后session-B再去读消息,然后有消息后将消息推送给客户端。或者有消息后给客户端推送一下有消息了,客户端再去拉。
这个session池使用在同步中,但是本质还是一个元数据,一般只需要存在于内存中即可,但是考虑到failover情况,那就需要持久化,这部分数据由于只需要指定单Key查询,用分布式NoSQL或关系型数据库都可以,一般复用当前的系统即可。
如果使用Tablestore,那么session表设计结构如下:
主键列顺序
第一列主键
第二列主键
属性列
列名
user_id
device_id
last_sequence_id
备注
接收者用户ID
设备ID,同一个用户可能会有多个设备,不同设备的读取位置可能不一致,所以这里需要一个设备ID。如果不需要支持多终端,则这一列可以省略。
该接收者已经推送给客户端的最新的顺序ID
5. 评论除了私信类型外,其他的feed流类型中,都有评论功能,评论的属性和存储库差不多,但是多了一层关系:被评论的消息,所以只要将评论按照被被评论消息分组组织即可,然后查询时也是一个范围查询就行。这种查询方式很简单,用不到关系型数据库中复杂的事务、join等功能,很适合用分布式NoSQL数据库来存储。
所以,一般的选择方式就是:
如果系统中已经有了分布式NoSQL数据库,比如Tablestore、Bigtable等,那么直接用这些即可。
如果没有上述系统,那么如果有MySQL等关系型数据库,那就选关系型数据库即可。
如果选择了Tablestore,那么“评论表”设计结构如下:
主键列顺序
第一列主键
第二列主键
属性列
属性列
属性列
字段名
message_id
comment_id
comment_content
reply_to
other
备注
微博ID或朋友圈ID等消息的ID
这一条评论的ID
评论内容
回复给哪个用户
其他
如果需要搜索评论内容,那么对这张表建立多元索引即可。
6. 赞最近几年,“赞”或“like”功能很流行,赞功能的实现和评论类似,只是比评论少了一个内容,所以选择方式和评论一样。
如果选择了Tablestore,那么“赞表”设计结构同评论表,这里就不再赘述了。
系统架构中加了元数据系统后的架构如下:
7. 搜索到此,我们已经介绍完了Feed流系统的主题架构,Feed流系统算是完成了。但是Feed流产品上还未结束,对于所有的feed流产品都需要有搜索能力,比如下面场景:
微博中的搜索用户。
搜索微博内容。
微信中搜索好友等。
这些内容搜索只需要字符匹配到即可,不需要非常复杂的相关性算法,所以只需要有能支持分词的检索功能即可,所以一般有两种做法:
使用搜索引擎,将存储库的内容和用户信息表内容推送给搜索系统,搜索的时候直接访问搜索系统。使用具备全文检索能力的数据库,比如最新版的MySQL、MongoDB或者Tablestore。
所以,选择的原则如下:
如果存储库使用了MySQL或者Tablestore,那么直接选择这两个系统就可以了。
如果整个系统都没使用MySQL、Tablestore,且已经使用了搜索系统,那么可以直接复用搜索系统,其他场景都不应该再额外加一个搜索系统进来,徒添复杂度。
如果使用Tablestore,那么只需要在相应表上建立多元索引即可:
如果需要对用户名支持搜索,那么需要对user_table建立多元索引,其中的nick_name需要是Text类型,且单字分词。
如果需要对Feed流内容支持搜索,那么需要对存储库表:store_table建立多元索引,这样就能直接对Feed流内容进行各种复杂查询了,包括多条件筛选、全文检索等。
系统架构中加了搜索功能后的架构如下:
8. 排序目前的Feed流系统中的排序方式有两种,一种是时间,一种是分数。
我们常用的微博、朋友圈、私信这些都是时间线类型的,因为这些产品定义中,需要我们主动关注某些人后才会看到这些人发表的内容,这个时候,最重要的是实时性,而不是发布质量,就算关注人发布了一条垃圾信息,我们也会被动看到。这种类型的产品适用于按照时间线排序。这一篇我们介绍的架构都是基于时间类型的。
另外一种是不需要关注任何人,我们能看到的都是系统希望我们看到的,系统在后台会分析我们的每个人的爱好,然后给每个人推送差异化的、各自喜欢的内容,这一种的架构和基于时间的完全不一样,我们在后续的推荐类型中专门介绍。
9. 删除Feed内容在Feed流应用中有一个问题,就是如果用户删除了之前发表的内容,系统该如何处理?因为系统里面有写扩散,那么删除的时候是不是也要写扩散一遍?这样的话,删除就不及时了,很难应对法律法规要求的快速删除。
针对这个问题,我们在之前设计的时候,同步表中只有消息ID,没有消息内容,在用户读取的时候需要到存储库中去读消息内容,那么我们可以直接删除存储库中的这一条消息,这样用户读取的时候使用消息ID是读不到数据的,也就相当于删除的内容,而且删除速度会很快。除了直接删除外,另外一种办法是逻辑删除,对于删除的feed内容,只做标记,当查询到带有标记的数据时就认为删除了。
10. 更新Feed内容更新和删除Feed处理逻辑一样,如果使用了支持多版本的存储系统,比如Tablestore,那么也可以支持编辑版本,和现在的微博一样。
11. 总结上面介绍了不同子功能的特点和系统要求,能满足需求的系统主要有两类,一类是阿里云的Tablestore单系统,一类是开源组件组成的组合系统。
开源组件组成的组合系统:包括MySQL、Redis、HBase等,这些系统单个都不能解决Feed流系统中遇到的问题,需要组合在一起,各司其职才能完成一个Feed流系统,适用于热衷开源系统,人多且喜欢运维操作的团队。
Tablestore单系统:只使用Tablestore单个系统就能解决上述的所有问题,这时候肯定有人要问?你是不是在吹牛? 这里不是吹牛,Tablestore在三年前就已经开始重视Feed流类型业务,之前也发表过多篇文章介绍,功能上也在专门为Feed流系统特别定制设计,所以到今天,只使用Tablestore一款产品,是可以满足上述需求的。选择Tablestore做Feed流系统的用户具有以下一些特征:
产品设计目标规模大,千万级或亿级。
不喜欢运维,喜欢专注于开发。
高效率团队,希望尽快将产品实现落地。
希望一劳永逸,未来系统随着用户规模增长可以自动扩容。
希望能按量付费,用户少的时候费用低,等用户增长起来后费用在跟随用户数增长。如果具有上述四个特征的任何一个,那么都是适合于用Tablestore。
架构实践
上面我们介绍了Feed流系统的设计理论,具体到不同的类型中,会有不同的侧重点,下面会逐一介绍。
朋友圈
朋友圈是一种典型的Feed流系统,关系是双写关系,关系有上限,排序按照时间,如果有个人持续产生垃圾内容,那就只能屏蔽掉TA,这一种类型就是典型的写扩散模型。
我们接下来会在文章《朋友圈类系统架构设计》中详细介绍朋友圈类型Feed流系统的设计。
微博
微博也是一种非常典型的Feed流系统,但不同于朋友圈,关系是单向的,那么也就会产生大V,这个时候就需要读写扩散模式,用读扩散解决大V问题。同时,微博也是主动关注类型的产品,所以排序也只能是时间,如果按照推荐排序,那么效果就会比较差。
接下里会在文章《微博类系统架构设计》中详细介绍微博类型Feed流系统的设计。
头条
头条是最近几年快速崛起的一款应用,在原有微博的Feed流系统上产生了进化,用户不需要主动关注其他人,只要初始浏览一些内容后,系统就会自动判断出你的喜好,然后后面再根据你的喜好给你推荐你可能会喜好的内容,训练时间长了后,推送的内容都会是你最喜欢看的。
后面,我们会在文章《头条类系统架构设计》中详细介绍头条类型Feed流系统的设计。
私信
私信也算是一种简单的Feed流系统,或者也可以认为是一种变相的IM,都是单对单的,没有群。我们后面也会有一篇文章《私信类系统架构设计》中做详细介绍。
总结
上面我们介绍了Feed流系统的整体框架,主要是产品定义、同步、存储、元数据、评论、赞、排序和搜索等内容,由于篇幅有限,每一章节都介绍的比较简单。读者如果对某一部分看完后仍然有疑问,可以继续再文后提问,我会继续去完善这篇文章,希望未来读者看完这篇文章后,就可以轻轻松松设计出一个亿级规模的Feed流系统。
另外,我们也欢迎有兴趣的读者一起来完成这个系列,帮忙实现朋友圈、微博、头条或者私信类型的文章,有任何问题都欢迎来讨论。
延伸
Feed类型的系统架构和IM(即时聊天)类型的系统架构非常类似,自从Tablestore从2016年开始优化此类系统,我们研发了Feed流和IM的通用底层框架-Timeline,目前已经演进到了V2版本,一体化支持存储、同步和搜索功能,我们已经有文章做了介绍:
《亿级消息系统的核心存储:Tablestore发布Timeline 2.0模型》《现代IM系统中的消息系统架构 - 架构篇》《现代IM系统中的消息系统架构 - 模型篇》《Tablestore权威指南》如果大家在阅读本篇文章或者上述文章时,有啥问题,或者对Tablestore有兴趣,都可以加入我们的钉钉群沟通: 11789671。
大数据应用之:MongoDB从入门到精通你不得不知的21个为什么?
一、引言:
互联网的发展和电子商务平台的崛起,催生了大数据时代的来临,作为大数据典型开发框架的MongoDB成为了No-sql数据库的典型代表。MongoDB从入门到精通你不得不知的21个为什么专为大数据时代,大数据应用系统系统分析、架构设计和平台开发人员而准备。希望能够为大家起到提纲挈领,指明大家学习目标和方向的作用。
一、正文
n1.MongoDB概述
1.1 MongoDB为何而生?
1.2 MongoDB有哪些技术特点?为何适应大数据时代的发展?
1.3 MongoDB不是万能良药,究竟适合哪些项目?
n2.MongoDB逻辑视图
2.1 mongoDB有哪些服务组件组成?
2.2 mongoDB服务组件间的关系?
n3.MongoDB物理存储
3.1 什么是无模式?
3.2 无模式为何还要集合呢?
3.3 集群模式下如何管理MongoDB数据库呢?
3.4 什么是分片?片与mongod又有和关系呢?
n4.MongoDB系统架构
4.1 单机模式架构结构?
4.2 集群模式结构结构?
4.3 MongoDB集群应用系统总体架构设计?
n5.MongoDB shell
5.1 Mongodb shell文档操作命令有哪些?
5.2 Mongodb shell集合操作命令有哪些?
5.3 Mongodb shell数据库操作命令有哪些?
5.4 Mongodb shell集群分片操作有哪些命令?
n6.MongoDB管理和维护
6.1 如何启动mongodb数据库?
6.2 如何启动MongoDB Web UI?
6.3 如何进行集群部署?
6.4 MongoDB关于备份和修复你知道多少?
6.5 安全与认证,我们如何确保MongoDB集群的安全?
三、应用架构
四、马上开始
有目标才有方向,方向既然已明了,我们还等什么呢?须知纸上得来终觉浅,绝知此事要躬行。从现在开始开启我们的MongoDB学习之旅吧。
五、招募大数据技术好友
新建了一个大数据技术交流的群,主要涉及计云计算、大数据开源框架Hadoop和MongoDB,有共同志向者请加群:293503507
作者:张子良
出处:http://www.cnblogs.com/hadoopdev
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。