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

PHP 操作 MongoDB 完整實戰指南:從入門到進階

     編輯:PHP 綜合知識

前言

MongoDB 以 JSON-like 文檔模型、動態 Schema、水平擴展能力,成為現代應用(日誌、IoT、內容管理、推薦系統)的首選 NoSQL。PHP 官方驅動 mongodb (Composer 包 mongodb/mongodb) 提供了優雅的 ODM 風格 API。本文涵蓋環境建置、CRUD、索引、聚合、事務、最佳實務。

環境建置

# 1. 安裝 PHP 擴展 (需先裝 libmongoc, libbson)
pecl install mongodb

# 2. 啟用擴展
echo "extension=mongodb.so" > /etc/php.d/50-mongodb.ini

# 3. 安裝官方庫 (提供高階 API)
composer require mongodb/mongodb

# 4. 驗證
php -r "echo extension_loaded('mongodb') ? 'OK' : 'FAIL';"

連線與基本配置

<?php
require 'vendor/autoload.php';

$client = new MongoDB\Client(
    'mongodb://user:pass@host1:27017,host2:27017/?replicaSet=rs0&authSource=admin',
    [],
    [
        'typeMap' => [
            'root'     => 'array',        // 結果預設回傳陣列而非物件
            'document' => 'array',
            'array'    => 'array',
        ],
        'driverOptions' => [
            'connectTimeoutMS' => 5000,
            'socketTimeoutMS'  => 10000,
        ],
    ]
);

$db = $client->selectDatabase('myapp');
$coll = $db->selectCollection('users');

// 測試連線
try {
    $client->listDatabases();
    echo "MongoDB 連線成功\n";
} catch (MongoDB\Driver\Exception\ConnectionTimeoutException $e) {
    die("連線失敗:" . $e->getMessage());
}
?>

核心 CRUD 操作

1. 插入

<?php
// 單筆
$insertOneResult = $coll->insertOne([
    'username' => 'alice',
    'email'    => 'alice@example.com',
    'roles'    => ['user'],
    'profile'  => ['age' => 28, 'city' => 'Taipei'],
    'created_at' => new MongoDB\BSON\UTCDateTime(), // 當前毫秒時間戳
    'status'   => 1,
]);
echo "Inserted ID: {$insertOneResult->getInsertedId()}\n";

// 批次 (無序寫入,效能最佳)
$insertManyResult = $coll->insertMany([
    ['username' => 'bob', 'email' => 'bob@example.com', 'roles' => ['user'], 'created_at' => new MongoDB\BSON\UTCDateTime()],
    ['username' => 'charlie', 'email' => 'charlie@example.com', 'roles' => ['admin'], 'created_at' => new MongoDB\BSON\UTCDateTime()],
]);
echo "Inserted IDs: " . implode(', ', array_map(fn($id) => (string)$id, $insertManyResult->getInsertedIds())) . "\n";

2. 查詢

<?php
// 單筆
$user = $coll->findOne(['username' => 'alice']);

// 多筆 + 投影 + 排序 + 分頁
$cursor = $coll->find(
    ['status' => 1, 'profile.age' => ['$gte' => 18]], // 過濾條件
    [
        'projection' => ['username' => 1, 'email' => 1, 'profile.city' => 1], // 只取需要欄位
        'sort'       => ['created_at' => -1], // -1 降序
        'skip'       => 0,
        'limit'      => 20,
    ]
);

foreach ($cursor as $doc) {
    echo "{$doc['username']} - {$doc['email']} - {$doc['profile']['city']}\n";
}

// 計數
$count = $coll->countDocuments(['status' => 1]);
echo "Active users: $count\n";

// 判斷是否存在
$exists = $coll->countDocuments(['email' => 'test@test.com'], ['limit' => 1]) > 0;

3. 更新

<?php
// 單筆更新 (原子操作符)
$updateResult = $coll->updateOne(
    ['username' => 'alice'],
    [
        '$set' => ['profile.age' => 29, 'last_login' => new MongoDB\BSON\UTCDateTime()],
        '$inc' => ['login_count' => 1],          // 計數器遞增
        '$push' => ['tags' => 'vip'],            // 陣列追加
        '$addToSet' => ['roles' => 'premium'],   // 唯一陣列追加
    ],
    ['upsert' => false] // 不存在不插入
);
echo "Matched: {$updateResult->getMatchedCount()}, Modified: {$updateResult->getModifiedCount()}\n";

// 批次更新
$coll->updateMany(
    ['roles' => 'user'],
    ['$set' => ['status' => 2]] // 批量停用
);

// 替換整份文檔 (保留 _id)
$coll->replaceOne(['_id' => $id], $newDocument);

4. 刪除

<?php
$coll->deleteOne(['username' => 'spam_user']);
$coll->deleteMany(['status' => 0, 'created_at' => ['$lt' => new MongoDB\BSON\UTCDateTime(strtotime('-1 year'))]]);

進階功能

1. 索引管理

<?php
// 單欄索引
$coll->createIndex(['username' => 1], ['unique' => true, 'name' => 'uniq_username']);

// 複合索引 (查詢順序要匹配)
$coll->createIndex(['status' => 1, 'profile.city' => 1, 'created_at' => -1], ['name' => 'idx_status_city_time']);

// 文字索引 (全文搜尋)
$coll->createIndex(['username' => 'text', 'email' => 'text'], ['name' => 'txt_search']);

// TTL 索引 (自動過期,如 Session、驗證碼)
$coll->createIndex(['expire_at' => 1], ['expireAfterSeconds' => 0, 'name' => 'ttl_expire']);

// 查看索引
foreach ($coll->listIndexes() as $index) {
    echo $index->getName() . ": " . json_encode($index->getKey()) . "\n";
}

2. 聚合管道

<?php
// 統計各城市用戶數,取前 10
$pipeline = [
    ['$match' => ['status' => 1]],
    ['$group' => [
        '_id' => '$profile.city',
        'count' => ['$sum' => 1],
        'avg_age' => ['$avg' => '$profile.age'],
    ]],
    ['$sort' => ['count' => -1]],
    ['$limit' => 10],
    ['$project' => ['_id' => 0, 'city' => '$_id', 'count' => 1, 'avg_age' => 1]],
];

$cursor = $coll->aggregate($pipeline);
foreach ($cursor as $doc) {
    echo "{$doc['city']}: {$doc['count']} users, avg age {$doc['avg_age']}\n";
}

3. 多文檔事務 (MongoDB 4.0+ Replica Set / 4.2+ Sharded Cluster)

<?php
$session = $client->startSession();
$session->startTransaction();

try {
    $coll->insertOne(['username' => 'tx_user', 'balance' => 100], ['session' => $session]);
    $coll->updateOne(['username' => 'merchant'], ['$inc' => ['balance' => -100]], ['session' => $session]);
    // 模擬業務邏輯
    if (rand(0, 1)) throw new Exception('模擬失敗');
    
    $session->commitTransaction();
    echo "事務提交成功\n";
} catch (Exception $e) {
    $session->abortTransaction();
    echo "事務回滾:" . $e->getMessage() . "\n";
} finally {
    $session->endSession();
}
?>

4. GridFS (大檔案儲存)

<?php
$bucket = $db->selectGridFSBucket();

// 上傳
$stream = fopen('/path/to/large_video.mp4', 'rb');
$fileId = $bucket->uploadFromStream('large_video.mp4', $stream, [
    'metadata' => ['uploader' => 'alice', 'category' => 'video'],
]);
fclose($stream);

// 下載
$bucket->downloadToStream($fileId, fopen('/path/to/save.mp4', 'wb'));

// 刪除
$bucket->delete($fileId);

資料建模最佳實務

模式適用場景範例
內嵌文檔一對少、親子強關聯、常一起讀取用戶 profile、訂單 items (少量)
引用一對多、多對多、獨立生命週期、需單獨查詢用戶 _id → 文章 author_id
混合熱點資料內嵌、冷資料引用文章內嵌前 3 則留言,其餘引用 comments 集合
Bucket 模式時間序列、IoT 感測器、日誌每文檔存 1 小時資料點陣列

效能優化清單

  • 投影:['projection' => ['_id' => 0, 'username' => 1]] 只傳需要欄位,減少網路/記憶體。
  • 索引覆蓋查詢:查詢欄位全在索引中,explain() 顯示 "totalKeysExamined" > 0, "totalDocsExamined" == 0。
  • 批次操作:insertMany/bulkWrite 替代迴圈單筆。
  • 連線池:單例 MongoDB\Client,PHP-FPM 請求間複用 (需啟用持久連線或用 Swoole/RoadRunner)。
  • 讀寫關注:強一致 writeConcern: {w: 'majority', j: true}、高可用讀 readPreference: 'secondaryPreferred'。
  • 監控:db.currentOp()、db.serverStatus()、mongostat、Prometheus Exporter。

常見坑與避雷指南

坑點後果正確做法
用陣列索引當 _id分片失效、分布不均用 ObjectId 或 Snowflake/UUID
大文檔無限增長16MB 限制、效能劣化Bucket 模式、拆集合
未建立索引就上線全表掃描、CPU 飆高上線前 explain() 驗證
查詢不加 limit記憶體爆炸、游標超時永遠加 limit、游標分批
直接存 DateTime 物件序列化問題、時區混亂用 MongoDB\BSON\UTCDateTime (毫秒 UTC)
事務用在高併發寫入鎖競爭、效能崩潰事務僅用於強一致必要場景

總結

PHP + MongoDB 組合極適合快速迭代、Schema 多變、海量非結構化資料場景。掌握「文檔建模、索引策略、聚合管道、事務邊界、連線池管理」五大核心,即能駕馭生產環境。記住:**MongoDB 不是關係型資料庫的替代品,而是互補工具**——關係型管核心交易、強一致;MongoDB 管彈性資料、高吞吐、水平擴展。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved