怎么用mysql实现点赞收藏功能_mysql社交项目设计

来源:这里教程网 时间:2026-02-28 20:49:38 作者:

点赞和收藏功能在 MySQL 中不是靠单表搞定的,核心在于「用户行为」与「内容实体」之间的多对多关系建模,且必须区分

like
favorite
两种独立行为。

用什么表结构存点赞和收藏

不能把

is_liked
is_favored
直接加到文章/视频/帖子表里——这会导致更新锁竞争、无法查“谁点过赞”、不支持取消再点、也不利于统计和分页拉取用户行为列表。

建一张
user_likes
表:
user_id
(主键之一)、
target_type
(如
'post'
'comment'
)、
target_id
(被点赞的记录 ID)、
created_at
;联合唯一索引为
(user_id, target_type, target_id)
同理建
user_favorites
表,字段结构一致;复用同一套逻辑但物理分离,方便后期各自加字段(比如收藏可加
folder_id
,点赞不需要)
target_type
字段用字符串而非外键,避免跨表约束和迁移成本;查询时用
WHERE target_type = 'post' AND target_id = 123
即可

怎么防止重复点赞或重复收藏

靠数据库层唯一约束最可靠,应用层判断只是优化体验,不能替代它。

插入前不做
SELECT
判断(存在竞态:A 查无记录 → B 插入 → A 再插入,重复)
直接执行
INSERT INTO user_likes (user_id, target_type, target_id) VALUES (101, 'post', 2001)
,配合
ON DUPLICATE KEY UPDATE created_at = NOW()
(如果已存在就刷新时间,适合“重新点赞”语义)
或者用
INSERT IGNORE
忽略重复错误(适合纯开关型操作);MySQL 返回影响行数:1 表示新增,0 表示已存在
注意:唯一索引必须包含
user_id + target_type + target_id
全部三列,漏掉任一列都会失效

怎么高效查某条内容被赞了多少次

不要每次查都

COUNT(*) FROM user_likes WHERE target_type='post' AND target_id=123
—— 高频读场景下会拖慢主库,尤其当行为表上亿行时。

在内容表(如
posts
)里加冗余字段:
like_count
favorite_count
,类型用
INT UNSIGNED
点赞/取消点赞时,用原子操作更新:
UPDATE posts SET like_count = like_count + 1 WHERE id = 123
(+1 或 -1)
该字段只用于展示总数;真实行为明细仍以
user_likes
表为准(比如做防刷、审计、通知等)
如果业务要求强一致性(如秒杀式点赞排行榜),可用 Redis 的
INCR
/
DECR
做前置计数,再异步落库,但多数社交场景用数据库原子更新足够

取消点赞或取消收藏怎么写才安全

删除操作比插入更容易出错,关键是「删对了人、删对了目标、且知道删没删成」。

DELETE FROM user_likes WHERE user_id = 101 AND target_type = 'post' AND target_id = 123
,不要只用
target_id
检查
ROW_COUNT()
返回值:0 表示本来就没点过,1 表示成功取消;这是判断“当前状态”的唯一依据
不要先
SELECT
DELETE
—— 同样有竞态;也不要依赖返回的“影响行数是否为 0”来报错,而应视作正常流程分支
如果要同时更新计数,建议用事务包裹:
START TRANSACTION;
DELETE FROM user_likes WHERE user_id = 101 AND target_type = 'post' AND target_id = 123;
UPDATE posts SET like_count = like_count - 1 WHERE id = 123;
COMMIT;
,并确保
posts.id
有主键索引,否则
UPDATE
可能锁表

最容易被忽略的是

target_type
的枚举管理——它散落在 SQL、代码、文档里,没人维护就会出现
'Post'
'post'
'POST'
混用,导致查询永远为空;建议在应用层定义常量,在建表注释里写明合法值,并在 INSERT 前做简单校验。

相关推荐