news 2026/5/5 8:39:35

DB::table(‘posts‘)->where(‘id‘, $postId)->increment(‘likes‘, $count);的庖丁解牛

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
DB::table(‘posts‘)->where(‘id‘, $postId)->increment(‘likes‘, $count);的庖丁解牛

它的本质是:一条看似简单的“自增”语句,在底层被转化为UPDATE posts SET likes = likes + ? WHERE id = ?。虽然它在 SQL 层面是原子的(不会读到脏数据),但在高并发场景下,它会导致严重的行锁等待 (Row Lock Wait)索引页争用 (Index Page Contention)。对于热门帖子,这行代码就是导致数据库 CPU 飙升、响应延迟激增的元凶**。

如果把数据库比作银行柜台

  • increment():是排队改账本
    • 流程:顾客 A 走到柜台 -> 柜员锁定账本第 10 页(加行锁)-> 读取当前余额 100 -> 计算 100+1=101 -> 写入 101 -> 解锁。
    • 并发问题:顾客 B、C、D… 同时来改第 10 页。他们必须串行排队。A 没办完,B 只能干等(Lock Wait)。
    • 后果:队伍越来越长,柜台处理速度越来越慢,最后大堂经理(连接池)崩溃。
  • RedisINCR:是电子计数器
    • 流程:顾客按一下按钮,数字自动+1。无需排队,微秒级完成。
    • 优势:完全异步,无锁竞争。
    • 核心逻辑别让所有人都去抢同一本账本。把记账工作交给高速缓存,账本只在最后对一次总账。

一、SQL 本质:它到底做了什么?

1. 生成的 SQL

Laravel 的increment方法最终生成:

UPDATEpostsSETlikes=likes+1WHEREid=123;-- 如果 $count > 1UPDATEpostsSETlikes=likes+5WHEREid=123;
2. 原子性保证 (Atomicity)
  • 正确性:在 InnoDB 引擎中,这条语句是原子的。
    • 它不是SELECT likes->PHP计算->UPDATE
    • 它是直接在存储引擎层完成读取+计算+写入
    • 结论:数据不会错(不会少加),这是它唯一的优点。
3. 锁机制 (Locking)
  • 行锁 (Row Lock):InnoDB 会对id=123这一行加X锁 (Exclusive Lock)
  • 持续时间:直到事务提交。
  • 影响:其他任何试图修改或锁定该行的事务(包括另一个increment)都必须等待。

💡 核心洞察increment保证了数据的“正确性”,但牺牲了系统的“并发性”。在低并发下没问题,在高并发下是灾难。


二、并发危害:为什么它是热点杀手?

1. 行锁等待 (Lock Wait)
  • 场景:爆款文章,每秒 1000 人点赞。
  • 现象
    • Thread 1 获得锁,执行 UPDATE (耗时 1ms)。
    • Thread 2-1000 进入Lock Wait Queue
    • Thread 2 等待 1ms,Thread 1000 等待 1000ms (1秒)。
  • 后果:接口响应时间线性增长,用户感觉“卡死”。
2. 上下文切换开销 (Context Switch Overhead)
  • 机制:MySQL 线程不断在“运行”和“等待锁”之间切换。
  • 后果:CPU 大量时间花在调度线程上,而非执行 SQL 上。sys态 CPU 使用率飙升。
3. 索引页争用 (Index Page Latch Contention)
  • 机制id是主键,聚簇索引。频繁更新同一行,会导致该索引页在 Buffer Pool 中被频繁读写。
  • 后果:即使没有行锁等待,内存层面的Latch (闩锁)竞争也会限制吞吐量。
4. Binlog 压力
  • 机制:每次UPDATE都会生成 Binlog 日志。
  • 后果:高频小事务导致 Binlog 文件迅速膨胀,主从同步延迟增加。

三、性能优化:如果必须用 DB,怎么救?

如果你不能引入 Redis,必须在 MySQL 层面优化:

1. 批量合并 (Batching)
  • 策略:不要在每个请求中都调用increment
  • 实现
    • 在 PHP 内存中累计计数。
    • 每隔 1 秒或每满 100 次,执行一次DB::...->increment('likes', 100)
  • 效果:将 100 次行锁竞争合并为 1 次。
  • 风险:服务重启会丢失未刷新的计数。
