数据库实验设计——朋友圈【概念篇】_朋友圈单双向好友关系 数据库表设计-程序员宅基地

技术标签: 微信  数据库  big data  

数据库真的不太好学23333,很多都要自己上网搜素。

一、理论层面

朋友圈表结构

涉及朋友圈数据的有四个核心的表:

一个是发布。发布数据记录了来自所有用户所有的feed,比如一个用户发布了几张图片,每张图片的URL是什么,在CDN里的URL是什么,它有哪些元属性,谁可以看,谁不可以看等等。
一个是相册。相册是每个用户独立的,记录了该用户所发布的所有内容。
一个是评论。评论就是针对某个具体发布的朋友评论和点赞操作。
一个是时间线。所谓“刷朋友圈”,就是刷时间线,就是一个用户所有朋友的发布内容。

朋友圈架构

整体架构如下,

  • 最上面为接入层,接入主要维护长连接,长连接主要为了安卓系统,一方面能够减少新建连接的性能损耗,另一方面由于谷歌的国内服务基本不可用,安卓的推送通知都是通过长连接哎完成的。

  • 接入层后面是逻辑层,逻辑层不仅有朋友圈,也有iOS的系统的通知,因为iOS App进入后台后只有15s的存活期,所以iOS上的推送通知要用API的Push完成。

  • 接下来是存储代理层,这一层主要负责一些关键数据的维护操作,比如用户在账号里面的动作操作和事故信息。

  • 再往下是KV存储层,这里不存在业务逻辑,只是单纯的Key-Value映射,负载均衡和容错。
    在这里插入图片描述

朋友圈流程举例

两个用户小王和Mary(如下图)。小王和Mary各自有各自的相册,可能在同一台服务器上,也可能在不同的服务器上。现在小王上传了一张图片到自己的朋友圈。上传图片不经过微信后台服务器,而是直接上传到最近的腾讯CDN节点,速度非常快。图片上传到该CDN后,小王的微信客户端会通知微信的朋友圈CDN:这里有一个新的发布(比如叫K2),这个发布的图片URL是什么,谁能看到这些图片,等等此类的元数据,来把这个发布写到发布的表里
在这里插入图片描述

在发布的表写完之后,会把这个K2的发布索引到小王的相册表里。所以相册表其实是很小的,里面只有索引指针。相册表写好了之后,会触发一个批处理的动作。这个动作就是去跟小王的每个好友说,小王有一个新的发布,请把这个发布插入到每个好友的时间线里面去
救命:什么是索引指针,什么是批处理

Mary上朋友圈了,而Mary是小王的一个好友。Mary拉自己的时间线的时候,时间线会告诉到有一个新的发布K2,然后Mary的微信客户端就会去根据K2的元数据去获取图片在CDN上的URL,把图片拉到本地。在这个过程中,发布是很重的,因为一方面要写一个自己的数据副本,然后还要把这个副本的指针插到所有好友的时间线里面去。如果一个用户有几百个好友的话,这个过程会比较慢一些。这是一个单数据副本写扩散的过程。但是相对应的,读取就很简单了,每一个用户只需要读取自己的时间线表,就这一个动作就行,而不需要去遍历所有好友的相册表。

使用写扩散的原因是,如果使用读是很容易失败的,一个用户如果要去读两百个好友的相册表,极端情况下可能要去两百个服务器上去问,这个失败的可能性是很大的。但是写失败了就没关系,因为写是可以等待的,写失败了就重新去拷贝,直到插入成功为止。
至于赞和评论的实现,是相对简单的。上面说了微信后台有一个专门的表存储评论和赞的数据,比如Kate是Mary和小王的朋友的话,刷到了K2这一条发布,就会同时从评论表里面拉取对应K2的、Mary留下的评论内容插入到K2内容的下方。而如果另一个人不是Mary和小王的共同朋友,则不会看到这条评论。

救命:什么是写扩列?

写扩散是主动把消息写到订阅者的消息列表里,这样订阅者就不用去我的outbox拉取消息 ,所以当我要是有很多订阅者时,我就要写很多次,这就是上面定义中说的写很重

在这里插入图片描述

仅好友可见功能

调用判断:当需要判断好友01id的界面能否取得某具体内容数据的时候,只需要通过表2判断该内容所归属usrid的好友关系列表表1中有没有 好友01id。

网上的一些trick

1.首先以每个用户的id为key生成一个list,list最好根据需求限制一下长度,毕竟不会有人刷朋友圈的时候会刷到前面几千条数据去吧
2.然后当用户A发布内容的时候往关注A的用户的list里将内容lpush进去(因为关注人可能比较多,可以使用异步操作),用户A删除内容的时候也将关注人list里相对的内容删除
3.当用户要查看朋友圈的时候就返回redis里的list的数据就行(也支持分页)
4.当用户关注或者取消关注一个人的时候需要清空list然后在关系数据库中搜索所有关注人发布的内容并存到list里面

二、应用层面

代码实现

  1. 消息表很好理解,存储所有用户发送的所有内容,图片存地址。
    utf8mb4格式可以存储emoji表情,具体可以参照之前的一篇文章
#消息表
CREATE TABLE friend_circle_message (
  id bigint(15) NOT NULL AUTO_INCREMENT COMMENT '主键',
  uid bigint(15) DEFAULT NULL COMMENT '用户id',
  content varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  picture varchar(200) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT '' COMMENT '图片',
  location varbinary(100) DEFAULT '' COMMENT '位置',
  create_time datetime DEFAULT NULL COMMENT '创建日期',
  PRIMARY KEY (id)
  FOREIGN KEY(uid) REFERENCES friend_circle_user(id)

  • uid表示作者id,可根据这个uid查询friend_circle_user表知道这个作者是谁。
  1. 时间轴表在朋友圈中是最关键的,存储着所有用的时间轴信息,因为当用户去拉取好友圈的时候,查询的就是本表,is_own字段用来区分当前数据是自己的发布还是好友发布的消息。
#时间轴表
CREATE TABLE friend_circle_timeline (
  id bigint(15) NOT NULL AUTO_INCREMENT,
  uid bigint(15) DEFAULT NULL COMMENT '用户id',
  fcmid bigint(15) DEFAULT NULL COMMENT '朋友圈信息id',
  is_own int(1) DEFAULT '0' COMMENT '是否是自己的',
  create_time datetime DEFAULT NULL COMMENT '创建日期',
  PRIMARY KEY (id)
)

  1. 评论表,每个元组就是一个评论;关联着一个用户、一个动态
#评论表
CREATE TABLE friend_circle_comment (
  id bigint(15) NOT NULL AUTO_INCREMENT COMMENT'评论编号',
  fcmid bigint(15) DEFAULT NULL COMMENT '朋友圈信息id',
  review_to bigint(15) DEFAULT NULL COMMENT '回复对象ID',
  uid bigint(15) DEFAULT NULL COMMENT '评论者id',
  content varchar(500) DEFAULT NULL,
  create_time datetime DEFAULT NULL COMMENT '创建日期',
  like_count int(10) DEFAULT '0' COMMENT '点赞数',
  PRIMARY KEY (id)
)

4.用户列表,存储所有用户的信息

#用户列表
CREATE TABLE friend_circle_user (
  id bigint(15) NOT NULL AUTO_INCREMENT COMMENT '主键',
  uid bigint(15) DEFAULT NULL COMMENT '用户id主键',
  nickname varchar(500) COMMENT '昵称比如蛋卷超人',
  sex int(1)   COMMENT '性别',
  password varchar(500)   COMMENT '登陆密码',
  PRIMARY KEY (id)
)

好友圈逻辑

  1. 发布朋友圈消息
    当用户发布一条朋友圈消息的时候,后端逻辑的处理(A和B已经是好友关系):
    用户A在朋友圈中发布一条消息,消息表t_friend_circle_message写入一条数据。时间轴表t_friend_circle_timeline中增加一条数据,uid设置A,is_own设置为1,表示在A的时间轴中增加一条自己发布的消息。查询用户A的好友,查到用户B(如果有还有其他好友D、E等等同样处理)时间轴表t_friend_circle_timeline中增加一条数据,uid设置B,is_own设置为0,表示在B的时间轴中增加一条好友发布的消息。
  2. 添加好友
    当用户A,添加用户C为好友之后,触发同步好友时间轴的操作
  INSERT INTO t_friend_circle_timeline (uid,fcmid,is_own,create_time)
            SELECT #{uid},`id`,0,create_time FROM t_friend_circle_message WHERE uid = #{fid};

把消息表t_friend_circle_message好友C发布的所有消息添加到自己的时间轴中。
再把消息表t_friend_circle_message自己发布的消息添加到好友C的时间轴中。
使用好is_own字段,因为都是互相添加好友的消息到自己的时间轴中,所以都应该为false(0)。

点赞实现

点赞其实很好做,记录点赞数++ 就可以实现,但是我们需要判断出当前用户是否点赞过,点过赞的标识出已点赞的状态,所以我们需要记录一条消息的点赞人id,当用户每次点赞的时候去查询一下点赞列表里是否存在当前用户的id。
消息id作为key,点赞人的uid作为value,放到redis中。
(存储的时候没有使用数组或字符串,而是直接把list[long] 存储的uid集合序列化了。在读取遍历的时候比较方便,但是取消点赞的时候需要遍历移除掉其中一位,不确定list合适不合适做为存储结构。)

@Override
public Page<TimelineDetail> page(long uid, int page, int pageSize) {
    
    int startNumber = (page - 1) * 10;
    Collection<TimelineDetail> list = timelineDetailMapper.page(uid, startNumber, pageSize);
    list.forEach(i -> getLikedAndCount(i, uid));

    return new Page<TimelineDetail>(list, 0, pageSize, page);
}

/**
 * 拿到是否点过赞 和点赞总数
 * 再获取点赞的人名。。
 */
private void getLikedAndCount(TimelineDetail timelineDetail, long uid) {
    
    Collection<Long> list = getLikeList(timelineDetail.getMessageId());
    if (CollectionUtils.isNotEmpty(list)) {
    
        List<String> nicknames = timelineDetailMapper.listNickname(list);
        if (CollectionUtils.isNotEmpty(nicknames)) {
    
            StringBuilder sb = new StringBuilder();
            nicknames.stream().filter(StringUtils::isNotEmpty).forEach(i -> sb.append(i).append(","));
            if (sb.length() > 0) {
    
                sb.deleteCharAt(sb.length() - 1);
            }
            timelineDetail.setLikeNickname(sb.toString());
        }

        list.stream()
                .filter(i -> i == uid)
                .forEach(i -> timelineDetail.setLiked(true));
        timelineDetail.setLikeCount(list.size());
    }
}

查询朋友圈的时候需要遍历redis中的值,然后把uid替换成昵称。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/joycychou/article/details/120809559

智能推荐

oracle 12c 集群安装后的检查_12c查看crs状态-程序员宅基地

文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态

解决jupyter notebook无法找到虚拟环境的问题_jupyter没有pytorch环境-程序员宅基地

文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境

国内安装scoop的保姆教程_scoop-cn-程序员宅基地

文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn

Element ui colorpicker在Vue中的使用_vue el-color-picker-程序员宅基地

文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker

迅为iTOP-4412精英版之烧写内核移植后的镜像_exynos 4412 刷机-程序员宅基地

文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机

Linux系统配置jdk_linux配置jdk-程序员宅基地

文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk

随便推点

matlab(4):特殊符号的输入_matlab微米怎么输入-程序员宅基地

文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入

C语言程序设计-文件(打开与关闭、顺序、二进制读写)-程序员宅基地

文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。‍ Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。

Touchdesigner自学笔记之三_touchdesigner怎么让一个模型跟着鼠标移动-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动

【附源码】基于java的校园停车场管理系统的设计与实现61m0e9计算机毕设SSM_基于java技术的停车场管理系统实现与设计-程序员宅基地

文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计

Android系统播放器MediaPlayer源码分析_android多媒体播放源码分析 时序图-程序员宅基地

文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;amp;gt;Jni-&amp;amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图

java 数据结构与算法 ——快速排序法-程序员宅基地

文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法