首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP MongoDB 驅動整數 Bug 與 MapReduce 替代方案

     編輯:PHP 相關知識

問題背景

MongoDB 有 32 位 (Int32) 與 64 位 (Int64) 整數。舊版 PHP 驅動 (≤1.2) 無視作業系統位元,全部視為 Int32,導致 64 位整數截斷。新版驅動引入 mongo.native_long 選項:

ini_set('mongo.native_long', true); // 64 位系統啟用 Int64

Group 操作 Bug (驅動 1.3+ 仍存在)

<?php
ini_set('mongo.native_long', true);
$m = new MongoClient();
$c = $m->test->test;

// 測試資料
for ($i=0; $i<10; $i++) {
    $c->insert(['group_id'=>rand(1,5), 'count'=>rand(1,5)]);
}

// Group 聚合
$keys = ['group_id'=>1];
$initial = ['count'=>0];
$reduce = 'function(obj, prev) { prev.count += obj.count; }';

$result = $c->group($keys, $initial, $reduce);
// 結果:count 變成 [object Object] 而非數字!
var_dump($result);

兩種暫時修正 (治標不治本)

  1. ini_set('mongo.native_long', false); 強制用 32 位/浮點數
  2. $initial = ['count' => 0.0]; 使用浮點數初始值

根本解決:MapReduce 替代 Group

<?php
ini_set('mongo.native_long', true);
$m = new MongoClient();
$db = $m->test;

$map = 'function() { emit(this.group_id, this.count); }';
$reduce = 'function(key, values) { 
    var sum = 0; 
    for (var i in values) sum += values[i]; 
    return sum; 
}';

$cmd = [
    'mapreduce' => 'test',
    'map' => $map,
    'reduce' => $reduce,
    'out' => ['inline' => 1] // 直接回傳結果
];
$result = $db->command($cmd);

foreach ($result['results'] as $r) {
    echo "group_id: {$r['_id']}, total: {$r['value']}\n";
}

Group vs MapReduce 對照 (MySQL 視角)

SQLMongoDB GroupMongoDB MapReduce
SELECT g, SUM(c) FROM t GROUP BY ggroup({key:{g:1}}, {c:0}, reduce)map: emit(g,c); reduce: sum(values)
HAVING SUM(c) > 100不支援finalize: function(k,v){ if(v>100)return v; }
效能單執行緒、鎖表可分片平行、支援增量
Bug 狀態Group 有整數 Bug,MapReduce 正常

現代驅動 (mongodb/mongodb 1.10+ / PHP 8+)

  • 使用 MongoDB\Driver\Manager + MongoDB\Collection::aggregate()
  • 聚合管線 (Aggregation Pipeline) 取代 MapReduce,效能更好、語法更清晰
  • 整數處理正確支援 MongoDB\BSON\Int64
$pipeline = [
    ['\$group' => ['_id' => '\$group_id', 'total' => ['\$sum' => '\$count']]]
];
$cursor = $collection->aggregate($pipeline);
foreach ($cursor as $doc) { var_dump($doc); }

遷移建議

  1. 升級 composer require mongodb/mongodb
  2. 將 group() 改寫為 aggregate()
  3. 移除 mongo.native_long 設定
  4. 測試 64 位整數正確性
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved