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

PHP MongoDB 擴充模組實戰陷阱:64 位元整數查詢與 MapReduce 超時處理

     編輯:PHP 綜合知識

核心問題:13 位元組時間戳查詢失效

MongoDB 儲存 13 位元組毫秒級時間戳(如 1347349162159)超出 32 位元整數範圍(最大 2147483647),必須以 64 位元整數 NumberLong 儲存。

錯誤查詢方式

// ❌ 字串查詢(資料為 NumberLong 時無匹配)
$query = ['log.stamp' => ['$gte' => '1347346800000', '$lt' => '1347350400000']];

// ❌ 純 PHP 整數(32 位元系統溢位,64 位元系統雖正確但非最佳實踐)
$query = ['log.stamp' => ['$gte' => 1347346800000, '$lt' => 1347350400000]];

正確解法:MongoInt64 類別

// ✅ 明確指定 64 位元整數型別
$start = 1347346800000;
$end   = 1347350400000;

$query = [
    'log.stamp' => [
        '$gte' => new MongoInt64($start),
        '$lt'  => new MongoInt64($end),
    ]
];

$cursor = $collection->find($query);

關鍵點: PHP MongoDB 驅動預設將數值視為 32 位元或浮點數,跨平台行為不一。MongoInt64 確保序列化為 BSON Int64 型別,與資料庫 NumberLong 精確匹配。

型別對照表

PHP 類別BSON 型別適用場景
MongoInt32Int3232 位元整數
MongoInt64Int6464 位元整數(時間戳、大數字 ID)
MongoIdObjectId_id 主鍵
MongoDateDateTime日期時間(UTC 毫秒)
MongoRegexRegex正則查詢
MongoCodeCodeJavaScript 代碼(MapReduce、$where)

MapReduce 長時間執行超時處理

大數據量 MapReduce 易觸發游標超時(預設 10 分鐘),需在 command() 額外參數指定 timeout。

完整範例

$map = new MongoCode('
    function() {
        var prop_id = this.log.cstparam.proId;
        var key = this.log.site + prop_id;
        emit(key, {channel: this.log.site, prop_id: prop_id, count: 1});
    }
');

$reduce = new MongoCode('
    function(key, emits) {
        var total = 0;
        for (var i in emits) total += emits[i].count;
        return {channel: emits[0].channel, prop_id: emits[0].prop_id, count: total};
    }
');

$query = [
    'log.stamp' => [
        '$gte' => new MongoInt64($timeRange['start']),
        '$lt'  => new MongoInt64($timeRange['end']),
    ]
];

// 關鍵:第二個參數傳入 timeout(毫秒)
$command = [
    'mapreduce' => $collectionName,
    'map'       => $map,
    'reduce'    => $reduce,
    'out'       => $tmpResultCollection,
    'query'     => $query,
];

// timeout=0 表示無限等待,或設定具體毫秒數如 3600000 (1小時)
$options = ['timeout' => self::MONGO_CURSOR_TIMEOUT]; // 定義常數 MONGO_CURSOR_TIMEOUT = 0

$db->command($command, $options);

新版驅動對應

PHP MongoDB 擴充模組已歷經三代:

  1. ext-mongo (舊版,已停維護) → 使用 MongoInt64、MongoCode
  2. ext-mongodb (新版核心驅動) + mongodb/mongodb (官方函式庫) → 使用 MongoDB\BSON\Int64、MongoDB\BSON\Javascript

新版寫法對照

use MongoDB\BSON\Int64;
use MongoDB\BSON\Javascript;

$query = [
    'log.stamp' => [
        '$gte' => new Int64($start),
        '$lt'  => new Int64($end),
    ]
];

$map = new Javascript('function() { ... }');
$reduce = new Javascript('function(key, emits) { ... }');

$manager->executeCommand($dbName, new \MongoDB\Driver\Command([
    'mapreduce' => $collName,
    'map' => $map,
    'reduce' => $reduce,
    'out' => $outColl,
    'query' => $query,
    'maxTimeMS' => 3600000, // 新版用 maxTimeMS 替代 timeout
]));

最佳實踐總結

  1. 所有超過 2147483647 的數值 必須用 MongoInt64/Int64 封裝
  2. 時間戳優先用 MongoDate:new MongoDate($timestamp/1000) 自動轉 UTC 毫秒,支援原生日期查詢運算子
  3. MapReduce 設定 maxTimeMS/timeout:防止長時間運算被伺服器殺掉
  4. 索引優化:log.stamp 欄位必須建立索引 db.coll.createIndex({'log.stamp': 1})
  5. 驅動升級:舊專案規劃遷移至 mongodb/mongodb 函式庫,相容 PHP 7/8、支援現代 BSON 型別
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved