当前位置: 首页 > news >正文

社交类网站设计:经典feed流系统架构详细设计(小红书微博等)

文章目录

  • 一、关注服务
    • 1、粉丝、关注数架构设计
      • (1)数据库实现方案1
      • (2)数据库实现方案2
      • (3)基于redis缓存优化
      • (4)使用专用计数服务
      • (5)近似计数(牺牲部分精度)
    • 2、粉丝、关注列表架构设计
      • (1)数据库实现方案1
      • (2)数据库实现方案2:分库分表不友好
      • (3)关注与粉丝列表缓存
      • (4)业务层面优化
      • (5)用户自己查询列表:不做限制
      • (6)代码落地
    • 3、高性能关注服务架构设计
      • (1)热点事件读并发高
      • (2)热点事件写并发高
      • (3)大数据量关系链表分库分表:普通用户/大V
      • (4)大V爆火数据库优化
  • 二、点赞计数服务
    • 1、通用计数服务设计
      • (1)DB写,缓存读(有性能限制,计数精准)
      • (2)缓存读写,批量入库(性能高,redis宕机计数不精准)
      • (3)扩展:通用计数功能DB优化
      • (4)计数服务如何分库分表?
      • (5)引入MQ缓冲异步入库
    • 2、高并发计数服务架构设计
      • (1)异步数据入库
      • (2)爆火帖子流量处理
      • (3)大数据量下判断用户是否已经点赞
      • (4)大数据量下查看点赞用户详情
  • 三、帖子服务架构设计
    • 1、帖子基本功能设计
      • (1)帖子数据库设计
      • (2)帖子查看架构设计
      • (3)发帖子功能设计
    • 2、帖子高性能架构设计
      • (1)上亿帖子分库分表
      • (2)redis缓存设计1:用户维度缓存(查询个人帖子列表)
      • (3)redis缓存设计2:时间维度缓存(参考Feed流)
      • (4)帖子列表查询功能(用户维度/时间维度/频道维度)(参考Feed流)
      • (5)帖子详情查看
    • 3、Feed流架构设计
      • (1)Feed流简介
      • (2)pull模式
      • (3)push模式(对查询进行优化)
      • (4)push和pull模式优化
      • (5)发帖、读贴流程及缓存架构设计
  • 四、附近的人架构设计
    • 1、架构设计
  • 五、评论服务架构设计
    • 1、什么是评论系统
    • 2、评论系统基本功能设计
      • (1)数据库设计与选型
      • (2)基本功能实现
    • 3、高性能评论系统架构设计
      • (1)基于mysql+redis缓存架构设计
      • (2)分库分表或者使用NewSQL
      • (3)高性能判断是否给评论点赞
  • 六、签到服务架构设计
    • 1、基本功能设计

一、关注服务

1、粉丝、关注数架构设计

在这里插入图片描述

(1)数据库实现方案1

fllower表记录被哪些用户关注(id、userId:用户ID、fllowerId:粉丝ID)
attention表记录当前用户关注了哪些用户(id、userId:用户ID、attentionId:关注用户ID)

-- 查询用户详细信息,首页需要显示用户头像、昵称等信息
select * from user where id=1;
-- 查询粉丝数
select count(1) from fllower where userId=1;
-- 查询关注数
select count(1) from attention where userId=1;

除了用户基本信息以外,还需要额外sql查询粉丝数和关注数。
如果表数据不多的话,粉丝数和关注查询的话,性能还是可以的。
但是单个表的数据会越来越多,而且关系网非常庞大,数据量会呈指数级上升,基于数据库很快会达到性能瓶颈的。

(2)数据库实现方案2

user表(id、name、fllower_count:粉丝数、attention_count:关注数)
user表新增粉丝数和关注数字段,查询方便了,但是每次关注和被关注,都需要在user表里(关注者和被关注者都要操作)对应数量加减。
可以同一事务中进行加减,也可以用MQ异步加减,但是异步加减需要保证一致性。

-- 查询粉丝数、关注数、用户详细信息,只需要查询一次即可
select * from user where id=1;

(3)基于redis缓存优化

