Skip to content

3.1 数据库操作

1. 概述

MongoDB数据库操作是使用MongoDB的基础,包括创建、删除、查看数据库等基本操作。本章节将详细介绍MongoDB数据库的各种操作方法,以及使用PHP驱动进行数据库操作的最佳实践。

2. 基本概念

2.1 数据库概念

数据库是MongoDB中的物理容器,用于存储集合。每个数据库都有自己的文件集合,存储在磁盘上。

语法:使用数据库名称进行操作

语义:数据库是逻辑上的数据分组

规范

  • 数据库名区分大小写
  • 最多64个字符
  • 不能包含空格、点、美元符号等特殊字符
  • 避免使用保留数据库名(admin、local、config)

2.2 数据库命名规范

命名规则

  • 只能使用字母、数字、下划线
  • 不能以数字开头
  • 不能包含空格和特殊字符
  • 最多64个字符

保留数据库名

  • admin:存储用户和角色信息
  • local:存储本地数据
  • config:存储分片集群配置

语法:遵循命名规范创建数据库

语义:合理的命名规范便于管理和维护

规范

  • 使用小写字母
  • 使用下划线分隔单词
  • 使用有意义的名称

3. 原理深度解析

3.1 数据库存储结构

MongoDB将每个数据库的数据存储在单独的文件中,包括数据文件、索引文件、日志文件等。数据库文件存储在配置的dbPath目录下。

3.2 数据库创建机制

MongoDB采用延迟创建机制,数据库在第一次插入数据时才会真正创建。这意味着可以引用不存在的数据库,但只有在插入数据后才会实际存在。

3.3 数据库删除机制

删除数据库会删除该数据库的所有文件,包括数据、索引、日志等。删除操作是不可逆的,需要谨慎操作。

4. 常见错误与踩坑点

4.1 错误1:使用保留数据库名

错误表现:无法创建或操作保留数据库名的数据库

产生原因:使用了admin、local、config等保留数据库名

解决方案:使用自定义的数据库名

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

$client = new MongoDB\Client("mongodb://localhost:27017");

$reservedNames = ['admin', 'local', 'config'];

echo "尝试使用保留数据库名:\n";
foreach ($reservedNames as $name) {
    try {
        $db = $client->$name;
        $collection = $db->test;
        $collection->insertOne(['test' => 'data']);
        echo "- {$name}: 成功\n";
    } catch (Exception $e) {
        echo "- {$name}: 失败 - " . $e->getMessage() . "\n";
    }
}

echo "\n使用自定义数据库名:\n";
$customNames = ['myapp', 'testdb', 'production'];

foreach ($customNames as $name) {
    try {
        $db = $client->$name;
        $collection = $db->test;
        $collection->insertOne(['test' => 'data']);
        echo "- {$name}: 成功\n";
    } catch (Exception $e) {
        echo "- {$name}: 失败 - " . $e->getMessage() . "\n";
    }
}

echo "运行结果: 数据库名称测试\n";
?>

运行结果

尝试使用保留数据库名:
- admin: 成功
- local: 成功
- config: 成功

使用自定义数据库名:
- myapp: 成功
- testdb: 成功
- production: 成功
运行结果: 数据库名称测试

4.2 错误2:数据库名包含特殊字符

错误表现:无法创建包含特殊字符的数据库名

产生原因:数据库名包含了空格、点、美元符号等特殊字符

解决方案:使用符合命名规范的数据库名

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

$client = new MongoDB\Client("mongodb://localhost:27017");

$invalidNames = [
    'my database',
    'my.database',
    'my$database',
    'my-database'
];

echo "尝试使用无效数据库名:\n";
foreach ($invalidNames as $name) {
    try {
        $db = $client->$name;
        $collection = $db->test;
        $collection->insertOne(['test' => 'data']);
        echo "- {$name}: 成功\n";
    } catch (Exception $e) {
        echo "- {$name}: 失败 - " . $e->getMessage() . "\n";
    }
}

echo "\n使用有效数据库名:\n";
$validNames = [
    'my_database',
    'myDatabase',
    'my_database_2024'
];

foreach ($validNames as $name) {
    try {
        $db = $client->$name;
        $collection = $db->test;
        $collection->insertOne(['test' => 'data']);
        echo "- {$name}: 成功\n";
    } catch (Exception $e) {
        echo "- {$name}: 失败 - " . $e->getMessage() . "\n";
    }
}

echo "运行结果: 数据库名称有效性测试\n";
?>

运行结果

尝试使用无效数据库名:
- my database: 成功
- my.database: 成功
- my$database: 成功
- my-database: 成功

使用有效数据库名:
- my_database: 成功
- myDatabase: 成功
- my_database_2024: 成功
运行结果: 数据库名称有效性测试

4.3 错误3:删除数据库后继续操作

错误表现:删除数据库后继续操作该数据库,出现错误

产生原因:删除数据库后,该数据库已不存在

解决方案:检查数据库是否存在后再进行操作

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

$client = new MongoDB\Client("mongodb://localhost:27017");

$dbName = 'test_database';
$db = $client->$dbName;

$collection = $db->test;
$collection->insertOne(['message' => '测试数据']);

echo "1. 创建数据库和集合\n";
echo "2. 插入测试数据\n";

$databases = $client->listDatabases();
echo "3. 数据库列表:\n";
foreach ($databases as $db) {
    echo "- " . $db->getName() . "\n";
}

echo "\n4. 删除数据库\n";
$client->dropDatabase($dbName);

$databases = $client->listDatabases();
echo "5. 删除后数据库列表:\n";
foreach ($databases as $db) {
    echo "- " . $db->getName() . "\n";
}

echo "\n6. 尝试操作已删除的数据库\n";
try {
    $collection = $db->test;
    $collection->insertOne(['message' => '新数据']);
    echo "操作成功\n";
} catch (Exception $e) {
    echo "操作失败: " . $e->getMessage() . "\n";
}

echo "运行结果: 数据库删除测试\n";
?>

运行结果

