高并发写入应改用redis队列削峰:tp接口仅lpush入队,独立进程rpop消费并批量db::insertall();需设超时、落库失败日志、业务主键幂等、禁用自增id判重。

高并发写入时直接操作数据库必然扛不住
ThinkPHP 默认的 Db::insert() 或模型 save() 在秒级数百次写入时,会迅速暴露连接池耗尽、主键冲突、死锁或慢查询堆积等问题。这不是 TP 框架的问题,而是 MySQL 单点写入吞吐有硬上限。强行加索引、拆表、读写分离都治标不治本——真正要动的是「写入节奏」。
用 Redis List + 多进程消费者替代直写
不引入 RocketMQ/Kafka 这类重型中间件,也能快速落地削峰。核心思路:TP 接口只做入队,由独立 PHP 进程消费并批量写库。
-
Redis::lPush('write_queue', json_encode($data))替代Db::insert(),毫秒级完成 - 用
supervisor启一个常驻进程(如php think queue:consume),循环执行Redis::rPop('write_queue') - 消费端累积 50 条或每 200ms 触发一次
Db::insertAll(),大幅降低 SQL 执行频次 - 注意:
rPop是阻塞式,需设超时防止空转;消费失败数据要落failed_queue表人工干预
TP 自带的 think-queue 扩展踩坑点
官方扩展封装了队列抽象,但默认配置在高并发下极易翻车:
- 驱动选
redis而非database,后者用 MySQL 存任务本身就成了新瓶颈 -
maxTries设为 1,避免重复投递导致数据重复;重试逻辑应由业务层兜底(比如先查再写) - 不要在
handle()里调sleep()或长耗时 IO,会卡死整个 worker 进程 - 日志必须开 ——
log_level => 'error',否则失败任务静默丢失,连排查入口都没有
消息体设计必须包含幂等字段
网络抖动或消费者重启会导致同一条消息被多次消费。TP 层不能依赖「消息只发一次」,而要在数据层拦截:
- 业务主键(如订单号、用户 ID+时间戳组合)作为
UNIQUE索引,冲突时INSERT IGNORE或捕获SQLSTATE[23000]错误 - 写入前先
SELECT id FROM table WHERE biz_id = ?,存在则跳过(适合低频更新场景) - 禁止用自增 ID 做幂等依据,它无法跨实例保证唯一性
队列不是银弹,它把瞬时压力转移到了消费端缓冲区和数据库批量写能力上。真正压测时,要盯着 Redis 内存增长曲线和消费延迟监控 —— 消费跟不上,队列就从削峰变成积压。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