注意,redis只能用于缓存,并且还有过期时间
具体数据以方案2数据库中的数据为准!

fllower_count:粉丝数、attention_count:关注数,存入redis,每次关注和被关注(关注者和被关注者都要操作),在redis中增减

// 对应redis的key
user:fllower_count:1 10000
user:attention_count:1 20000

或者连带用户的信息都存入redis中,使用hash格式,hash格式也可以实现字段的自增减操作,但是用户信息修改以后,需要将缓存删除,保证缓存一致性问题

(4)使用专用计数服务

开发独立的计数服务(如 Twitter 的 Snowflake 方案变种),通过分布式计数器处理关注数和粉丝数的增减,避免数据库直接承担高并发计数压力。
- 每个用户的计数分配独立的计数器,通过 Redis 的 INCR 命令原子性更新计数。
- 定期将 Redis 中的计数批量持久化到数据库(如每10分钟同步一次),降低数据库写入频率。

(5)近似计数(牺牲部分精度)

对于非核心场景(如显示“100万+粉丝”),使用概率数据结构(如 HyperLogLog)估算粉丝数,减少内存占用和计算成本。

2、粉丝、关注列表架构设计

注意,粉丝列表和关注列表,访问频率并不会很大,主页中也只会展示粉丝数和关注数。
只有用户点击粉丝数和关注数以后,才会打开列表页面。
在这里插入图片描述

(1)数据库实现方案1

fllower表记录被哪些用户关注(id、userId:用户ID、fllowerId:粉丝ID、create_time)
attention表记录当前用户关注了哪些用户(id、userId:用户ID、attentionId:关注用户ID、create_time)

-- 查询粉丝列表,包含基本信息,并且分页、按照时间排序
select * from fllower f 
left join user u on f.userId = u.id
where userId=1 order by create_time desc limit 1,10;
-- 查询关注数,包含基本信息,并且分页、按照时间排序
select * from attention
left join user u on f.userId = u.id
where userId=1 order by create_time desc limit 1,10;

数据量多的话,查询效率肯定是非常差的,如果分页限制的话,性能会好很多。

注意,排序有可能是按照时间排序,微博是按照大V粉丝数进行排序的。

(2)数据库实现方案2:分库分表不友好

只使用一个relationship关系表(id、attentionId:关注用户ID、fllowerId:被关注者ID)
这样的话,关注操作只需要记录一张表数据即可,同时查询的话也更简单

-- 查询粉丝列表
select * from relationship r 
left join user u on r.fllowerId = u.id
where r.fllowerId=1 order by create_time desc limit 1,10;-- 查询关注列表
select * from relationship r 
left join user u on r.attentionId = u.id
where r.attentionId=1 order by create_time desc limit 1,10;

但是分库分表不友好,因为无法确定是根据attentionId分,还是根据fllowerId分。

(3)关注与粉丝列表缓存

缓存条数200条(按需设置缓存条数)即可,加上5-10秒过期时间,因为百万粉丝的话不可能都缓存。
每次关注和取消关注,基于redis的list结构,存储用户ID,(关注者和被关注者都要操作)。
查询的时候,每次分页查询10条userId,然后通过userId来查询用户的基本信息,比如redis的mget、mysql的in操作(如果都存入redis缓存的话,更新用户信息缓存更新是不及时的)。

-- KEYS[1]为用户ID,ARGV[1]为关注者或被关注者的基本信息
-- 需要保证原子操作,虽然不一定需要保证原子性,因为最终结果一定是原子的
local size = redis.call('get', KEYS[1])
if(tonumber(size)>=200) thenredis.call('rpop', KEYS[1])
end
redis.call('lpush', KEYS[1], ARGV[1])
return 0

如果取关的话,从redis的list直接删除对应数据,变成199条,不需要额外从数据库补充1条,因为不差那一条,而且数据过期以后会自动更新到redis,又变成200条了,更何况还会有新人关注他。

为什么是修改redis?而不是删除redis?
因为如果热门大V频繁被关注和取消关注的话,通常伴随着较大查询qps,如果直接删除缓存,可能对数据库的性能影响较大

(4)业务层面优化