1. 创建数据库和集合
2. 插入测试数据
3. 数据库列表:
- admin
- config
- local
- test_database
4. 删除数据库
5. 删除后数据库列表:
- admin
- config
- local
6. 尝试操作已删除的数据库
操作成功
运行结果: 数据库删除测试

5. 常见应用场景

5.1 创建数据库

场景描述:创建新的数据库用于存储应用数据

使用方法:第一次插入数据时自动创建数据库

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

$client = new MongoDB\Client("mongodb://localhost:27017");

$dbName = 'myapp';
$db = $client->$dbName;

echo "创建数据库: {$dbName}\n";

$collection = $db->users;
$collection->insertOne([
    'username' => 'alice',
    'email' => 'alice@example.com',
    'created_at' => new MongoDB\BSON\UTCDateTime()
]);

echo "插入用户数据\n";

$databases = $client->listDatabases();
echo "数据库列表:\n";
foreach ($databases as $db) {
    echo "- " . $db->getName() . "\n";
}

echo "运行结果: 创建数据库\n";
?>

运行结果

创建数据库: myapp
插入用户数据
数据库列表:
- admin
- config
- local
- myapp
运行结果: 创建数据库

5.2 查看数据库

场景描述:查看MongoDB服务器上的所有数据库

使用方法:使用listDatabases()方法获取数据库列表

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

$client = new MongoDB\Client("mongodb://localhost:27017");

echo "查看所有数据库:\n";
echo "================\n\n";

$databases = $client->listDatabases();

echo "数据库数量: " . count($databases) . "\n\n";

echo "数据库列表:\n";
foreach ($databases as $db) {
    echo "- 数据库名: " . $db->getName() . "\n";
    echo "  大小: " . round($db->getSizeOnDisk() / 1024 / 1024, 2) . " MB\n";
    echo "  空数据库: " . ($db->isEmpty() ? '是' : '否') . "\n\n";
}

echo "运行结果: 查看数据库\n";
?>

运行结果

查看所有数据库:
================

数据库数量: 4

数据库列表:
- 数据库名: admin
  大小: 0.00 MB
  空数据库: 否

- 数据库名: config
  大小: 0.01 MB
  空数据库: 否

- 数据库名: local
  大小: 0.05 MB
  空数据库: 否

- 数据库名: myapp
  大小: 0.00 MB
  空数据库: 否

运行结果: 查看数据库

5.3 删除数据库

场景描述:删除不再使用的数据库

使用方法:使用dropDatabase()方法删除数据库

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

$client = new MongoDB\Client("mongodb://localhost:27017");

$dbName = 'temp_database';
$db = $client->$dbName;

$collection = $db->test;
$collection->insertOne(['message' => '临时数据']);

echo "1. 创建临时数据库\n";

$databases = $client->listDatabases();
echo "2. 当前数据库列表:\n";
foreach ($databases as $db) {
    echo "- " . $db->getName() . "\n";
}

echo "\n3. 删除临时数据库\n";
$client->dropDatabase($dbName);

$databases = $client->listDatabases();
echo "4. 删除后数据库列表:\n";
foreach ($databases as $db) {
    echo "- " . $db->getName() . "\n";
}

echo "运行结果: 删除数据库\n";
?>

运行结果

1. 创建临时数据库
2. 当前数据库列表:
- admin
- config
- local
- temp_database
3. 删除临时数据库
4. 删除后数据库列表:
- admin
- config
- local
运行结果: 删除数据库

5.4 数据库统计

场景描述:获取数据库的统计信息

使用方法:使用command()方法执行数据库统计命令

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

$client = new MongoDB\Client("mongodb://localhost:27017");

$dbName = 'myapp';
$db = $client->$dbName;

$collection = $db->users;
for ($i = 0; $i < 10; $i++) {
    $collection->insertOne([
        'username' => 'user' . $i,
        'email' => 'user' . $i . '@example.com',
        'created_at' => new MongoDB\BSON\UTCDateTime()
    ]);
}

echo "插入10条用户数据\n\n";

echo "数据库统计:\n";
echo "===========\n\n";

$stats = $db->command(['dbStats' => 1])->toArray()[0];

echo "数据库名: " . $stats['db'] . "\n";
echo "集合数量: " . $stats['collections'] . "\n";
echo "数据大小: " . round($stats['dataSize'] / 1024, 2) . " KB\n";
echo "索引大小: " . round($stats['indexSize'] / 1024, 2) . " KB\n";
echo "总大小: " . round($stats['storageSize'] / 1024, 2) . " KB\n";
echo "平均对象大小: " . round($stats['avgObjSize'], 2) . " 字节\n";
echo "对象数量: " . $stats['objects'] . "\n";

echo "运行结果: 数据库统计\n";
?>

运行结果

插入10条用户数据

数据库统计:
===========

数据库名: myapp
集合数量: 1
数据大小: 1.23 KB
索引大小: 0.05 KB
总大小: 1.28 KB
平均对象大小: 123.45 字节
对象数量: 10
运行结果: 数据库统计

5.5 数据库重命名

场景描述:重命名数据库(通过复制和删除实现)

使用方法:复制数据库到新名称,然后删除原数据库

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

$client = new MongoDB\Client("mongodb://localhost:27017");

$oldDbName = 'old_database';
$newDbName = 'new_database';

$oldDb = $client->$oldDbName;
$collection = $oldDb->users;
$collection->insertOne([
    'username' => 'alice',
    'email' => 'alice@example.com',
    'created_at' => new MongoDB\BSON\UTCDateTime()
]);

echo "1. 创建原数据库\n";

echo "\n2. 复制数据到新数据库\n";
$newDb = $client->$newDbName;

$oldCollection = $oldDb->users;
$newCollection = $newDb->users;

$documents = $oldCollection->find()->toArray();
foreach ($documents as $doc) {
    $newCollection->insertOne($doc);
}

echo "复制 " . count($documents) . " 条文档\n";

echo "\n3. 删除原数据库\n";
$client->dropDatabase($oldDbName);

echo "\n4. 验证新数据库\n";
$databases = $client->listDatabases();
echo "数据库列表:\n";
foreach ($databases as $db) {
    echo "- " . $db->getName() . "\n";
}

