惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
News and Events Feed by Topic
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
月光博客
月光博客
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
大猫的无限游戏
大猫的无限游戏
T
Tailwind CSS Blog
S
SegmentFault 最新的问题
V
V2EX
阮一峰的网络日志
阮一峰的网络日志
C
Cisco Blogs
博客园 - 叶小钗
P
Privacy International News Feed
Jina AI
Jina AI
Apple Machine Learning Research
Apple Machine Learning Research
T
Threatpost
IT之家
IT之家
博客园 - 聂微东
Know Your Adversary
Know Your Adversary
Help Net Security
Help Net Security
罗磊的独立博客
I
Intezer
S
Schneier on Security
博客园_首页
C
CERT Recently Published Vulnerability Notes
雷峰网
雷峰网
Cisco Talos Blog
Cisco Talos Blog
宝玉的分享
宝玉的分享
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Webroot Blog
Webroot Blog
TaoSecurity Blog
TaoSecurity Blog
MyScale Blog
MyScale Blog
P
Privacy & Cybersecurity Law Blog
T
The Exploit Database - CXSecurity.com
PCI Perspectives
PCI Perspectives
Security Latest
Security Latest
H
Heimdal Security Blog
S
Secure Thoughts
Hacker News: Ask HN
Hacker News: Ask HN
Y
Y Combinator Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Microsoft Security Blog
Microsoft Security Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
SecWiki News
SecWiki News
The GitHub Blog
The GitHub Blog
A
Arctic Wolf
A
About on SuperTechFans
aimingoo的专栏
aimingoo的专栏
T
Threat Research - Cisco Blogs
Engineering at Meta
Engineering at Meta
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC

博客园 - koushr

hls k8s常用命令 k8s 1.18.0安装 mysql json函数 group by的列、where的列的有效性 go基础第六篇:sync包 doris脚本 es es语法 veo ride nacos 枫叶互动 极光矩阵 富途 移卡科技 nginx高可用 k8s第一篇:k8s集群架构组件 架构设计第二篇:支付模块架构设计 可观测性体系设计第一篇:可观测性体系基本概念 ES高级第一篇:倒排索引 山海星辰 python第一篇:基础语法 pulsar基础第二篇:命令行 pulsar基础第一篇:pulsar安装及基本概念 clickhouse第三篇:安装 clickhouse第二篇:MergeTree引擎 clickhouse第一篇:引擎 redis高阶第一篇:令牌桶算法限流 http2.0 gin入参多次获取 docker第二篇:docker安装常用中间件
架构设计第一篇:点赞模块功能设计
koushr · 2026-03-19 · via 博客园 - koushr

内容类app的点赞功能设计

点赞功能即用户对内容点赞,点赞后用户可以看到该内容最新的点赞数。用户可以查询自己的点赞列表,可以查看内容的点赞列表。

level1:用户对内容点赞时,往数据库中插入一条记录。取消点赞时,删除此条记录。

表结构是:id、user_id(用户id)、content_id(内容id)、create_time、update_time

为了防止重复点赞,需要在user_id和content_id上加唯一索引。

上面方案读写全部都走数据库,在并发大的情况下,对数据库会造成很大压力,响应时间也会很慢。

level2:引入redis,把高并发的读写放到redis中。

创建两种类型的key,一种是set类型的键值对,存放点赞事件,key形如content:likes:{user_id},member是content_id。点赞时,就sadd,取消点赞时,就srem。另一种是string类型的键值对,存放内容的点赞量,key形如content:likesnum:{content_id},value是该内容对应的点赞量。点赞时,就incr 1,取消点赞时,就incr -1。

举个例子,如果u1对c1点赞,则先sadd content:likes:u1 c1,如果返回0,则表示重复点赞,直接返回给前端即可。如果返回1,则再incr content:likesnum:c1,给点赞数加1。最后把点赞事件通过消息队列异步存到数据库中,注意要幂等,即insert ignore。这样,在拉取内容列表时,查用户对内容是否点了赞,只需执行sismember,查内容的点赞数,只需执行get。用户查询自己的点赞列表时,就有问题了,set没有存点赞时间,无法按照点赞时间倒序取。所以,要把set类型改为sorted set类型,key形如content:likes:zset:{user_id},member是content_id,score是点赞时间对应的时间戳,这样就会自动按照点赞时间排列。用户查询自己的点赞列表,只需zrangebyscore。用户查看内容的点赞列表,用当前两种key都不行,所以需要再创建一个sorted set类型的键值对,key形如content:likes:zset:{content_id},member是user_id,score是点赞时间对应的时间戳。这样,当执行一次点赞操作,需要操作三个key,那么最好是用lua脚本原子式执行多命令。

level3:高并发下,点赞事件一条条写到数据库,会对数据库造成压力。此外,还必须要考虑热点数据。

一条条写数据库,要改成批量写数据库。需要在应用层积攒消息,即一直poll,直到积攒到一定量或是积攒一定时间,再批量入库,再commitSync。注意,如果是一个消费者消费多个分区,则需要用那个有参数且参数是各分区偏移量的commitSync()方法,以保证每个分区都能commit正确。

热点数据,如一个内容被几十万个用户点赞,或者一个用户点赞了几十万个内容,那么存储点赞事件的那两个key就会变成大key,占用太多内存。我们可以只保留最近1万条记录,之前的记录清掉。这样的话,查询点赞列表时,如果取到1万条之后,就从数据库中取。基本上不会有人会翻到1w条之后。

还有一种热点数据,即一个内容短时间内收到了大量的赞,导致短时间内频繁操作存储点赞量的这个key。如果redis是集群,那么我们可以把这个key分片,即拆分成content:likesnum:{content_id}_0,content:likesnum:{content_id}_1。。。content:likesnum:{content_id}_n,让这些key分到不同的redis节点上,这样就可以提升吞吐率了。

点赞系统的设计核心是:

1. 使用 Redis 承担高并发读写:
- ZSet 存用户/内容点赞关系(支持分页)
- String 存点赞计数

2. 使用 Lua 保证多 key 操作原子性

3. 通过 MQ 异步落库,并采用批量写提升吞吐

4. 针对热点问题:
- 大 key:只保留最近数据 + DB 兜底
- 热点计数:采用分片计数(sharding counter)

5. 分片计数结合 Redis Cluster,实现多节点并行写入,
突破单节点吞吐瓶颈

6. 通过幂等设计(唯一索引)保证数据一致性