数据库真的不太好学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。
1.首先以每个用户的id为key生成一个list,list最好根据需求限制一下长度,毕竟不会有人刷朋友圈的时候会刷到前面几千条数据去吧
2.然后当用户A发布内容的时候往关注A的用户的list里将内容lpush进去(因为关注人可能比较多,可以使用异步操作),用户A删除内容的时候也将关注人list里相对的内容删除
3.当用户要查看朋友圈的时候就返回redis里的list的数据就行(也支持分页)
4.当用户关注或者取消关注一个人的时候需要清空list然后在关系数据库中搜索所有关注人发布的内容并存到list里面
#消息表
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)
#时间轴表
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)
)
#评论表
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)
)
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替换成昵称。
文章浏览阅读3.8k次,点赞9次,收藏28次。直接上一个工作中碰到的问题,另外一个系统开启多线程调用我这边的接口,然后我这边会开启多线程批量查询第三方接口并且返回给调用方。使用的是两三年前别人遗留下来的方法,放到线上后发现确实是可以正常取到结果,但是一旦调用,CPU占用就直接100%(部署环境是win server服务器)。因此查看了下相关的老代码并使用JProfiler查看发现是在某个while循环的时候有问题。具体项目代码就不贴了,类似于下面这段代码。while(flag) {//your code;}这里的flag._main函数使用while(1)循环cpu占用99
文章浏览阅读347次。idea shift f6 快捷键无效_idea shift +f6快捷键不生效
文章浏览阅读135次。Ecmacript 中没有DOM 和 BOM核心模块Node为JavaScript提供了很多服务器级别,这些API绝大多数都被包装到了一个具名和核心模块中了,例如文件操作的 fs 核心模块 ,http服务构建的http 模块 path 路径操作模块 os 操作系统信息模块// 用来获取机器信息的var os = require('os')// 用来操作路径的var path = require('path')// 获取当前机器的 CPU 信息console.log(os.cpus._node模块中有很多核心模块,以下不属于核心模块,使用时需下载的是
文章浏览阅读10w+次,点赞435次,收藏3.4k次。SPSS 22 下载安装过程7.6 方差分析与回归分析的SPSS实现7.6.1 SPSS软件概述1 SPSS版本与安装2 SPSS界面3 SPSS特点4 SPSS数据7.6.2 SPSS与方差分析1 单因素方差分析2 双因素方差分析7.6.3 SPSS与回归分析SPSS回归分析过程牙膏价格问题的回归分析_化工数学模型数据回归软件
文章浏览阅读7.5k次。如何利用hutool工具包实现邮件发送功能呢?1、首先引入hutool依赖<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.19</version></dependency>2、编写邮件发送工具类package com.pc.c..._hutool发送邮件
文章浏览阅读867次,点赞2次,收藏2次。docker安装elasticsearch,elasticsearch-head,kibana,ik分词器安装方式基本有两种,一种是pull的方式,一种是Dockerfile的方式,由于pull的方式pull下来后还需配置许多东西且不便于复用,个人比较喜欢使用Dockerfile的方式所有docker支持的镜像基本都在https://hub.docker.com/docker的官网上能找到合..._docker安装kibana连接elasticsearch并且elasticsearch有密码
文章浏览阅读1.3w次,点赞57次,收藏92次。整理 | 郑丽媛出品 | CSDN(ID:CSDNnews)近年来,随着机器学习的兴起,有一门编程语言逐渐变得火热——Python。得益于其针对机器学习提供了大量开源框架和第三方模块,内置..._beeware
文章浏览阅读7.9k次。//// ViewController.swift// Day_10_Timer//// Created by dongqiangfei on 2018/10/15.// Copyright 2018年 飞飞. All rights reserved.//import UIKitclass ViewController: UIViewController { ..._swift timer 暂停
文章浏览阅读986次,点赞2次,收藏2次。1.硬性等待让当前线程暂停执行,应用场景:代码执行速度太快了,但是UI元素没有立马加载出来,造成两者不同步,这时候就可以让代码等待一下,再去执行找元素的动作线程休眠,强制等待 Thread.sleep(long mills)package com.example.demo;import org.junit.jupiter.api.Test;import org.openqa.selenium.By;import org.openqa.selenium.firefox.Firefox.._元素三大等待
文章浏览阅读3k次,点赞4次,收藏14次。Java软件工程师职位分析_java岗位分析
文章浏览阅读2k次。Java:Unreachable code的解决方法_java unreachable code
文章浏览阅读1w次。1、html中设置标签data-*的值 标题 11111 222222、点击获取当前标签的data-url的值$('dd').on('click', function() { var urlVal = $(this).data('ur_如何根据data-*属性获取对应的标签对象