用afterWrite钩子自动更新热度值——它在数据插入或更新成功后触发,确保主键已生成、事务已提交,避免并发或回滚导致统计错乱;beforeWrite和afterRead均不适用。

模型字段自动更新热度值用什么钩子
ThinkPHP 没有内置“标签热度”概念,得自己在模型写逻辑。最稳的方式是用 afterWrite 钩子——它在数据插入或更新成功后触发,能确保主键已生成、事务已提交,避免因并发或回滚导致统计错乱。
-
beforeWrite不行:此时数据还没落库,万一失败,热度就白加了 -
afterRead更不行:每次查都加一次,直接把热度刷爆 - 如果只统计“被搜索/被点击”类行为,建议单独建日志表+异步聚合,别在核心模型里硬塞
怎么安全地给标签字段加热度计数
别直接 $model->save(['hot' => $model->hot + 1]) —— 这会覆盖其他字段,且在并发下大概率出错。要用数据库自增语句,靠 DB 层原子性保安全。
- 推荐用
Db::name('tag')->where('id', $id)->inc('hot')->update() - 如果必须走模型,用
$model->where('id', $id)->setInc('hot')(注意不是save()) - 字段类型必须是
INT或BIGINT,别用DECIMAL或字符串存数字 - 初始值设为
0,别留NULL,否则setInc在某些版本会报错
热度排序时 order('hot desc') 为什么不准
因为默认没加索引,数据一多就慢,而且 MySQL 的 ORDER BY 在无索引时可能返回非确定顺序,尤其配合 LIMIT 时容易漏掉同热度的记录。
- 立刻加索引:
ALTER TABLE `tag` ADD INDEX idx_hot (hot); - 如果热度变化频繁,考虑把
hot和id组合索引:ADD INDEX idx_hot_id (hot, id),避免回表 - 别用
order('hot desc')->limit(10)直接取“最热10个”——同热度标签可能被随机截断,应加二级排序,比如order('hot desc, id desc')
缓存热度值要不要加时间戳校验
要。ThinkPHP 的 cache() 默认不带过期逻辑,直接缓存 hot 字段值会导致前端看到的永远是旧数据。
立即学习“PHP免费学习笔记(深入)”;
- 读热度时用
cache('tag_hot_'.$id, null, 3600),显式设 1 小时过期 - 更新热度后,务必清对应缓存:
cache('tag_hot_'.$id, null) - 别缓存整个模型对象——
hot变化快,其他字段不变,缓存粒度太粗反而拖慢更新效率 - Redis 作缓存时,注意
setInc后没同步回缓存,所以读缓存、写 DB、删缓存三步不能少
热度统计本身不难,难的是在高并发下不出错、不拖慢主流程、不和缓存对不上。每一步都要想清楚:这行代码是在 DB 里执行,还是 PHP 里算,还是缓存里读——混在一起就容易漏掉原子性或一致性。


















