Table of Contents

1. Mô hình

1.1. Các mô hình trong MongoDb

1.1.1. Mô hình Standalone

Mô hình Standalone trong MongoDB là cấu hình đơn giản nhất, nơi MongoDB chạy trên một máy chủ duy nhất. Đây là mô hình cơ bản nhất và dễ thiết lập nhất, thích hợp cho môi trường phát triển, thử nghiệm hoặc các ứng dụng nhỏ không yêu cầu độ khả dụng cao hoặc khả năng mở rộng.

Đặc điểm của mô hình Standalone:

  1. Đơn giản và Dễ Thiết Lập: Cài đặt MongoDB trên một máy chủ duy nhất là dễ dàng và nhanh chóng.
  2. Hiệu Suất: Hiệu suất có thể bị giới hạn bởi tài nguyên của một máy chủ duy nhất, không có tính năng phân đoạn (sharding) hay sao lưu (replication) để cải thiện hiệu suất.
  3. Không Có Tính Khả Dụng Cao: Nếu máy chủ gặp sự cố, toàn bộ cơ sở dữ liệu sẽ không khả dụng, không có khả năng tự động chuyển đổi dự phòng.
  4. Không Có Sao Lưu (Replication): Không có bản sao dự phòng của dữ liệu, dữ liệu có nguy cơ bị mất nếu máy chủ gặp sự cố.
  5. Thích Hợp Cho: Môi trường phát triển và thử nghiệm, ứng dụng nhỏ không yêu cầu độ khả dụng cao.

1.1.2. Mô hình Replicate

Mô hình Replica Set trong MongoDB là một cấu hình nâng cao được sử dụng để cung cấp độ khả dụng cao và sao lưu dữ liệu. Trong mô hình này, dữ liệu được sao chép trên nhiều máy chủ (node) để đảm bảo rằng nếu một node gặp sự cố, các node khác vẫn có thể cung cấp dữ liệu và tiếp tục phục vụ các yêu cầu của người dùng.

Đặc điểm của mô hình Replica Set:

  1. Khả Dụng Cao (High Availability): Dữ liệu được sao chép trên nhiều node, giúp đảm bảo rằng hệ thống vẫn hoạt động ngay cả khi một node bị lỗi. Nếu node chính (primary) gặp sự cố, một trong các node phụ (secondary) sẽ tự động được bầu chọn làm node chính mới.
  2. Sao Lưu Dữ Liệu (Data Redundancy): Dữ liệu được sao chép trên nhiều node, giảm nguy cơ mất dữ liệu. Các node phụ có thể được cấu hình để sao lưu và lưu trữ dữ liệu lịch sử.
  3. Cân Bằng Tải (Load Balancing): Các node phụ có thể xử lý các truy vấn chỉ đọc, giúp giảm tải cho node chính. Cải thiện hiệu suất của hệ thống khi có nhiều yêu cầu đọc.
  4. Khả Năng Tự Phục Hồi (Self-Healing): Hệ thống tự động phục hồi khi một node bị lỗi và tự động sao chép dữ liệu khi node mới được thêm vào.

1.1.3. Mô hình Sharding

Mô hình Sharding trong MongoDB là một kỹ thuật được sử dụng để mở rộng cơ sở dữ liệu bằng cách phân phối dữ liệu trên nhiều máy chủ. Mục đích chính của Sharding là xử lý khối lượng dữ liệu lớn và cải thiện hiệu suất của cơ sở dữ liệu bằng cách phân chia dữ liệu thành các phân đoạn nhỏ hơn, gọi là “shard”, và phân phối chúng trên các máy chủ khác nhau.

Đặc điểm của mô hình Sharding:

  1. Mở Rộng Quy Mô (Scalability): Cho phép mở rộng cơ sở dữ liệu một cách tuyến tính bằng cách thêm nhiều máy chủ hơn. Cải thiện khả năng xử lý khối lượng dữ liệu lớn và tăng số lượng truy vấn.
  2. Phân Phối Dữ Liệu (Data Distribution): Dữ liệu được phân chia thành các shard và phân phối trên nhiều máy chủ. Các shard có thể được lưu trữ trên các cụm máy chủ khác nhau.
  3. Cân Bằng Tải (Load Balancing): Cân bằng tải giữa các shard giúp cải thiện hiệu suất tổng thể của hệ thống. Các shard có thể được di chuyển hoặc phân phối lại để đảm bảo cân bằng tải.
  4. Khả Năng Chịu Lỗi (Fault Tolerance): Mỗi shard có thể được cấu hình dưới dạng một replica set để đảm bảo độ khả dụng cao và sao lưu dữ liệu. Nếu một shard gặp sự cố, các replica set khác vẫn có thể tiếp tục phục vụ các yêu cầu của người dùng.

1.2. Kiến trúc MongoDb

1.2.1. Cấu trúc MongoDb

Bảng so sánh mối tương quan với mô hình cơ sở dữ liệu quan hệ:

RDBMS Database Table Column Row
MongoDb Database Collection Field Document

1.2.2. Kiến trúc hoạt động MongoDb

Storage Engine: Trong MongoDb có nhiều Storage Engine khác nha, mỗi loại lại có giải thuật xử lý riêng - có ưu nhược điểm khác nhau. Chịu trách nhiệm lưu trữ và xử lý dữ liệu trên Memory và các file vật lý trên OS.

Các loại Storage Engine:

  1. MMAPv1
  2. WiredTiger (mặc định)
  3. In-Memory (sử dụng với Mongo Enterprise)

https://www.mongodb.com/docs/manual/core/storage-engines/

2. Cài đặt & sử dụng

2.1. Cài đặt MongoDB PHP Extension

https://www.php.net/manual/en/mongodb.installation.pecl.php

yes '' | sudo pecl install mongodb
 
...
Build process completed successfully
Installing '/opt/homebrew/Cellar/php@8.2/8.2.20/pecl/20220829/mongodb.so'
install ok: channel://pecl.php.net/mongodb-1.19.3
Extension mongodb enabled in php.ini

2.2. Sử dụng với PHP Laravel

config/database.php config:

<?php
return [
 
    'connections' => [
        'mongodb' => [
            'driver' => 'mongodb',
            'host' => env('MONGODB_HOST', 'localhost'),
            'port' => env('MONGODB_PORT', 27017),
            'database' => env('MONGODB_DATABASE'),
            'username' => env('MONGODB_USERNAME'),
            'password' => env('MONGODB_PASSWORD'),
            'options' => array(
                'database' => env('MONGODB_DATABASE')
            )
        ],
 
    ],

2.2.1. Sử dụng thư viện mongodb/mongodb

<?php
 
namespace App\Console\Commands;
 
use Illuminate\Console\Command;
use MongoDB\Client;
 
class MongoDbTestCommand extends Command
{
    /**
     * The name and signature of the console command.
     * https://www.mongodb.com/docs/php-library/current/tutorial/install-php-library/
     * @var string
     */
    protected $signature = 'app:mongo-db-test';
 
    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description';
 
    /**
     * Execute the console command.
     */
    public function handle()
    {
        $host = config('database.connections.mongodb.host');
        $port = config('database.connections.mongodb.port');
        $dbName = config('database.connections.mongodb.database');
 
        $conn = new Client("mongodb://{$host}:{$port}");
 
        $db = $conn->listDatabases();
 
        //$db = $conn->$dbName;
        $db = $conn->selectDatabase($dbName);
 
        //$collection = $conn->test->test;
        $collection = $db->selectCollection('test');
 
        //$this->insertCollections($collection);
        //$this->updateCollection($collection);
 
        //var_dump($this->getCollections($db));
 
        $this->find($collection);
 
        //$this->deleteOne($collection);
        //$this->deleteMany($collection);
 
    }
 
    /**
     * findOne
     *
     * @param  mixed $collection
     * @return void
     */
    private function findOne($collection){
        $result = $collection->findOne(['category' => 'Bar']);
 
        var_dump($result->title, $result->content, $result->category);             
    }    
    /**
     * find
     *
     * @param  mixed $collection
     * @return void
     */
    private function find($collection){
        $result = $collection->find([
            'category' => 'Bar',
            'title' => ['$regex' => 'Foo', '$options'=> 'i']
        ], [
            'limit' => 2,
            'skip' => 0
        ]);
 
        //foreach ($result as $row) {
        //    var_dump($row->title, $row->category); 
        //}
 
        var_dump($result->toArray());   
    }
 
    /**
     * deleteOne
     *
     * @param  mixed $collection
     * @return void
     */
    private function deleteOne($collection){
        $result = $collection->deleteOne(['category' => 'Foo']);
 
        var_dump($result->getDeletedCount());             
    }      
    /**
     * deleteMany
     *
     * @param  mixed $collection
     * @return void
     */
    private function deleteMany($collection){
        $result = $collection->deleteMany(
            [
                'title' => [
                    '$regex' => 'Foo', '$options' => 'i'
                ]
            ]);
 
        var_dump($result->getDeletedCount());             
    }  
 
    /**
     * getDatabases
     *
     * @param  mixed $conn
     * @return void
     */
    private function getDatabases($conn){
        $db = $conn->listDatabases();
 
        foreach ($db as $databaseInfo) {
            var_dump($databaseInfo->getName());
        }
    }    
    /**
     * dropDatabase
     *
     * @param  mixed $conn
     * @param  mixed $database
     * @return void
     */
    private function dropDatabase($conn, $database){
        return $conn->dropDatabase($database);
    }
 
    /**
     * getCollections
     *
     * @param  mixed $db
     * @return void
     */
    private function getCollections($db){
        $result = $db->listCollections();
 
        foreach ($result as $item) {
            var_dump($item->getName());
        }
    }    
 
    /**
     * dropCollection
     *
     * @param  mixed $db
     * @param  mixed $collection
     * @return void
     */
    private function dropCollection($db, $collection){
        $db->dropCollection($collection);
    }
 
    /**
     * insertCollections
     *
     * @param  mixed $collection
     * @return void
     */
    private function insertCollections($collection){
        $insertResult = $collection->insertOne([
            'title'    => 'Second Post',
            'content'  => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Odio harum necessitatibus eius magnam, doloribus explicabo omnis natus alias, nisi dolorum ea ab. Nobis quo magnam aliquid molestias, ipsum nihil eum.',
            'category' => 'Foo',
        ]);
 
        var_dump($insertResult);        
 
        $insertResult = $collection->insertMany([
            [
                'title'    => 'Foo Post',
                'content'  => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Odio harum necessitatibus eius magnam, doloribus explicabo omnis natus alias, nisi dolorum ea ab. Nobis quo magnam aliquid molestias, ipsum nihil eum.',
                'category' => 'Foo',
            ],
            [
                'title'    => 'Bar Post',
                'content'  => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Odio harum necessitatibus eius magnam, doloribus explicabo omnis natus alias, nisi dolorum ea ab. Nobis quo magnam aliquid molestias, ipsum nihil eum.',
                'category' => 'Bar'],
        ]);
 
        var_dump($insertResult);
    }     
 
    /**
     * updateCollection
     *
     * @param  mixed $collection
     * @return void
     */
    private function updateCollection($collection){
        $updatedResult = $collection->updateOne(
            ['_id' => new \MongoDB\BSON\ObjectId('66ad03ca578cc2020505ad62')],
            ['$set' => [
                    'title'    => 'News Title',
                    'content'  => 'News Content',
                    'category' => 'Foo New',
                ],
            ]
        );
 
        var_dump($updatedResult);     
 
        $updatedResult = $collection->updateMany(
            ['category' => 'Foo'],
            ['$set' => [
                    'category' => 'Bar',
                ],
            ]
        );
 
        var_dump($updatedResult);        
    }
 
 
}

2.2.2. Sử dụng thư viện mongodb/laravel-mongodb

Cài đặt thư viện mongodb/laravel-mongodb:

composer require mongodb/laravel-mongodb:^4.7

2.3. Mongo Shell

tungnt@MacBook-Pro-cua-Nguyen-2 2 % /Users/tungnt/Downloads/mongosh-2.2.15-darwin-arm64/bin/mongosh

2.3.1. Kiểm tra storage engine đang sử dụng

test> db.serverStatus().storageEngine
{
  name: 'wiredTiger',
  supportsCommittedReads: true,
  oldestRequiredTimestampForCrashRecovery: Timestamp({ t: 0, i: 0 }),
  supportsPendingDrops: true,
  dropPendingIdents: Long('0'),
  supportsSnapshotReadConcern: true,
  readOnly: false,
  persistent: true,
  backupCursorOpen: false
}

2.3.2. Một số lệnh cơ bản

test> show databases;
admin   132.00 KiB
config  108.00 KiB
local    88.00 KiB
test    244.00 KiB
 
test> use tungnt
switched to db tungnt
tungnt> db.mycollection.insertOne({name: "TungNT", age: 35, city: "Hanoi"})
{
  acknowledged: true,
  insertedId: ObjectId('66b0b08801bb92333841de0f')
}
 
tungnt> show collections;
mycollection
 
tungnt> db.mycollection.find()
[
  {
    _id: ObjectId('66b0b08801bb92333841de0f'),
    name: 'TungNT',
    age: 35,
    city: 'Hanoi'
  }
]
 
tungnt> db.mycollection.insertMany([{name: "TungNT", age: 35, city: "Hanoi"}, {name: "VienLD", phone: "+84999888777"}])
{
  acknowledged: true,
  insertedIds: {
    '0': ObjectId('66b0b1e801bb92333841de10'),
    '1': ObjectId('66b0b1e801bb92333841de11')
  }
}
 
tungnt> db.mycollection.getIndexes()
[ { v: 2, key: { _id: 1 }, name: '_id_' } ]
 
tungnt> db.runCommand({collstats: "mycollection"})
{
  ns: 'tungnt.mycollection',
  size: 191,
  count: 3,
  avgObjSize: 63,
  numOrphanDocs: 0,
  storageSize: 36864,
  freeStorageSize: 16384,
  capped: false,
...
 
tungnt> db.mycollection.createIndex({name: 1}, {name: "IDX_NAME"})
IDX_NAME
tungnt> db.mycollection.getIndexes()
[
  { v: 2, key: { _id: 1 }, name: '_id_' },
  { v: 2, key: { name: 1 }, name: 'IDX_NAME' }
]
 
tungnt> db.mycollection.find({name: "TungNT"}).explain("executionStats")
{
  explainVersion: '2',
  queryPlanner: {
    namespace: 'tungnt.mycollection',
    indexFilterSet: false,
    parsedQuery: { name: { '$eq': 'TungNT' } },
    queryHash: '1AD872C6',
    planCacheKey: 'B92F173A',
    maxIndexedOrSolutionsReached: false,
    maxIndexedAndSolutionsReached: false,
    maxScansToExplodeReached: false,