Spring开发社交模块小记
一、引言
社交模块作为热点数据来说,可能会频繁改动字段,因此用Mysql是肯定不现实的,一般使用Redis。这里我以发表朋友圈动态为例,社交模块包括发表动态,点赞、评论、收藏、关注以及签到统计等模块,这里我简单实现了动态发表,点赞、评论这三个模块。
关注功能模块,使用Redis集合Set,一个人两个集合数据,定时更新到数据库
https://blog.csdn.net/INGNIGHT/article/details/107066022
https://www.cnblogs.com/linjiqin/p/12828315.html
点赞、收藏模块,Set(点赞视频、点赞人评论)和Hash(like::url =1或0)结构都比较合适
https://juejin.cn/post/6904816415912493069#heading-10
https://juejin.cn/post/6895185457110319118#heading-20
https://juejin.cn/post/6844903967168675847
评论模块,可以选择list,用list和zset存储id,其他存储内容
https://juejin.cn/post/6844903709374169102
https://blog.csdn.net/qq171563857/article/details/107406409
https://symonlin.github.io/2019/07/29/redis-1/
登录统计、签到,使用Redis的Bitmap
二、数据库设计
数据库自行参考,可以考虑持久化到数据库。这里说一下我的设计思路:
动态分为视频动态和图片形式的动态,类似于抖音和微信朋友圈,该模块单独编写,需要信息从其他模块获取;评论为二级评论,后端包装后返回,评论可以点赞等操作;点赞优先经过Redis,若没有查询数据库
1 | create database if not exists lamp_social; |
三、动态发表模块设计
1、介绍
Feed流产品在我们手机APP中几乎无处不在,常见的Feed流比如微信朋友圈、新浪微博、今日头条等。对Feed流的定义,可以简单理解为只要大拇指不停地往下划手机屏幕,就有一条条的信息不断涌现出来。
大多数Feed流产品都包含两种Feed流,一种是基于算法推荐,另一种是基于关注(好友关系)。例如下图中的微博和知乎,顶栏的页卡都包含“关注”和“推荐”这两种。两种Feed流背后用到的技术差别会比较大(读扩散、写扩散)。
2、Redis结构选择
动态发布因为考虑到先缓存到Redis,在异步保存到MySql,因此动态主键使用Redis的自增函数,通过Redis生成MySql的动态主键;
对于动态数据的存储,我使用了list存储结构,新的数据从左边压入list,考虑到feed流查询,我还设置了一个伴生list列表,用来与动态同步存储主键值,首先通过lastid查询上一次浏览的值,查询list的index,在通过存储动态的列表返回一个列表;同时使用了读写锁,是为了保证原子性;
最后异步或定时检查列表长度,若过长可以从右边舍弃,或者设置列表过期时间,插入的时候重新刷新过期时间
四、评论模块设计
1、介绍
mysql表字段,评论父表和字表存储在同一个数据表,根据p_comment_id
字段分辨,返回的时候先查询出总的list,在使用JDK8的Stream流形成树形结构返回。ORM映射使用了Fluent MyBatis ,树形结构格式转换;
2、对象类型转换工具
首先创建转换工具类,这里先将对象转化为json,在通过解析json进行复制操作
1 | import com.alibaba.fastjson.JSON; |
3、多级评论树型拼接
我的VO类,主要用将数据库的评论拼装返回前端
1 |
|
树形结构拼装,用了jdk8新特性
1 |
|
如果遇到下面问题,回退版本号,我当时遇到了
1 | // fastJson1.2.78版本会概率性出现该错误,回退到1.2.76即可 |
4、评论简单过滤
简单原理如上图所示,创建结点类,里面包含是否是敏感词结束符,以及一个HashMap,哈希里key值存储的是敏感词的一个词,value指向下一个结点(即指向下一个词),一个哈希表中可以存放多个值,比如赌博、赌黄这两个都是敏感词。
敏感词文件存在在resources
文件夹下,通过类加载器获取里面的敏感词。在springboot中,被@PostConstruct
修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。PostConstruct
在构造函数之后执行,init()
方法之前执行。
1 | /** |
五、点赞模块设计
1、问题描述
考虑到点赞是字段频繁变动的,用Mysql肯定不合适,使用需要使用Redis内存数据库。这里以动态点赞为例子,点赞模块需要解决的几个问题
- 用户对某个动态点赞/取消点赞
- 该动态获得了多少赞
- 用户是否已经点赞该动态
- 用户的总点赞数是多少
- 数据的持久化
2、Redis数据结构选择
对于点赞来说,Set和Hash结构都可以选择。set中的值不能重复,是无序不重复的,Hash相当于Map集合,相当于key-Map,通常来存储经常变动的对象。对于点赞,两种结构都可以,根据业务自由选择
1. Set结构存储
这里我选择了一种较为简单的存储方案,不过这种方案很难进行MySql持久化,用Set结构存储某视频点赞的用户,用String结构存储用户点赞数量,查询用户是否点赞只需查询用户是否在这个Set集合里,点赞/取消点赞加入/移除Set,查询某视频点赞数只需统计Set集合中的用户数量,两个存储结构为
- 视频点赞Set的存储结构
like:dynamic:{dynamicType}:{dynamicId}={userId}
- 用户点赞数量的String存储结构
like:user:{userId}=value
2. Hash结构存储
这种Hash结构可以记录点赞人和被点赞产品,还有点赞状态(点赞/取消点赞设置值为1/0),还可以固定时间间隔取出 Redis 中所有点赞数据
- 视频点赞Hash的Key结构
like:dynamic:{dynamicType}
,里面的键值对为{userId}::{dynamicId}=1
- 视频点赞数Hash的Key结构
like:count:dynamic
,里面的键值对为{dynamicId}={count}
- 用户点咱叔Hash的Key结构
like:count:user
,里面的键值对为{userId}={count}
3、编码实现
1. 配置redis
首先进行redis配置,实现序列化,否则不能正常显示
1 | /** |
创建FastJson2JsonRedisSerializer
类
1 | public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T> { |
2. Redis工具类编写
新建RedisKeyUtil,进行key的拼接
1 | /** |
3. 使用Set结构存储点赞
redis点赞模块service代码,自己写的,没有持久化,仅供参考
1 |
|
4. 使用Hash结构存储点赞
1 |
|
1 | /** |
5. Quartz定时任务持久化
对于Hash结构存储的,还可以根据::分离出点赞人和被赞动态,拆分后进行持久化,配置好Quartz定时任务后,下面举例其中一个
1 | // 返回需要插入数据库的列表 |
以上是我暂时做的,可能有很多问题,如果有问题,希望能够指出,后期不一定可放源码