用户浏览关注、粉丝列表,最多支持看200条。
比如说微博的关注粉丝列表,往下一直翻的话,就会触发限制

在这里插入图片描述

(5)用户自己查询列表:不做限制

用户查询自己,并发量并不会很高,所以可以取消缓存和查询数的设计,直接分页查库即可。

这里涉及到分页查询优化问题,深分页优化问题。

(6)代码落地

// 查询粉丝/关注列表
public List search(int userId, int type, int start, int stop) {
// 一、type=0 自己查询自己,直接查库
if (type == 0) {select fllower 或 attention ,查询关注列表和粉丝列表
} // 二、type=1 别人查询自己
if (type == 1) {1、先从redis查询list列表,获取userids2、根据userids,进行mget或者mysql的in操作,查询用户基本信息3、如果缓存过期,select fllower 或 attention ,查询关注列表和粉丝列表,然后存储redis中
} }

3、高性能关注服务架构设计

(1)热点事件读并发高

1、可以考虑使用本地缓存+redis缓存的方式,或者双重本地缓存。

使用缓存,都需要考虑缓存问题,比如说缓存与数据库的一致性、缓存雪崩、击穿、穿透问题。
从查询数据库性能优化谈到redis缓存-谈一谈缓存的穿透、雪崩、击穿

(2)热点事件写并发高

1、写并发过高,可以考虑使用MQ进行削峰填谷。

2、或者借鉴BufferTrigger缓冲区,实现请求合并、流量聚合。
流量聚合简单来说就是把多次的请求整合为一个请求处理,业务对单次的请求不敏感时并且能接受一定延迟时才能使用。
1.提供一个能存放数据的容器
2.当达到指定数量时输出容器中的数据
3.当达到指定时间时输出容器中的数据
使用场景:对大量的数据进行聚合,然后进行批量操作,适用于数据量大且相似或相同数据多的任务或者能接受一定时间内的延迟问题。

3、BufferTrigger和MQ协同使用的方式。

4、关注服务需要注意MQ顺序消费问题。(全局顺序消费、局部顺序消费)
全局顺序消费:全局只有一个queue,和一个消费者实例
局部顺序消费:通常在实际应用中,我们需要将同一个订单号(或者userId)的相关操作,按照规则(可以是hash或取模等)发送到同一个queue上,然后消费者实例,使用顺序消费模式消费消息。

注意,如果是单纯计数的话,不太需要顺序消费,因为最终的结果都是一样的。
如果是记录关注列表的话,是需要顺序消费的。取关是删除,关注是新增,如果顺序相反,会出问题。

(3)大数据量关系链表分库分表:普通用户/大V

1、为了分库分表,就需要关系链表进行区分开:
fllower表记录被哪些用户关注(id、userId:用户ID、fllowerId:粉丝ID、create_time)
attention表记录当前用户关注了哪些用户(id、userId:用户ID、attentionId:关注用户ID、create_time)
直接通过userId进行分库分表。

2、大V,几千万粉丝分库分表
大V以一张单独的表进行存储。大V认证以后,可以进行判断以后直接查询对应的数据库。

其实,主要是fllower表需要分库分表,因为一个人的粉丝可能很多,但是关注者并不会很多。

(4)大V爆火数据库优化

进行数据单独迁移:
1、配置当前数据库开关为单写
2、通过脚本迁移静态数据。迁完后,验证静态数据迁移准确性
3、配置当前数据库开关为双写(代码、binlog方式)
4、切换数据源,服务提供的数据源切换到新库

二、点赞计数服务

1、通用计数服务设计

(1)DB写,缓存读(有性能限制,计数精准)

在这里插入图片描述

(2)缓存读写,批量入库(性能高,redis宕机计数不精准)

在这里插入图片描述

点赞业务特点:
1、吞吐量高
2、能接受数据不一致

可以用MySQL做持久存储,Redis做缓存,读写操作落缓存,异步或线程定期刷新DB。
counter点赞数表:id,postld:帖子ID,count:点赞数
Redis存储:key:postld,value:count

(3)扩展:通用计数功能DB优化

