increment()最安全,直接数据库层原子执行views = views + 1,避免并发覆盖;需传字段名,支持步长、条件where及多字段更新,但不触发模型事件、不刷新内存值,返回受影响行数。

直接用 increment() 和 decrement(),别先查再改再存——否则并发时会丢数据。
为什么不能用 $model->field++ 再 save()
这是最常踩的坑:PHP 层读出旧值、加 1、写回,中间可能被其他请求覆盖。比如两个并发请求同时读到 views = 100,各自加 1 后都写回 101,实际应为 102。
而 increment('views') 是一条原子 SQL:UPDATE table SET views = views + 1 WHERE ...,由数据库保证执行不中断。
- 返回值是受影响行数(
int),不是模型实例,别链式调用->fresh() - 不触发
updating/updated事件,需手动补日志或缓存更新逻辑 - 内存中模型属性不会自动更新,
$post->views仍是旧值,要最新值得自己refresh()或重查
increment() 的参数和条件组合怎么写
基础用法是 increment('field'),默认加 1;但真实场景往往需要控制步长、附带更新、加 where 条件。
- 加 5:
$post->increment('likes', 5) - 加 1 并更新时间戳:
$post->increment('views', 1, ['updated_at' => now()]) - 只对特定用户 IP 加:
Post::where('id', $id)->where('ip', request()->ip())->increment('views') - 防刷兜底(没匹配到条件时返回 0):检查返回值
if (Post::where(...)->increment('views') === 0) { // 未生效,可能是重复访问 }
PostgreSQL 下 increment() 报错 Null value not allowed 怎么办
Laravel 10+ 对 PostgreSQL 更严格:字段为 NULL 时不再隐式转成 0,而是直接报错。MySQL/SQLite 还兼容,但 PG 不行。
- 建表时确保字段有默认值:
$table->integer('views')->default(0) - 已有表可先初始化空值:
DB::statement("UPDATE posts SET views = 0 WHERE views IS NULL") - 查询时双保险:
whereNotNull('views')->orWhere('views', 0),避免条件漏掉 NULL 行 - 字段加索引(尤其用于
where条件的字段),否则高并发下全表扫描拖垮性能
用原生 DB::raw('field + 1') 替代可以吗
语法上能跑,但没必要,还容易翻车:
- SQLite 下可能把数字当字符串拼接:
'9' + 1变成'91' - 不校验 unsigned 溢出,而
increment()在 PDO 层会抛异常 - 不同数据库驱动(MySQL/SQL Server/PG)的自增写法不同,
increment()自动适配,DB::raw()得自己写死 - 调试时看 query log,
increment()生成的 SQL 更直观,一眼看出意图
真正复杂的地方不在「怎么写」,而在「什么时候不该用」——比如要联动更新缓存、记录操作人、或依赖模型事件做后续动作,就得绕开 increment() 改用事务 + 手动 update,或者补全事件逻辑。别默认以为加个数字只是数据库一行的事。


