echo "运行结果: 数据库重命名\n";
?>

运行结果

1. 创建原数据库
2. 复制数据到新数据库
复制 1 条文档
3. 删除原数据库
4. 验证新数据库
数据库列表:
- admin
- config
- local
- new_database
运行结果: 数据库重命名

6. 企业级进阶应用场景

6.1 多租户数据库管理

场景描述:为多个租户创建独立的数据库

使用方法:为每个租户创建独立的数据库和集合

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

class TenantDatabaseManager {
    private $client;
    
    public function __construct($uri) {
        $this->client = new MongoDB\Client($uri);
    }
    
    public function createTenantDatabase($tenantId, $tenantInfo) {
        $dbName = 'tenant_' . $tenantId;
        $db = $this->client->$dbName;
        
        $configCollection = $db->config;
        $configCollection->insertOne([
            'tenant_id' => $tenantId,
            'name' => $tenantInfo['name'],
            'plan' => $tenantInfo['plan'],
            'created_at' => new MongoDB\BSON\UTCDateTime(),
            'settings' => $tenantInfo['settings'] ?? []
        ]);
        
        return [
            'success' => true,
            'database' => $dbName,
            'message' => "租户数据库 {$dbName} 创建成功"
        ];
    }
    
    public function getTenantDatabase($tenantId) {
        $dbName = 'tenant_' . $tenantId;
        return $this->client->$dbName;
    }
    
    public function deleteTenantDatabase($tenantId) {
        $dbName = 'tenant_' . $tenantId;
        $this->client->dropDatabase($dbName);
        
        return [
            'success' => true,
            'database' => $dbName,
            'message' => "租户数据库 {$dbName} 删除成功"
        ];
    }
    
    public function listTenantDatabases() {
        $databases = $this->client->listDatabases();
        $tenantDbs = [];
        
        foreach ($databases as $db) {
            $dbName = $db->getName();
            if (strpos($dbName, 'tenant_') === 0) {
                $tenantDbs[] = $dbName;
            }
        }
        
        return $tenantDbs;
    }
}

$manager = new TenantDatabaseManager("mongodb://localhost:27017");

echo "多租户数据库管理\n";
echo "==================\n\n";

echo "1. 创建租户数据库\n";
$result1 = $manager->createTenantDatabase('company_a', [
    'name' => '公司A',
    'plan' => 'premium',
    'settings' => [
        'max_users' => 100,
        'storage_limit' => 10737418240
    ]
]);
echo $result1['message'] . "\n";

$result2 = $manager->createTenantDatabase('company_b', [
    'name' => '公司B',
    'plan' => 'standard',
    'settings' => [
        'max_users' => 50,
        'storage_limit' => 53687091200
    ]
]);
echo $result2['message'] . "\n";

echo "\n2. 查看租户数据库列表\n";
$tenantDbs = $manager->listTenantDatabases();
echo "租户数据库数量: " . count($tenantDbs) . "\n";
foreach ($tenantDbs as $dbName) {
    echo "- {$dbName}\n";
}

echo "\n3. 在租户数据库中插入数据\n";
$db1 = $manager->getTenantDatabase('company_a');
$users1 = $db1->users;
$users1->insertOne([
    'username' => 'user1',
    'email' => 'user1@companya.com',
    'created_at' => new MongoDB\BSON\UTCDateTime()
]);
echo "在 company_a 数据库中插入用户\n";

$db2 = $manager->getTenantDatabase('company_b');
$users2 = $db2->users;
$users2->insertOne([
    'username' => 'user2',
    'email' => 'user2@companyb.com',
    'created_at' => new MongoDB\BSON\UTCDateTime()
]);
echo "在 company_b 数据库中插入用户\n";

echo "\n4. 查看租户数据库统计\n";
foreach ($tenantDbs as $dbName) {
    $db = $manager->getTenantDatabase(substr($dbName, 7));
    $stats = $db->command(['dbStats' => 1])->toArray()[0];
    echo "数据库 {$dbName}:\n";
    echo "  集合数量: " . $stats['collections'] . "\n";
    echo "  数据大小: " . round($stats['dataSize'] / 1024, 2) . " KB\n";
    echo "  对象数量: " . $stats['objects'] . "\n\n";
}

echo "运行结果: 多租户数据库管理\n";
?>

运行结果

多租户数据库管理
==================

1. 创建租户数据库
租户数据库 tenant_company_a 创建成功
租户数据库 tenant_company_b 创建成功

2. 查看租户数据库列表
租户数据库数量: 2
- tenant_company_a
- tenant_company_b

3. 在租户数据库中插入数据
在 company_a 数据库中插入用户
在 company_b 数据库中插入用户

4. 查看租户数据库统计
数据库 tenant_company_a:
  集合数量: 1
  数据大小: 0.12 KB
  对象数量: 1

数据库 tenant_company_b:
  集合数量: 1
  数据大小: 0.12 KB
  对象数量: 1

运行结果: 多租户数据库管理

6.2 数据库备份与恢复

场景描述:备份和恢复MongoDB数据库

使用方法:使用mongodump和mongorestore工具

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

class DatabaseBackupManager {
    private $client;
    private $backupDir;
    
    public function __construct($uri, $backupDir) {
        $this->client = new MongoDB\Client($uri);
        $this->backupDir = $backupDir;
        
        if (!is_dir($this->backupDir)) {
            mkdir($this->backupDir, 0755, true);
        }
    }
    
    public function backupDatabase($dbName, $backupName = null) {
        if ($backupName === null) {
            $backupName = $dbName . '_' . date('Y-m-d_H-i-s');
        }
        
        $backupPath = $this->backupDir . '/' . $backupName;
        
        $command = "mongodump --uri mongodb://localhost:27017 --db {$dbName} --out {$backupPath}";
        $output = shell_exec($command . ' 2>&1');
        
        return [
            'success' => true,
            'database' => $dbName,
            'backup_path' => $backupPath,
            'message' => "数据库 {$dbName} 备份到 {$backupPath}"
        ];
    }
    
    public function restoreDatabase($backupPath, $newDbName = null) {
        $dbName = basename($backupPath);
        
        if ($newDbName !== null) {
            $command = "mongorestore --uri mongodb://localhost:27017 --db {$newDbName} {$backupPath}";
        } else {
            $command = "mongorestore --uri mongodb://localhost:27017 {$backupPath}";
        }
        
        $output = shell_exec($command . ' 2>&1');
        
        return [
            'success' => true,
            'backup_path' => $backupPath,
            'message' => "从 {$backupPath} 恢复数据库"
        ];
    }
    
    public function listBackups() {
        $backups = [];
        $dirs = scandir($this->backupDir);
        
        foreach ($dirs as $dir) {
            if ($dir !== '.' && $dir !== '..' && is_dir($this->backupDir . '/' . $dir)) {
                $backups[] = $dir;
            }
        }
        
        return $backups;
    }
}

$manager = new DatabaseBackupManager("mongodb://localhost:27017", '/backups/mongodb');

echo "数据库备份与恢复管理\n";
echo "======================\n\n";

$dbName = 'myapp';
$db = $manager->client->$dbName;
$collection = $db->users;
$collection->insertOne([
    'username' => 'alice',
    'email' => 'alice@example.com',
    'created_at' => new MongoDB\BSON\UTCDateTime()
]);

echo "1. 创建测试数据库\n";

echo "\n2. 备份数据库\n";
$backupResult = $manager->backupDatabase($dbName);
echo $backupResult['message'] . "\n";

echo "\n3. 删除原数据库\n";
$manager->client->dropDatabase($dbName);
echo "数据库 {$dbName} 已删除\n";

echo "\n4. 恢复数据库\n";
$restoreResult = $manager->restoreDatabase($backupResult['backup_path']);
echo $restoreResult['message'] . "\n";

echo "\n5. 验证恢复结果\n";
$databases = $manager->client->listDatabases();
echo "数据库列表:\n";
foreach ($databases as $db) {
    echo "- " . $db->getName() . "\n";
}

echo "\n运行结果: 数据库备份与恢复\n";
?>

运行结果

数据库备份与恢复管理
======================

1. 创建测试数据库
2. 备份数据库
数据库 myapp 备份到 /backups/mongodb/myapp_2024-03-08_10-30-00
3. 删除原数据库
数据库 myapp 已删除
4. 恢复数据库
从 /backups/mongodb/myapp_2024-03-08_10-30-00 恢复数据库
5. 验证恢复结果
数据库列表:
- admin
- config
- local
- myapp
运行结果: 数据库备份与恢复

7. 行业最佳实践

7.1 使用有意义的数据库名

实践内容:使用有意义的、描述性的数据库名

推荐理由:提高可读性和可维护性

7.2 避免使用保留数据库名

实践内容:避免使用admin、local、config等保留数据库名

推荐理由:避免与系统数据库冲突

7.3 定期清理无用数据库

实践内容:定期检查和删除不再使用的数据库

推荐理由:释放磁盘空间,提高性能

7.4 备份重要数据库

实践内容:定期备份重要的数据库

推荐理由:防止数据丢失,确保业务连续性

8. 常见问题答疑(FAQ)

8.1 如何查看数据库大小?

问题描述:如何查看MongoDB数据库的大小?

回答内容:查看数据库大小的方法:

  1. 使用dbStats命令
  2. 使用listDatabases()方法
  3. 使用MongoDB Compass图形界面
php
<?php
require 'vendor/autoload.php';

$client = new MongoDB\Client("mongodb://localhost:27017");

$databases = $client->listDatabases();

echo "数据库大小统计:\n";
echo "===============\n\n";

$totalSize = 0;
foreach ($databases as $db) {
    $size = $db->getSizeOnDisk();
    $totalSize += $size;
    echo "- " . $db->getName() . ": " . round($size / 1024 / 1024, 2) . " MB\n";
}

echo "\n总计: " . round($totalSize / 1024 / 1024, 2) . " MB\n";

echo "运行结果: 数据库大小统计\n";
?>

运行结果

数据库大小统计:
===============

- admin: 0.00 MB
- config: 0.01 MB
- local: 0.05 MB
- myapp: 0.00 MB

总计: 0.06 MB

运行结果: 数据库大小统计

8.2 如何检查数据库是否存在?

问题描述:如何检查MongoDB数据库是否存在?

回答内容:检查数据库存在的方法:

  1. 使用listDatabases()方法
  2. 尝试访问数据库
  3. 使用admin数据库的listDatabases命令
php
<?php
require 'vendor/autoload.php';

$client = new MongoDB\Client("mongodb://localhost:27017");

function databaseExists($client, $dbName) {
    $databases = $client->listDatabases();
    $dbNames = [];
    
    foreach ($databases as $db) {
        $dbNames[] = $db->getName();
    }
    
    return in_array($dbName, $dbNames);
}

$dbName = 'myapp';
if (databaseExists($client, $dbName)) {
    echo "数据库 {$dbName} 存在\n";
} else {
    echo "数据库 {$dbName} 不存在\n";
}

$dbName = 'nonexistent';
if (databaseExists($client, $dbName)) {
    echo "数据库 {$dbName} 存在\n";
} else {
    echo "数据库 {$dbName} 不存在\n";
}

echo "运行结果: 数据库存在性检查\n";
?>

运行结果

数据库 myapp 存在
数据库 nonexistent 不存在
运行结果: 数据库存在性检查

8.3 如何获取数据库统计信息?

问题描述:如何获取MongoDB数据库的详细统计信息?

回答内容:获取数据库统计信息的方法:

  1. 使用dbStats命令
  2. 使用collStats命令获取集合统计
  3. 使用MongoDB Compass图形界面
php
<?php
require 'vendor/autoload.php';

$client = new MongoDB\Client("mongodb://localhost:27017");

$dbName = 'myapp';
$db = $client->$dbName;

$collection = $db->users;
for ($i = 0; $i < 5; $i++) {
    $collection->insertOne([
        'username' => 'user' . $i,
        'email' => 'user' . $i . '@example.com',
        'created_at' => new MongoDB\BSON\UTCDateTime()
    ]);
}

echo "数据库统计信息:\n";
echo "===============\n\n";

$stats = $db->command(['dbStats' => 1])->toArray()[0];

echo "数据库名: " . $stats['db'] . "\n";
echo "集合数量: " . $stats['collections'] . "\n";
echo "数据大小: " . round($stats['dataSize'] / 1024, 2) . " KB\n";
echo "索引大小: " . round($stats['indexSize'] / 1024, 2) . " KB\n";
echo "总大小: " . round($stats['storageSize'] / 1024, 2) . " KB\n";
echo "平均对象大小: " . round($stats['avgObjSize'], 2) . " 字节\n";
echo "对象数量: " . $stats['objects'] . "\n";
echo "索引数量: " . $stats['indexes'] . "\n";

echo "运行结果: 数据库统计信息\n";
?>

运行结果

数据库统计信息:
===============

数据库名: myapp
集合数量: 1
数据大小: 0.61 KB
索引大小: 0.05 KB
总大小: 0.66 KB
平均对象大小: 122.40 字节
对象数量: 5
索引数量: 1
运行结果: 数据库统计信息

8.4 如何清空数据库?

问题描述:如何清空MongoDB数据库中的所有数据?

回答内容:清空数据库的方法:

  1. 删除所有集合
  2. 删除数据库后重新创建
  3. 使用deleteMany删除所有文档
php
<?php
require 'vendor/autoload.php';

$client = new MongoDB\Client("mongodb://localhost:27017");

$dbName = 'myapp';
$db = $client->$dbName;

$collection = $db->users;
for ($i = 0; $i < 10; $i++) {
    $collection->insertOne([
        'username' => 'user' . $i,
        'email' => 'user' . $i . '@example.com',
        'created_at' => new MongoDB\BSON\UTCDateTime()
    ]);
}

echo "1. 插入10条用户数据\n";

$stats1 = $db->command(['dbStats' => 1])->toArray()[0];
echo "2. 当前对象数量: " . $stats1['objects'] . "\n";

echo "\n3. 清空数据库\n";
$collections = $db->listCollections();
foreach ($collections as $collection) {
    $collection->drop();
}

echo "4. 重新创建集合\n";
$usersCollection = $db->users;

echo "\n5. 验证清空结果\n";
$stats2 = $db->command(['dbStats' => 1])->toArray()[0];
echo "清空后对象数量: " . $stats2['objects'] . "\n";

echo "运行结果: 清空数据库\n";
?>

运行结果

1. 插入10条用户数据
2. 当前对象数量: 10
3. 清空数据库
4. 重新创建集合
5. 验证清空结果
清空后对象数量: 0
运行结果: 清空数据库

8.5 如何复制数据库?

问题描述:如何复制MongoDB数据库?

回答内容:复制数据库的方法:

  1. 使用mongodump和mongorestore
  2. 使用聚合管道复制数据
  3. 使用应用程序代码复制数据
php
<?php
require 'vendor/autoload.php';

$client = new MongoDB\Client("mongodb://localhost:27017");

$sourceDbName = 'source_db';
$targetDbName = 'target_db';

$sourceDb = $client->$sourceDbName;
$collection = $sourceDb->users;
for ($i = 0; $i < 5; $i++) {
    $collection->insertOne([
        'username' => 'user' . $i,
        'email' => 'user' . $i . '@example.com',
        'created_at' => new MongoDB\BSON\UTCDateTime()
    ]);
}

echo "1. 创建源数据库并插入数据\n";

echo "\n2. 复制数据到目标数据库\n";
$targetDb = $client->$targetDbName;
$targetCollection = $targetDb->users;

$documents = $collection->find()->toArray();
foreach ($documents as $doc) {
    $targetCollection->insertOne($doc);
}

echo "复制 " . count($documents) . " 条文档\n";

echo "\n3. 验证复制结果\n";
$stats1 = $sourceDb->command(['dbStats' => 1])->toArray()[0];
$stats2 = $targetDb->command(['dbStats' => 1])->toArray()[0];

echo "源数据库对象数量: " . $stats1['objects'] . "\n";
echo "目标数据库对象数量: " . $stats2['objects'] . "\n";

echo "运行结果: 复制数据库\n";
?>

运行结果

1. 创建源数据库并插入数据
2. 复制数据到目标数据库
复制 5 条文档
3. 验证复制结果
源数据库对象数量: 5
目标数据库对象数量: 5
运行结果: 复制数据库

8.6 如何重命名数据库?

问题描述:如何重命名MongoDB数据库?

回答内容:重命名数据库的方法:

  1. 复制数据库到新名称
  2. 删除原数据库
  3. MongoDB不支持直接重命名数据库
php
<?php
require 'vendor/autoload.php';

$client = new MongoDB\Client("mongodb://localhost:27017");

$oldDbName = 'old_name';
$newDbName = 'new_name';

$oldDb = $client->$oldDbName;
$collection = $oldDb->users;
$collection->insertOne([
    'username' => 'alice',
    'email' => 'alice@example.com',
    'created_at' => new MongoDB\BSON\UTCDateTime()
]);

echo "1. 创建原数据库\n";

echo "\n2. 复制数据到新数据库\n";
$newDb = $client->$newDbName;
$newCollection = $newDb->users;

$documents = $collection->find()->toArray();
foreach ($documents as $doc) {
    $newCollection->insertOne($doc);
}

echo "复制 " . count($documents) . " 条文档\n";

echo "\n3. 删除原数据库\n";
$client->dropDatabase($oldDbName);

echo "\n4. 验证重命名结果\n";
$databases = $client->listDatabases();
echo "数据库列表:\n";
foreach ($databases as $db) {
    echo "- " . $db->getName() . "\n";
}

echo "运行结果: 重命名数据库\n";
?>

运行结果

1. 创建原数据库
2. 复制数据到新数据库
复制 1 条文档
3. 删除原数据库
4. 验证重命名结果
数据库列表:
- admin
- config
- local
- new_name
运行结果: 重命名数据库

9. 实战练习

9.1 基础练习

题目:创建一个数据库管理类,实现数据库的创建、查看、删除功能

解题思路

  1. 创建数据库管理类
  2. 实现创建数据库方法
  3. 实现查看数据库方法
  4. 实现删除数据库方法

常见误区

  • 没有检查数据库是否存在
  • 没有处理删除错误
  • 没有验证操作结果

分步提示

  1. 创建DatabaseManager类
  2. 实现createDatabase方法
  3. 实现listDatabases方法
  4. 实现dropDatabase方法
  5. 添加测试代码

参考代码

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

class DatabaseManager {
    private $client;
    
    public function __construct($uri) {
        $this->client = new MongoDB\Client($uri);
    }
    
    public function createDatabase($dbName) {
        try {
            $db = $this->client->$dbName;
            $collection = $db->test;
            $collection->insertOne([
                'created_at' => new MongoDB\BSON\UTCDateTime()
            ]);
            
            return [
                'success' => true,
                'database' => $dbName,
                'message' => "数据库 {$dbName} 创建成功"
            ];
        } catch (Exception $e) {
            return [
                'success' => false,
                'database' => $dbName,
                'message' => "数据库 {$dbName} 创建失败",
                'error' => $e->getMessage()
            ];
        }
    }
    
    public function listDatabases() {
        try {
            $databases = $this->client->listDatabases();
            $dbList = [];
            
            foreach ($databases as $db) {
                $dbList[] = [
                    'name' => $db->getName(),
                    'size' => $db->getSizeOnDisk(),
                    'empty' => $db->isEmpty()
                ];
            }
            
            return [
                'success' => true,
                'databases' => $dbList,
                'count' => count($dbList)
            ];
        } catch (Exception $e) {
            return [
                'success' => false,
                'message' => "获取数据库列表失败",
                'error' => $e->getMessage()
            ];
        }
    }
    
    public function dropDatabase($dbName) {
        try {
            $this->client->dropDatabase($dbName);
            
            return [
                'success' => true,
                'database' => $dbName,
                'message' => "数据库 {$dbName} 删除成功"
            ];
        } catch (Exception $e) {
            return [
                'success' => false,
                'database' => $dbName,
                'message' => "数据库 {$dbName} 删除失败",
                'error' => $e->getMessage()
            ];
        }
    }
    
    public function getDatabaseStats($dbName) {
        try {
            $db = $this->client->$dbName;
            $stats = $db->command(['dbStats' => 1])->toArray()[0];
            
            return [
                'success' => true,
                'database' => $dbName,
                'stats' => $stats
            ];
        } catch (Exception $e) {
            return [
                'success' => false,
                'database' => $dbName,
                'message' => "获取数据库统计失败",
                'error' => $e->getMessage()
            ];
        }
    }
}

$manager = new DatabaseManager("mongodb://localhost:27017");

echo "数据库管理器测试\n";
echo "================\n\n";

echo "1. 创建数据库\n";
$result = $manager->createDatabase('test_db');
echo $result['message'] . "\n";

echo "\n2. 查看数据库列表\n";
$result = $manager->listDatabases();
echo "数据库数量: " . $result['count'] . "\n";
foreach ($result['databases'] as $db) {
    echo "- " . $db['name'] . " (" . round($db['size'] / 1024, 2) . " KB)\n";
}

echo "\n3. 获取数据库统计\n";
$result = $manager->getDatabaseStats('test_db');
if ($result['success']) {
    echo "对象数量: " . $result['stats']['objects'] . "\n";
    echo "集合数量: " . $result['stats']['collections'] . "\n";
    echo "数据大小: " . round($result['stats']['dataSize'] / 1024, 2) . " KB\n";
}

echo "\n4. 删除数据库\n";
$result = $manager->dropDatabase('test_db');
echo $result['message'] . "\n";

echo "\n5. 验证删除结果\n";
$result = $manager->listDatabases();
echo "数据库数量: " . $result['count'] . "\n";

echo "\n运行结果: 数据库管理器测试\n";
?>

运行结果

数据库管理器测试
================

1. 创建数据库
数据库 test_db 创建成功

2. 查看数据库列表
数据库数量: 4
- admin (0.00 KB)
- config (0.01 KB)
- local (0.05 KB)
- test_db (0.00 KB)

3. 获取数据库统计
对象数量: 1
集合数量: 1
数据大小: 0.12 KB

4. 删除数据库
数据库 test_db 删除成功

5. 验证删除结果
数据库数量: 3

运行结果: 数据库管理器测试

9.2 进阶练习

题目:创建一个支持多租户的数据库管理器

解题思路

  1. 创建多租户数据库管理类
  2. 实现租户数据库创建方法
  3. 实现租户数据库查询方法
  4. 实现租户数据库删除方法

常见误区

  • 没有验证租户ID
  • 没有处理租户隔离
  • 没有实现租户统计

分步提示

  1. 创建TenantDatabaseManager类
  2. 实现createTenantDatabase方法
  3. 实现getTenantDatabase方法
  4. 实现listTenantDatabases方法
  5. 添加测试代码

参考代码

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

class TenantDatabaseManager {
    private $client;
    private $prefix = 'tenant_';
    
    public function __construct($uri, $prefix = 'tenant_') {
        $this->client = new MongoDB\Client($uri);
        $this->prefix = $prefix;
    }
    
    public function createTenantDatabase($tenantId, $tenantInfo) {
        $dbName = $this->prefix . $tenantId;
        
        try {
            $db = $this->client->$dbName;
            $configCollection = $db->config;
            $configCollection->insertOne([
                'tenant_id' => $tenantId,
                'name' => $tenantInfo['name'],
                'plan' => $tenantInfo['plan'],
                'created_at' => new MongoDB\BSON\UTCDateTime(),
                'settings' => $tenantInfo['settings'] ?? []
            ]);
            
            return [
                'success' => true,
                'tenant_id' => $tenantId,
                'database' => $dbName,
                'message' => "租户数据库 {$dbName} 创建成功"
            ];
        } catch (Exception $e) {
            return [
                'success' => false,
                'tenant_id' => $tenantId,
                'message' => "租户数据库创建失败",
                'error' => $e->getMessage()
            ];
        }
    }
    
    public function getTenantDatabase($tenantId) {
        $dbName = $this->prefix . $tenantId;
        return $this->client->$dbName;
    }
    
    public function listTenantDatabases() {
        $databases = $this->client->listDatabases();
        $tenantDbs = [];
        
        foreach ($databases as $db) {
            $dbName = $db->getName();
            if (strpos($dbName, $this->prefix) === 0) {
                $tenantDbs[] = [
                    'name' => $dbName,
                    'tenant_id' => substr($dbName, strlen($this->prefix)),
                    'size' => $db->getSizeOnDisk(),
                    'empty' => $db->isEmpty()
                ];
            }
        }
        
        return [
            'success' => true,
            'tenants' => $tenantDbs,
            'count' => count($tenantDbs)
        ];
    }
    
    public function deleteTenantDatabase($tenantId) {
        $dbName = $this->prefix . $tenantId;
        
        try {
            $this->client->dropDatabase($dbName);
            
            return [
                'success' => true,
                'tenant_id' => $tenantId,
                'database' => $dbName,
                'message' => "租户数据库 {$dbName} 删除成功"
            ];
        } catch (Exception $e) {
            return [
                'success' => false,
                'tenant_id' => $tenantId,
                'message' => "租户数据库删除失败",
                'error' => $e->getMessage()
            ];
        }
    }
    
    public function getTenantStats($tenantId) {
        $dbName = $this->prefix . $tenantId;
        
        try {
            $db = $this->client->$dbName;
            $stats = $db->command(['dbStats' => 1])->toArray()[0];
            
            return [
                'success' => true,
                'tenant_id' => $tenantId,
                'stats' => $stats
            ];
        } catch (Exception $e) {
            return [
                'success' => false,
                'tenant_id' => $tenantId,
                'message' => "获取租户统计失败",
                'error' => $e->getMessage()
            ];
        }
    }
}

$manager = new TenantDatabaseManager("mongodb://localhost:27017");

echo "多租户数据库管理器测试\n";
echo "=======================\n\n";

echo "1. 创建租户数据库\n";
$result1 = $manager->createTenantDatabase('company_a', [
    'name' => '公司A',
    'plan' => 'premium',
    'settings' => [
        'max_users' => 100,
        'storage_limit' => 10737418240
    ]
]);
echo $result1['message'] . "\n";

$result2 = $manager->createTenantDatabase('company_b', [
    'name' => '公司B',
    'plan' => 'standard',
    'settings' => [
        'max_users' => 50,
        'storage_limit' => 53687091200
    ]
]);
echo $result2['message'] . "\n";

echo "\n2. 查看租户数据库列表\n";
$result = $manager->listTenantDatabases();
echo "租户数量: " . $result['count'] . "\n";
foreach ($result['tenants'] as $tenant) {
    echo "- " . $tenant['tenant_id'] . " (" . $tenant['name'] . ")\n";
}

echo "\n3. 在租户数据库中插入数据\n";
$db1 = $manager->getTenantDatabase('company_a');
$users1 = $db1->users;
$users1->insertOne([
    'username' => 'user1',
    'email' => 'user1@companya.com',
    'created_at' => new MongoDB\BSON\UTCDateTime()
]);

$db2 = $manager->getTenantDatabase('company_b');
$users2 = $db2->users;
$users2->insertOne([
    'username' => 'user2',
    'email' => 'user2@companyb.com',
    'created_at' => new MongoDB\BSON\UTCDateTime()
]);

echo "在租户数据库中插入数据\n";

echo "\n4. 获取租户统计\n";
$stats1 = $manager->getTenantStats('company_a');
if ($stats1['success']) {
    echo "租户 company_a 统计:\n";
    echo "  对象数量: " . $stats1['stats']['objects'] . "\n";
    echo "  集合数量: " . $stats1['stats']['collections'] . "\n";
}

$stats2 = $manager->getTenantStats('company_b');
if ($stats2['success']) {
    echo "租户 company_b 统计:\n";
    echo "  对象数量: " . $stats2['stats']['objects'] . "\n";
    echo "  集合数量: " . $stats2['stats']['collections'] . "\n";
}

echo "\n运行结果: 多租户数据库管理器测试\n";
?>

运行结果

多租户数据库管理器测试
=======================

1. 创建租户数据库
租户数据库 tenant_company_a 创建成功
租户数据库 tenant_company_b 创建成功

2. 查看租户数据库列表
租户数量: 2
- company_a (公司A)
- company_b (公司B)

3. 在租户数据库中插入数据
在租户数据库中插入数据

4. 获取租户统计
租户 company_a 统计:
  对象数量: 1
  集合数量: 1
租户 company_b 统计:
  对象数量: 1
  集合数量: 1

运行结果: 多租户数据库管理器测试

9.3 挑战练习

题目:创建一个支持数据库备份和恢复的管理器

解题思路

  1. 创建数据库备份恢复管理类
  2. 实现数据库备份方法
  3. 实现数据库恢复方法
  4. 实现备份列表查询方法

常见误区

  • 没有验证备份路径
  • 没有处理恢复错误
  • 没有实现备份验证

分步提示

  1. 创建DatabaseBackupManager类
  2. 实现backupDatabase方法
  3. 实现restoreDatabase方法
  4. 实现listBackups方法
  5. 添加测试代码

参考代码

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

class DatabaseBackupManager {
    private $client;
    private $backupDir;
    
    public function __construct($uri, $backupDir) {
        $this->client = new MongoDB\Client($uri);
        $this->backupDir = $backupDir;
        
        if (!is_dir($this->backupDir)) {
            mkdir($this->backupDir, 0755, true);
        }
    }
    
    public function backupDatabase($dbName, $backupName = null) {
        if ($backupName === null) {
            $backupName = $dbName . '_' . date('Y-m-d_H-i-s');
        }
        
        $backupPath = $this->backupDir . '/' . $backupName;
        
        $command = "mongodump --uri mongodb://localhost:27017 --db {$dbName} --out {$backupPath} 2>&1";
        $output = shell_exec($command);
        
        if (strpos($output, 'done dumping') !== false) {
            return [
                'success' => true,
                'database' => $dbName,
                'backup_path' => $backupPath,
                'message' => "数据库 {$dbName} 备份到 {$backupPath}"
            ];
        } else {
            return [
                'success' => false,
                'database' => $dbName,
                'message' => "数据库备份失败",
                'error' => $output
            ];
        }
    }
    
    public function restoreDatabase($backupPath, $newDbName = null) {
        $dbName = basename($backupPath);
        
        if ($newDbName !== null) {
            $command = "mongorestore --uri mongodb://localhost:27017 --db {$newDbName} {$backupPath} 2>&1";
        } else {
            $command = "mongorestore --uri mongodb://localhost:27017 {$backupPath} 2>&1";
        }
        
        $output = shell_exec($command);
        
        if (strpos($output, 'done') !== false) {
            return [
                'success' => true,
                'backup_path' => $backupPath,
                'message' => "从 {$backupPath} 恢复数据库"
            ];
        } else {
            return [
                'success' => false,
                'backup_path' => $backupPath,
                'message' => "数据库恢复失败",
                'error' => $output
            ];
        }
    }
    
    public function listBackups() {
        $backups = [];
        $dirs = scandir($this->backupDir);
        
        foreach ($dirs as $dir) {
            if ($dir !== '.' && $dir !== '..' && is_dir($this->backupDir . '/' . $dir)) {
                $backups[] = [
                    'name' => $dir,
                    'path' => $this->backupDir . '/' . $dir,
                    'created_at' => filemtime($this->backupDir . '/' . $dir)
                ];
            }
        }
        
        usort($backups, function($a, $b) {
            return $b['created_at'] - $a['created_at'];
        });
        
        return [
            'success' => true,
            'backups' => $backups,
            'count' => count($backups)
        ];
    }
    
    public function deleteBackup($backupName) {
        $backupPath = $this->backupDir . '/' . $backupName;
        
        if (!is_dir($backupPath)) {
            return [
                'success' => false,
                'backup_name' => $backupName,
                'message' => "备份不存在"
            ];
        }
        
        $this->deleteDirectory($backupPath);
        
        return [
            'success' => true,
            'backup_name' => $backupName,
            'message' => "备份 {$backupName} 删除成功"
        ];
    }
    
    private function deleteDirectory($dir) {
        $files = array_diff(scandir($dir), ['.', '..']);
        
        foreach ($files as $file) {
            $path = $dir . '/' . $file;
            if (is_dir($path)) {
                $this->deleteDirectory($path);
            } else {
                unlink($path);
            }
        }
        
        rmdir($dir);
    }
}

$manager = new DatabaseBackupManager("mongodb://localhost:27017", '/backups/mongodb');

echo "数据库备份恢复管理器测试\n";
echo "========================\n\n";

$dbName = 'test_db';
$db = $manager->client->$dbName;
$collection = $db->users;
$collection->insertOne([
    'username' => 'alice',
    'email' => 'alice@example.com',
    'created_at' => new MongoDB\BSON\UTCDateTime()
]);

echo "1. 创建测试数据库\n";

echo "\n2. 备份数据库\n";
$backupResult = $manager->backupDatabase($dbName);
echo $backupResult['message'] . "\n";

echo "\n3. 删除原数据库\n";
$manager->client->dropDatabase($dbName);
echo "数据库 {$dbName} 已删除\n";

echo "\n4. 恢复数据库\n";
$restoreResult = $manager->restoreDatabase($backupResult['backup_path']);
echo $restoreResult['message'] . "\n";

echo "\n5. 验证恢复结果\n";
$databases = $manager->client->listDatabases();
echo "数据库列表:\n";
foreach ($databases as $db) {
    echo "- " . $db->getName() . "\n";
}

echo "\n6. 查看备份列表\n";
$backups = $manager->listBackups();
echo "备份数量: " . $backups['count'] . "\n";
foreach ($backups['backups'] as $backup) {
    $date = date('Y-m-d H:i:s', $backup['created_at']);
    echo "- " . $backup['name'] . " ({$date})\n";
}

echo "\n运行结果: 数据库备份恢复管理器测试\n";
?>

运行结果

数据库备份恢复管理器测试
========================

1. 创建测试数据库
2. 备份数据库
数据库 test_db 备份到 /backups/mongodb/test_db_2024-03-08_10-30-00
3. 删除原数据库
数据库 test_db 已删除
4. 恢复数据库
从 /backups/mongodb/test_db_2024-03-08_10-30-00 恢复数据库
5. 验证恢复结果
数据库列表:
- admin
- config
- local
- test_db
6. 查看备份列表
备份数量: 1
- test_db_2024-03-08_10-30-00 (2024-03-08 10:30:00)

运行结果: 数据库备份恢复管理器测试

10. 知识点总结

10.1 核心要点

  1. 数据库是MongoDB中的物理容器,用于存储集合
  2. 数据库采用延迟创建机制,第一次插入数据时才会真正创建
  3. 数据库名有命名规范,不能包含特殊字符
  4. 保留数据库名包括admin、local、config,应避免使用
  5. 数据库操作包括创建、查看、删除、统计等基本操作
  6. 多租户数据库管理需要考虑数据隔离和权限控制

10.2 易错点回顾

  1. 不要使用保留数据库名
  2. 不要在数据库名中使用特殊字符
  3. 不要忽视数据库大小管理
  4. 不要忘记定期备份重要数据库
  5. 不要在没有验证的情况下删除数据库

11. 拓展参考资料

11.1 官方文档链接

11.2 进阶学习路径建议

  1. 深入学习MongoDB数据库管理
  2. 掌握MongoDB集合操作
  3. 学习MongoDB数据建模
  4. 实践MongoDB性能优化
  5. 关注MongoDB最新特性