点赞数据应存入独立的 user_likes 表,含 user_id、post_id、created_at 字段,并设 (user_id, post_id) 联合唯一索引,以支持行为追溯、去重、状态判断及高并发安全。

点赞数据该存到哪张表里
直接加到 posts 表里加个 like_count 字段?不行。用户点一次赞,你得知道是谁点的、什么时候点的、能不能取消——这些都要求独立记录行为,不是只记总数。
正确做法是建一张独立的点赞关系表:user_likes,至少包含 user_id、post_id、created_at 三个字段,联合唯一索引设在 (user_id, post_id) 上。否则同一个用户反复点,会写多条重复记录,查总数或判断是否已点都会出错。
- 别用
INT类型存“是否已点”,布尔值无法回溯行为,也撑不住高并发写入 - 如果业务需要“点踩”或“收藏”,别硬塞进同一张表加类型字段,优先考虑拆成
user_favorites、user_dislikes等独立表,语义清晰、扩展不撕裂 - MySQL 5.7+ 建议用
TIMESTAMP而非DATETIME,自动处理时区,避免前端传时间戳时漏掉时区转换
ThinkPHP 中怎么判断用户是否已点赞
不能每次渲染都查一遍 user_likes 表再 PHP 判断——页面有 20 条动态,就得发 20 次 SQL。得用关联查询一次性捞出来。
假设模型是 Post 和 UserLike,在 Post 模型里定义一个关联:
public function userLike()
{
return $this->hasOne(UserLike::class)
->where('user_id', session('user_id'));
}
然后查列表时用 with('userLike') 预载入,模板里直接用 $post->userLike !== null 判断即可。
- 注意 session key 名必须和你登录后存的实际 key 一致,比如用
think-auth插件可能是auth_user_id,别硬写user_id - 如果用户未登录,
session('user_id')为null,where条件会变成WHERE user_id = NULL,查不到结果但也不会报错——这是预期行为,但得在模板里补一层空值判断 - 别在循环里调
$post->getUserLikeAttr()这类获取器,会 N+1 查询,性能断崖式下跌
点赞接口怎么防重复提交和并发冲突
前端按钮点完立刻禁用,只是体验层防护。后端没锁,两个几乎同时的请求仍可能都写进数据库,导致重复点赞或取消失效。
ThinkPHP 本身不带分布式锁,但可以用数据库唯一索引兜底 + 事务捕获异常:
try {
Db::startTrans();
UserLike::create([
'user_id' => $uid,
'post_id' => $pid,
]);
Db::commit();
return ['code' => 0, 'msg' => '点赞成功'];
} catch (\Exception $e) {
Db::rollback();
if (strpos($e->getMessage(), 'Duplicate entry') !== false) {
return ['code' => 1, 'msg' => '已点过赞'];
}
return ['code' => -1, 'msg' => '操作失败'];
}
- 必须确保
(user_id, post_id)是数据库唯一索引,否则 catch 不住重复写入 - 别用
findOrSave()或先查后 insert,中间存在竞争窗口,高并发下必出问题 - Redis 锁(如
SETNX)可以加,但得注意锁过期时间要大于事务执行时间,且解锁必须用 Lua 脚本保证原子性——对简单点赞场景,索引+事务更轻量、更可靠
点赞数实时更新但页面不刷新怎么做
用 AJAX 提交点赞后,只改 DOM 里的数字,不重载整个页面。但要注意:这个数字不能只靠 JS 自增/自减,得以接口返回的真实值为准。
后端接口返回结构里必须带最新点赞数,比如:
{ "code": 0, "data": { "like_count": 142, "is_liked": true } }
前端拿到后,用这个 like_count 覆盖显示,而不是 current + 1。否则网络延迟、重复点击、别人也在点,都会让本地计数失真。
- 别把
like_count存在 HTML 的data-属性里再读取计算,DOM 和服务端状态不同步是常态 - 如果列表页用了分页或懒加载,新加载的项也要走同样逻辑,不能漏掉初始化
is_liked状态 - 移动端双击屏幕有时会触发两次 click,建议用
touchend替代click绑定,或加 300ms 防抖,但防抖不能替代服务端幂等校验
实际最难的不是写代码,是想清楚“点赞”到底代表什么动作——是仅计数?还是社交信号?是否要通知作者?要不要进消息中心?这些决定了表结构、索引策略、缓存粒度,甚至要不要上消息队列。一开始没想透,后面加功能全是补丁。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