一个人可能有关注数、粉丝数、总访问量。
一个帖子可能有转发量、阅读量、点赞量。

数据库表可以优化为:
couter计数表:id、userId、targetId:计数目标ID、type:计数类型(阅读数、点赞数等)、countValue:数量。
redis存储:使用hash存储

这样的话,查询一个帖子的转发数、阅读数、点赞数:

-- 数据库查询,结果为多条,根据type区分是转发数、阅读数、点赞数
select * from couter where targetId = 1;-- redis查询
hgetall targetId

注意,该方案有一个缺点,严重依赖redis。redis宕机、异常可能会导致数据不一致。
数据少部分缺失,点赞计数服务是业务允许的。redis实现主从、主备即可。
部分精准数据(比如购买量),可以考虑第一种DB写,缓存读方案。

(4)计数服务如何分库分表?

根据userId进行分库分表。

(5)引入MQ缓冲异步入库

引入MQ、队列等缓冲组件,实现削峰填谷
在这里插入图片描述

2、高并发计数服务架构设计

(1)异步数据入库

异步任务将缓存数据写入数据库,并不需要每次轮训所有数据进行写入。
redis中数据是非常多的,如果遍历所有的key入库,对性能影响是非常大的。

可以每次写入操作,写入redis的zset,值为数据的id,并且分数设置为时间戳。
定时任务从zset中取,优先取时间最旧的,然后根据取出的id进行数据库更新操作。

(2)爆火帖子流量处理

计数服务进行模糊计数(10W+、100W+)等。
部分流量丢弃,比如自己的评论只在前端显示,其他人是看不到的。(像直播弹幕,有的弹幕并不是所有人都会看到,有可能被丢弃)

(3)大数据量下判断用户是否已经点赞

考虑使用redis:

-- 某个帖子被某个用户点赞,该方式在大数据量场景内存占用较高
set 帖子ID:用户ID 1
-- 或者使用hash格式
hset 帖子ID 用户ID 1-- 考虑使用bitmap数据类型,该方式只能用于ID为整数的方式
setbit 帖子ID 用户ID 1
setbit 1000 100 1
-- 格式:setbit key offset value

大数据量,考虑使用布隆过滤器:布隆过滤器,如果数据不存在则一定不存在,数据存在不一定存在(有误判率)
但是,布隆过滤器是无法删除数据的。
在这里插入图片描述

(4)大数据量下查看点赞用户详情

redis中数据格式:

-- 如果每个帖子数据量太多(几十万),可以考虑分片
帖子ID(1) => [userId1, userId2 ...]
帖子ID(2) => [userId10, userId11 ...]
帖子ID(3) => [userId20, userId21 ...]

其实一般情况下,不需要查看一个帖子下面所有点赞人数的,只缓存最近的几个即可。
在这里插入图片描述

三、帖子服务架构设计

可以发布视频、图片、文本等等帖子。
不管是微博还是⼩红书,还是朋友圈等,都有⼀个最多可以发多少张图⽚的限制,所以这些app不可能让你⽆限制的发图,这样肯定对于系统资源占⽤是⽐较⼤的,并且如果是发视频的话只能发⼀个视频。
在这里插入图片描述

1、帖子基本功能设计

(1)帖子数据库设计

1、帖子主表
ID、title:标题、uid:作者ID、channel_id:频道ID、type:图文视频、labels:标签、address:图片视频地址,多个内容用逗号分隔、
发帖位置(同城搜索)、发帖经纬度(据你多远)、发帖时间、是否置顶、置顶时间

2、帖子内容表
ID、帖子ID、content:帖子内容(文案)

3、帖子点赞表
ID、帖子ID、用户ID

(2)帖子查看架构设计

如果量不大的话,可以直接查询数据库/使用redis缓存。

select * from 帖子表 left join 帖子内容表 on 帖子.id = 帖子内容.帖子ID;

如果涉及查询、模糊匹配,可以考虑通过binlog的方式将 帖子内容同步到ES中,进行文本搜索。
在ES中冗余帖子标题、频道、标签、位置、内容等等。

(3)发帖子功能设计

总体流程:发帖子的时候,添加图片和视频,当点击确定的时候,视频和图片已经上传好了(异步上传),返回一个ID或者地址,只需要在数据库里存储这个地址即可。

细节处理:标签处理、文本和视频审核、处理表情、处理视频(添加水印等)。

2、帖子高性能架构设计

(1)上亿帖子分库分表

帖子上亿之后,分库分表其实并不是很好实现,不论是根据帖子ID分,还是根据用户ID分,其实对于一些功能都不是很友好。
比如说,有可能按照同城搜索、按照频道搜索、按照用户搜索等等。

或许考虑根据时间来进行分库分表,或许更加合理一些。

(2)redis缓存设计1:用户维度缓存(查询个人帖子列表)

查询“我的关注”功能,以用户维度进行缓存。

同一个用户一天发出的帖子数量是有限的,通常不超过10条,平均3条左右,单个用户一周发的帖子很难超过100KB,极端情况下1MB,远低于Redis value大小的上限。
所以考虑设计Redis:

key:userld+时间戳(精确到星期)
value:Redis为hash类型,field为postId,value为帖子内容
expire设置为一个星期,即最多同时存在两个星期的数据(假设每贴平均长度0.1KB,1亿用户每天发3贴预计数量为400GB)

对某个用户一段时间范围的查找变为针对该用户本周时间戳的hscan命令,用户发帖等操作同时更新DB和缓存,DB的变更操作记录保证一致性。

但是,有些热用户的粉丝数量极高,意味着这个热点用户所在Redis服务器的查询频率为1000万每秒,所以这里我们再进一步引入本地缓存来缓解服务端缓存压力。

(3)redis缓存设计2:时间维度缓存(参考Feed流)

查询“同城、频道”功能,以时间、置顶维度查询所有帖子。

先查询置顶的,剩余的按照发帖时间倒序排序,存入redis中(list格式)。
用户发帖,可以考虑DB和Redis双写,头插到缓存中,并更新过期时间。

可以考虑加上缓存淘汰策略,防止内存过高溢出。
也可以考虑使用滑动窗口的方式,动态删除和更新帖子的缓存。
也可以考虑添加热点探测功能,动态缓存热点帖子。

(4)帖子列表查询功能(用户维度/时间维度/频道维度)(参考Feed流)

1、查询预热,查询第一页的时候将下一页异步加载出来。
2、简略信息展示:文本过多展示…,视频和图片展示前三个,想要查看更多的话需要点击更多单独查看详情。

大致流程:
1、查询redis,list结构,key为用户id,value为帖子的json信息(id、标题、图片地址、内容等)。或者时间维度,key为时间或者频道ID。
2、根据帖子ID,查询帖子的点赞数、转发数等,进行数据拼装。
3、如果redis没查到,查询数据库并更新缓存。
4、用户拿到数据之后自行渲染,图片和视频的地址通过用户异步渲染即可。

redis缓存数据:
可以考虑缓存时间,比如缓存2周以内的帖子。
也可以考虑缓存的数据条数,比如100条,按照时间进行顶替。
冷门数据查询mysql即可,不需要进行缓存。

(5)帖子详情查看

根据帖子的ID,将帖子信息缓存到redis中。
考虑到部分热贴redis扛不住,可以考虑本地缓存。

3、Feed流架构设计

(1)Feed流简介

Feed:Feed流中的每⼀条状态或者消息都是Feed,⽐如朋友圈中的⼀个状态就是⼀个Feed,微博中的⼀条微博就是⼀个Feed。
Feed流:持续更新并呈现给⽤户内容的信息流。每个⼈的朋友圈,微博关注⻚等等都是⼀个Feed流。

Timeline:按照时间的Feed流,Timeline其实是⼀种Feed流的类型,微博,朋友圈都是Timeline类型的Feed流,但是由于Timeline类型出现最早,使⽤最⼴泛,最为⼈熟知,有时候也⽤Timeline来表示Feed流。
关注⻚Timeline:展示其他⼈Feed消息的⻚⾯,⽐如朋友圈,微博的⾸⻚等。
个⼈⻚Timeline:展示⾃⼰发送过的Feed消息的⻚⾯,⽐如微信中的相册,微博的个⼈⻚等。

Feed流系统最⼤的特点:
读写严重不平衡,读多写少,⼀般读写⽐例都在10:1,甚⾄100:1之上。

随着⽤户关系的变化,某个⽤户的timeline也随之变化。⽽⼀个⽤户对⾃⼰帖⼦的增删,会影响多个其他⽤户的timeline。
可⻅timeline的内容受⽤户间关系影响,当用户关系复杂时,timeline的性能将会受到挑战。

(2)pull模式

和push模式不同,pull模式下⽤户每次新增/删除不需要同步帖子数据到他的所有follower,所以不存在push模式下热点⽤户增删帖⼦瓶颈。
但是每个⽤户查询我关注的帖子时,需要同时查询其所有的关注的⽤户近期帖⼦列表,查询压力比较大。

-- 查询我的关注(如果分库分表的话,查询比较麻烦)
-- 不需要timeline表了,直接查投递箱即可,或者直接查帖子表即可
select * from post where userid in (1, 2, 3) order by time limit 1, 10;

在这里插入图片描述
小型的系统,使用pull就可以了。

(3)push模式(对查询进行优化)

push模式的特点是:⽤户每次新增⼀条帖⼦,将此帖⼦推到他的follower所在DB分⽚上,follower在每次浏览timeline时,直接查询⾃⼰分⽚所存储的数据。

timeline表结构:
ID、用户ID(关注者ID)、帖子ID、帖子时间

比如说,用户1有2、3、4三个粉丝,用户1发布了一个帖子(ID为99)以后,在timeline中记录四条记录(自己本身也需要记录一条):

ID、用户ID(关注者ID)、帖子ID、帖子时间
1	1	99	20252	2	99	20253	3	99	20254	4	99	2025

分库分表的话,根据用户ID分库分表即可。

优点:查询比较方便:

select * from timeline where userid = 2;

缺点:
1、如果粉丝比较多的话,需要一次性在数据库中新增很多数据,造成增删帖⼦瓶颈。
2、除了发帖以外,删帖、关注、取关操作,同样需要操作timeline表数据。

(4)push和pull模式优化

push优化:
设置上限(微信好友的上限,qq好友的上限,小红书、微博关注上限)

pull优化:
增加轮询间隔,减少请求次数
主动刷新多次增加验证码等限制

push和pull结合:
1、可以对⼤V采⽤pull(拉)模式,普通⽤户使⽤push(推)模式。大V粉丝多,普通用户粉丝少。
2、对活跃粉丝采⽤推模式,⾮活跃粉丝采⽤拉模式(这种⽅式可以较好的避免⼤流量对平台的冲击)

(5)发帖、读贴流程及缓存架构设计

缓存设计两个箱子:
收件箱(inbox)(redis):存放发送过来的帖⼦id
发件箱(outbox)(redis):存放⽤户⾃⼰发的帖⼦id(相当于是个⼈的timeline)
缓存使⽤Redis的zset进⾏存储,key是接收者uid,value是postid,按照时间排序。(相当于缓存了收件箱)