2. 减少事务范围
  • 策略:确保increment在一个极短的事务中执行,尽快提交。
  • 代码
    DB::transaction(function()use($postId){DB::table('posts')->where('id',$postId)->increment('likes');// 不要在这里做其他耗时操作!});
3. 乐观锁重试 (Optimistic Locking Retry) -不推荐用于计数
  • 说明:乐观锁适合状态变更,不适合高频计数,因为冲突率太高,重试会导致更严重的 CPU 浪费。

四、替代方案:架构级解法

方案 A:Redis INCR + 异步落库 (最佳实践)
  • 流程
    1. Redis::incr("post:{$id}:likes")。原子操作,无锁,微秒级。
    2. :直接读 Redis 获取点赞数。
    3. 同步
      • 定时任务:每分钟将 Redis 计数同步到 MySQL。
      • 消息队列:每次INCR发送 MQ,消费者批量更新 MySQL。
  • 优势:彻底解除数据库行锁瓶颈,支撑万级 QPS。
  • 一致性:最终一致性。用户看到的可能比实际多/少几秒,但可接受。
方案 B:MySQL 延迟更新 (Write-Behind)
  • 流程
    1. PHP 接收请求,将(post_id, user_id)放入本地内存数组或 APCu。
    2. 当数组达到阈值(如 50 个),一次性执行UPDATE ... SET likes = likes + 50
  • 优势:减少 DB 交互次数。
  • 劣势:单机部署有效,集群部署复杂。
方案 C:分表/分库 (Sharding)
  • 流程:将点赞记录分散到多个表中。
  • 劣势:架构复杂度极高,对于单纯的计数场景,杀鸡用牛刀。

🚀 总结:原子化“DB Increment”全景图

维度关键点
本质基于行锁的原子更新,高并发下的性能瓶颈
SQL 行为UPDATE table SET col = col + 1 WHERE id = ?
主要危害行锁等待、上下文切换、Binlog 膨胀
适用场景低频更新、非热点数据、强一致性要求极高
禁忌场景爆款文章点赞、秒杀库存扣减、高频计数器
最佳替代Redis INCR + 异步持久化
PHP 隐喻Mutex Lock on Database Row
公式Throughput = 1 / (Lock_Wait_Time + Execution_Time)

终极心法

DB Increment 的本质,是“用串行化换取正确性”。
在低并发时,它是安全的捷径;在高并发时,它是致命的堵塞点。
别让数据库承担它不该承担的计数压力。
于原子中见安全,于锁中见瓶颈;以架构为尺,解单点之牛,于高并发工程中,求吞吐之真。

行动指令

  1. 审查代码:找出项目中所有的increment调用。
  2. 评估频率:哪些是热点数据(如文章点赞、视频播放量)?
  3. 重构热点:将热点计数迁移到 RedisINCR
  4. 保留冷点:低频数据(如文章评论数、后台统计)可以保留 DBincrement,简化架构。
  5. 思维升级:记住,数据库擅长存数据和复杂查询,但不擅长高频简单计数。把计数交给 Redis,把存储交给 MySQL。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/5 8:33:26

provision-core:构建声明式自动化工作流的底层框架

1. 项目概述:一个被低估的自动化基石如果你在团队协作、项目交付或者基础设施管理领域摸爬滚打过几年,大概率会对“重复劳动”这四个字深恶痛绝。今天要聊的这个项目,provision-org/provision-core,乍一看名字平平无奇&#xff0c…

作者头像 李华
网站建设 2026/5/5 8:33:26

避坑指南:鸿蒙HarmonyOS List列表开发中,关于分割线、滚动索引和性能的那些“坑”

鸿蒙List组件深度避坑:分割线、滚动索引与性能优化的实战解析 第一次在鸿蒙应用里实现通讯录滑动索引功能时,我盯着那个错位3个像素的分割线调试到凌晨两点——这大概就是HarmonyOS开发者共同的成长仪式。本文将分享那些官方文档没细说、但实际开发中一定…

作者头像 李华
网站建设 2026/5/5 8:31:30

Python 爬虫数据处理:爬取数据定时备份与恢复机制

前言 在规模化 Python 爬虫项目长期运行过程中,数据丢失、数据损坏、数据库异常、服务器宕机、误操作删除等问题频发,直接导致爬虫采集成果损毁,严重影响业务连续性与数据完整性。爬虫数据具备持续增量、来源分散、采集周期长、不可重复完整爬取等特性,单纯依赖数据库原生…

作者头像 李华
网站建设 2026/5/5 8:30:31

对话机器人工程化实践:从架构设计到生产部署的完整指南

1. 项目概述与核心价值 最近在开源社区里,一个名为 moltbot-best-practices 的项目引起了我的注意。这个项目托管在 NextFrontierBuilds 组织下,名字直译过来是“MoltBot最佳实践”。乍一看,你可能会觉得这又是一个围绕某个特定聊天机器人…

作者头像 李华
网站建设 2026/5/5 8:30:27

基于nRF52840的无线智能水阀设计与应用

1. 项目概述:基于nRF52840的无线智能水阀设计 在智能家居领域,水系统管理一直是个被低估的痛点。传统机械阀门需要手动操作,而市面上多数"智能阀门"要么需要复杂布线,要么缺乏真正的无线自由度。Uhome Systems团队推出的…

作者头像 李华