根本原因是Model::upsert()在Hyperf中仍走模型生命周期、未合并SQL且默认查库判断冲突;应改用Db::insert()配合ON DUPLICATE KEY UPDATE原生语句,确保唯一索引、字段一致、分片控制,并手动包裹事务。
Hyperf模型批量更新慢,根本原因不是数据库本身,而是
或
这类方法在内部仍走完整模型生命周期——每条数据都实例化、校验、触发事件、拼SQL、再单条执行。哪怕你传入1000条,它默认仍是N次查询或N次独立UPDATE。要真正提速,必须绕过模型层,直连
配合
语句。
为什么Model::upsert()在Hyperf里还是慢
看似是批量接口,但在Hyperf 3.x中它底层仍会尝试先
查是否存在(尤其没指定
时),再决定INSERT或UPDATE;更关键的是,它不合并成一条SQL,而是拆成多条单独语句执行。实测万级数据下,耗时可能比手写原生语句高5–8倍。
它会为每条数据创建
实例,触发
、
、
等开销
字段映射依赖
和
,动态处理进一步拖慢速度
若没显式传
参数,Hyperf会尝试读取主键+唯一索引,额外查information_schema
并发环境下,多个协程共用同一连接池但未复用PDO预编译句柄,导致prepare重复开销
怎么用Db::insert() + ON DUPLICATE KEY UPDATE写批量更新
核心是把更新逻辑“反向”写成「插入语句 + 冲突时更新」,MySQL原生支持,一条SQL搞定多行。前提是表必须有
或
索引(比如
或
)。
Hyperf 3.1.66
本页面提供企业级 PHP 协程框架 Hyperf 3.1.66 版本的官方源码下载与完整更新日志。重点解析 v3.1.66 版本中新增的 gRPC 多客户端负载均衡支持、Pool 连接池全量刷新、Guzzle 持久化 Cookie 以及数据库 JSON 包含键查询等核心优化特性。
下载
构造二维数组,确保每项键名与数据库字段完全一致:
,不能是
而字段叫
调用
不行——它不支持
;必须用
并手动拼SQL
正确写法是:
,其中
是
展平后的索引数组
注意
引用的是当前这一行的输入值,不是原表值;如需保留原值(比如只更新
不碰
),就写
容易踩的三个坑
这三个问题不解决,哪怕语句写对了,照样报错或静默失败。
:常见于
里混用关联数组和索引数组,或字段顺序不统一。必须保证所有子数组键名一致、顺序一致,且不含空值或null字段(除非对应列允许NULL)
“更新没生效”,实际是唯一索引没建对:检查是否真在目标字段(如
)上建了
或
;如果用
做冲突判断,但表只有
主键,那
永远不触发
超长报错
:单次最多塞1000行左右。分片用
,别硬刚2000+;同时确认MySQL配置
≥ 64M
最常被忽略的一点:Hyperf里
不自动开启事务。如果你的批量更新需要原子性,必须手动
包裹,否则某一批失败,前面成功的不会回滚。另外,
函数在MySQL 5.7+才完全稳定,线上用8.0+更稳妥。
Model::update()Model::upsert()Db::insert()ON DUPLICATE KEY UPDATEModel::upsert()SELECT$uniqueByModelbootingsetAttributecasts$fillable$casts$uniqueByPRIMARY KEYUNIQUEiduser_id['id' => 1, 'sort' => 7]['user_id' => 1]idDb::table('your_table')->insert($batch)ON DUPLICATE KEY UPDATEDb::insert()Db::insert("INSERT INTO your_table (id, sort, updated_at) VALUES ? ON DUPLICATE KEY UPDATE sort = VALUES(sort), updated_at = VALUES(updated_at)", [$values])$valuesarray_merge(...$batch)VALUES(sort)sortupdated_atupdated_at = updated_atSQLSTATE[HY093]: Invalid parameter number$batchidUNIQUEPRIMARY KEYuser_ididON DUPLICATE KEY UPDATEPackets larger than max_allowed_packet are not allowedarray_chunk($data, 500)max_allowed_packetDb::insert()Db::transaction()VALUES()