发送Feed流程:⽤户发帖,发送这个feed我们是需要使⽤MQ的
1、Feed消息先进⼊⼀个队列服务。
2、先从关注列表中读取到⾃⼰的粉丝列表,以及判断⾃⼰是否是⼤V。
3、将⾃⼰的Feed消息写⼊个⼈⻚Timeline(发件箱)。
4、如果是⼤V,此时拉取活跃⽤户;如果是普通⽤户,则拉取⾃⼰的所有粉丝⽤户(活跃和⾮活跃⽤户)。然后将⾃⼰的Feed消息同步写给⾃⼰的粉丝,同步的内容为Feed ID。(大V的活跃粉丝用push,大V的非活跃粉丝用pull,普通用户都用push,注意!自己也是自己的粉丝
5、发布Feed的流程到此结束。
在这里插入图片描述在这里插入图片描述

读取Feed流程:
1、判断⾃⼰是否是活跃⽤户,如果不是,去读取⾃⼰关注的⼤V列表。如果是活跃用户,只需要查看自己收件箱即可
2、读取⾃⼰的收件箱,范围起始位置是上次读取到的最新Feed的ID,结束位置可以使当前时间,也可以是MAX。然后通过查询出来的FeedId反查Feed内容,并且把已经软删除的数据剔除出去。
3、非活跃用户如果有拉取到关注的⼤V列表,则再次并发读取每⼀个⼤V的发件箱,如果关注了10个⼤V,那么则需要10次访问。
4、非活跃用户合并2和3步的结果,然后按时间排序,返回给⽤户。

在这里插入图片描述

关注、取关,其实就是发帖与删帖的流程。

四、附近的人架构设计

陌陌,探探这些社交软件附近的⼈特点:
可以进⾏数据筛选(男⼥,年龄等)
可以进⾏排序
对距离精准度计算要求相对⾼

微博、小红书、抖⾳附近的⼈特点:
实际上查询的是附近的帖⼦
对距离精准度计算要求相对不⾼
不需要进⾏数据筛选(男⼥,年龄等)

1、架构设计

使用mysql+redis的geohash数据结构实现即可。

用户登录、用户发帖记录经纬度值,存入mysql和redis。
用户点击附近的人,通过自身的经纬度以及帖子或者其他人的经纬度,判断距离进行筛选。

数据多的话,用redis按照地区进行分库分表即可。

五、评论服务架构设计

1、什么是评论系统

评论只分为两级,显示总评论数、每个评论的点赞数、评论者、被评论者。

评论系统都包含什么内容:
1.发表评论:⽀持⽆限盖楼回复
2.读取评论:按照时间,热度排序;显示评论数,楼中楼等
3.删除评论:⽤户删除,作者删除,后台管理员删除
4.评论互动:点赞,举报等
5.管理评论:置顶,精选,后台运维管理(搜索,删除,审核等)
6.富⽂本展示:例如表情分享等。
7.评论标签:例如作者点赞,作者回复,好友点赞等
8.评论装扮:⼀般⽐较热的评论会出现好评,神评等装饰
9.热评管理:结合AI和⼈⼯,为⽤户营造更好的评论区氛围。
在这里插入图片描述

2、评论系统基本功能设计

(1)数据库设计与选型

comment评论表:
ID、objId(对象ID,比如帖子、视频、留言等)、
type(类型,帖子、视频、商品)、
memberId(发表者)、
root(根ID,不为0则是回复评论,用于查询回复链)、parent(父评论ID,0为root评论)、
floor(评论楼层)、like(点赞数)、
status(状态,正常/隐藏)、top(置顶)、
message(评论内容)、count(评论总数,展开***条回复,查看子评论数量)、time(时间)

评论业务的数据是海量的,数据量随着业务⽅的增多成倍增⻓,需要具备快速便捷的⽔平扩展和迁移能⼒。
评论业务需要提供快速和稳定的读写能⼒,能够读写分离和⾃动恢复。
⽽评论业务不涉及⽤户资产,对事务的要求性不⾼。因此选⽤ MongoDB 集群 作为最底层的数据存储⽅式。

中小型的系统,用mysql就可以了,如果是大型系统,可以考虑MongoDB有更大的优势。

(2)基本功能实现

此处提供mysql实现方式,mongodb同理。

1、发评论功能
需要校验文本审核、黑名单校验,然后入库。
如果是二级评论,需要修改父评论的子评论总数+1。

-- 2、查询所有评论,需要进行分页查询
select * from comment where objId=1 and parent = 0 order by time desc;

3、高性能评论系统架构设计

(1)基于mysql+redis缓存架构设计

在这里插入图片描述
因为不是使⽤MongoDB存储数据,使⽤MySQL存储数据,所以关联查询是⽐较⽅便的,所以这⾥我们可以创建三张表
计数表:包含id主键,根+⼦评论总数,根评论总数等
索引表:id主键,发表者id,跟评论id,⽗评论id等,和MongoDB其实是⼀样的
内容表:内容的大小是不确定的,考虑单独使用一张表。

缓存设计:
1、计数缓存(点赞数、评论数)
2、评论id列表(index):zset类型,根据时间或热度排序
3、根评论(key:作者id;value:根评论id列表),查看评论列表时,默认只展示根评论列表。
4、⼆级评论(key:根评论id;value:⼆级评论id列表)
5、评论的具体内容 string类型, key:评论id value:评论内容
可以按照时间或者点赞数进⾏排序(根评论根据点赞数进⾏排序,⼦评论是根据时间来进⾏排序)(基于zset类型,分数为时间或者点赞数

本地缓存:
缓存热门帖子的热门评论的前几条点赞数最多的评论和二级评论。(通过热点探测技术)

新增、删除评论:
需要同步删除缓存,或者监听binlog重建缓存。

(2)分库分表或者使用NewSQL

根据objId(对象ID)进行分库分表。

数据量大的话,可以考虑使用MongoDB或者NewSql数据库TiDB、HBase等。

(3)高性能判断是否给评论点赞

在这里插入图片描述

创建⼀张表来记录所有的数据,然后再把这些数据保存在缓存当中,通过读取缓存来判断⽤户是否点赞过。
数据量是非常庞大的。
使用bitmap的话,问题也很多的,稀疏数据也会浪费大量的空间。

可以考虑使用布隆过滤器。(注意,布隆过滤器查不到的话是一定没有,查到的话不一定有,需要进行二次查询。而且布隆过滤器不允许删除,所以需要添加一个取消点赞的缓存)
可以考虑点赞数少的话(<1000),直接用redis的set,如果大于1000,就使用布隆过滤器,顺便初始化。
在这里插入图片描述

六、签到服务架构设计

1、基本功能设计

⽤户签到功能,一般会送代金券、积分等等。

user_integral用户积分表:
ID、userId、当前积分。

user_integral_log用户积分流水表:
ID、userId、type(积分类型 1.签到 2.连续签到 3.补签)、integral(积分)、operation_time(操作时间,签到和补签的具体⽇期)

用Redis的bitmap,⽤来实现签到是恰到好处的,可以对用户按照每天进行bitmap的记录。

http://www.xdnf.cn/news/699661.html

相关文章:

  • window 显示驱动开发-处理 E_INVALIDARG 返回值
  • ArgoDB表类型及常用命令
  • 491. Non-decreasing Subsequences
  • DeepSeek R1 与 V3 的全面对比,两个版本有什么差别?
  • 【Linux】linux上看到的内存和实际内存不一样?
  • Linux云计算训练营笔记day17(Python)
  • Cisco Packer Tracer 组建虚拟局域网(VLAN)
  • 【前端】【Jquery】一篇文章学习Jquery所有知识点
  • keepalived两台设备同时出现VIP问题
  • MySql--explain的用法
  • 【Linux网络篇】:简单的TCP网络程序编写以及相关内容的扩展
  • css样式块重复调用
  • 楼宇自控系统重塑建筑设备管理:告别低效,迈向智能管理时代
  • 华为OD机试真题——书籍叠放(2025A卷:200分)Java/python/JavaScript/C/C++/GO最佳实现
  • Linux系统之cal命令的基本使用
  • 国有企业采购方式及适用情形
  • Java集合进阶
  • C++补充基础小知识:什么是接口类 和 抽象类?为什么要继承?
  • 线程的生命周期?怎么终止线程?线程和线程池有什么区别?如何创建线程池?说一下 ThreadPoolExecutor 的参数含义?
  • yolov12毕设前置知识准备 1
  • Linux基本指令/上
  • Python常用模块实用指南
  • Python人工智能算法学习 禁忌搜索算法求解旅行商问题(TSP)的研究与实现
  • .net Winfrom 如何将窗口设置为MDI容器
  • QGIS新手教程2:线图层与多边形图层基础操作指南(点线互转、中心点提取与WKT导出)
  • Git:现代软件开发的基石——原理、实践与行业智慧·优雅草卓伊凡
  • go实例化结构体的方式
  • 【C/C++】设计模式之工厂模式:从简单到抽象的演进
  • 《接口和抽象类到底怎么选?设计原则与经典误区解析》
  • com.alibaba.fastjson.JSONException: default constructor not found.