You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP MongoDB客户端批量更新50万+文档时出现缓冲区溢出错误

PHP MongoDB批量更新触发缓冲区溢出错误的解决方案

环境信息

  • PHP 8.2.6
  • Debian GNU/Linux 11 (bullseye)
  • MongoDB PHP扩展版本:1.15.0+1.11.1+1.9.2+1.7.5

错误信息

/build/php-mongodb-1.15.0+1.11.1+1.9.2+1.7.5/build-8.2/src/libmongoc/src/libmongoc/src/mongoc/mongoc-buffer.c:139 _mongoc_buffer_append(): precondition failed: (buffer->datalen + data_size) < INT_MAX

问题场景

批量更新50万条MongoDB文档时进程终止并抛出上述错误,限制单次更新数据集在20万条以内可正常运行。尝试按100万条为周期提交批量写入,但问题未解决。

原批量更新代码

$manager = new \MongoDB\Driver\Manager('mongodb://localhost:27017');           
$bulk = new \MongoDB\Driver\BulkWrite(["ordered" => false]);

while (is_object($richiesta = $richieste->fetch_object())) {
    // ... 处理$dati和$aXML的逻辑
    $bulk->update(
        ['_id' => $dati['id_mov_contab']],
        $aXML,
        ['multi' => false, 'upsert' => true]
    );
}

if ($bulk->count() > 0) {
    $manager->executeBulkWrite('catasto.catasto', $bulk);
}

未解决问题的修改代码

$manager = new \MongoDB\Driver\Manager('mongodb://localhost:27017');           
$bulk = new \MongoDB\Driver\BulkWrite(["ordered" => false]);

$c=0;
while (is_object($richiesta = $richieste->fetch_object())) {
    // ... 处理$dati和$aXML的逻辑
    $bulk->update(
        ['_id' => $dati['id_mov_contab']],
        $aXML,
        ['multi' => false, 'upsert' => true]
    );
    if($c%1000000 ==0 && $bulk->count()>0 ) {
        $manager->executeBulkWrite('catasto.catasto', $bulk);
        $manager = new \MongoDB\Driver\Manager('mongodb://localhost:27017');           
        $bulk = new \MongoDB\Driver\BulkWrite(["ordered" => false]);
    }
    $c++;
}

if($bulk->count()>0) {
    $manager->executeBulkWrite('catasto.catasto', $bulk);
}

解决方案

1. 降低批量提交阈值

之前设置的100万条远超过安全阈值(20万条),导致批量操作的总数据量超过INT_MAX(约2GB)的缓冲区限制。将批量大小调整为20万以内,比如10万条,确保每次提交的总数据量不触发缓冲区溢出。

2. 优化批量操作逻辑

无需重复创建Manager对象(复用即可),仅在每次提交后重置BulkWrite对象即可,减少不必要的资源开销。

3. 检查单条更新数据大小

如果$aXML包含较大内容(比如大段XML数据),单条更新的BSON体积会增加,此时需要进一步降低批量大小(比如5万条),确保批量总数据量不接近2GB限制。

修正后的代码示例

$manager = new \MongoDB\Driver\Manager('mongodb://localhost:27017');           
$bulk = new \MongoDB\Driver\BulkWrite(["ordered" => false]);

$batchSize = 100000; // 设置为10万条一批
$count = 0;

while (is_object($richiesta = $richieste->fetch_object())) {
    // ... 处理$dati和$aXML的逻辑
    $bulk->update(
        ['_id' => $dati['id_mov_contab']],
        $aXML,
        ['multi' => false, 'upsert' => true]
    );
    
    $count++;
    // 达到批量大小则提交并重置
    if ($count % $batchSize === 0) {
        $manager->executeBulkWrite('catasto.catasto', $bulk);
        $bulk = new \MongoDB\Driver\BulkWrite(["ordered" => false]);
    }
}

// 处理剩余未提交的少量数据
if ($bulk->count() > 0) {
    $manager->executeBulkWrite('catasto.catasto', $bulk);
}

4. 可选:分批查询源数据

如果源数据($richieste)是从数据库查询的结果,可以改为分批查询(比如每次取10万条),避免一次性加载大量数据到内存,进一步降低整体内存占用。

内容的提问来源于stack exchange,提问作者caiofior

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 18:54:53