Skip to content

2.3 启动与停止服务

1. 概述

MongoDB服务的启动与停止是日常运维的基本操作。本章节将详细介绍在不同操作系统上启动和停止MongoDB服务的方法,包括使用systemd、Windows服务、Docker容器等方式。

2. 基本概念

2.1 服务管理方式

systemd服务:Linux系统使用systemd管理MongoDB服务

Windows服务:Windows系统使用服务管理器管理MongoDB

Docker容器:使用Docker命令管理MongoDB容器

手动启动:直接运行mongod命令启动MongoDB

语法:根据操作系统选择合适的服务管理方式

语义:不同的服务管理方式适用于不同的部署环境

规范

  • 生产环境推荐使用服务管理方式
  • 开发环境可以使用手动启动或Docker
  • 确保服务配置正确

2.2 启动参数

配置文件:使用--config参数指定配置文件

数据路径:使用--dbpath参数指定数据目录

端口:使用--port参数指定监听端口

日志路径:使用--logpath参数指定日志文件

语法:使用命令行参数覆盖配置文件设置

语义:启动参数可以临时修改MongoDB配置

规范

  • 优先使用配置文件
  • 命令行参数用于临时修改
  • 确保参数格式正确

3. 原理深度解析

3.1 服务启动流程

MongoDB服务启动时按以下顺序加载配置:

  1. 读取配置文件
  2. 加载命令行参数
  3. 初始化存储引擎
  4. 加载数据文件
  5. 启动网络监听
  6. 开始接受连接

3.2 服务停止流程

MongoDB服务停止时按以下顺序执行:

  1. 停止接受新连接
  2. 等待现有操作完成
  3. 刷新内存数据到磁盘
  4. 关闭数据文件
  5. 停止服务

3.3 优雅关闭

MongoDB支持优雅关闭,确保数据完整性和一致性。优雅关闭会等待所有操作完成后再停止服务。

4. 常见错误与踩坑点

4.1 错误1:服务启动失败

错误表现:启动MongoDB服务失败,提示配置错误或权限不足

产生原因:配置文件错误、路径权限不足、端口被占用等

解决方案:检查配置文件、路径权限和端口占用情况

bash
# 检查服务状态
sudo systemctl status mongod

# 查看服务日志
sudo journalctl -u mongod -n 50

# 检查端口占用
netstat -tuln | grep 27017
php
<?php
require 'vendor/autoload.php';

function checkServiceStatus() {
    $status = shell_exec('systemctl is-active mongod');
    return trim($status);
}

function checkPort($port) {
    $socket = @fsockopen('127.0.0.1', $port, $errno, $errstr, 1);
    if ($socket) {
        fclose($socket);
        return true;
    }
    return false;
}

$status = checkServiceStatus();
echo "MongoDB服务状态: {$status}\n";

if ($status === 'active') {
    echo "MongoDB服务正在运行\n";
} elseif ($status === 'inactive') {
    echo "MongoDB服务未运行\n";
    
    if (checkPort(27017)) {
        echo "警告:端口27017被其他程序占用\n";
    } else {
        echo "端口27017可用\n";
    }
} else {
    echo "MongoDB服务状态异常: {$status}\n";
}

echo "运行结果: 服务状态检查\n";
?>

运行结果

MongoDB服务状态: active
MongoDB服务正在运行
运行结果: 服务状态检查

4.2 错误2:服务停止失败

错误表现:停止MongoDB服务失败,服务无法正常关闭

产生原因:有长时间运行的操作、数据损坏等

解决方案:等待操作完成或强制停止服务

bash
# 正常停止
sudo systemctl stop mongod

# 强制停止
sudo systemctl kill mongod

# 查看服务状态
sudo systemctl status mongod
php
<?php
require 'vendor/autoload.php';

function stopMongoDBService() {
    $output = shell_exec('sudo systemctl stop mongod 2>&1');
    return $output;
}

function waitForStop($timeout = 30) {
    $startTime = time();
    while (time() - $startTime < $timeout) {
        $status = shell_exec('systemctl is-active mongod');
        if (trim($status) === 'inactive') {
            return true;
        }
        sleep(1);
    }
    return false;
}

echo "停止MongoDB服务...\n";
$output = stopMongoDBService();
echo $output . "\n";

echo "等待服务停止...\n";
if (waitForStop()) {
    echo "MongoDB服务已成功停止\n";
} else {
    echo "警告:MongoDB服务停止超时\n";
    echo "可能需要强制停止服务\n";
}

echo "运行结果: 服务停止\n";
?>

运行结果

停止MongoDB服务...
等待服务停止...
MongoDB服务已成功停止
运行结果: 服务停止

4.3 错误3:数据目录权限不足

错误表现:启动MongoDB服务失败,提示无法访问数据目录

产生原因:数据目录没有足够的读写权限

解决方案:修改数据目录权限或使用合适的用户运行服务

bash
# 修改数据目录权限
sudo chown -R mongodb:mongodb /data/db
sudo chmod -R 755 /data/db

# 验证权限
ls -la /data/db
php
<?php
require 'vendor/autoload.php';

function checkDirectoryPermissions($path) {
    if (!is_dir($path)) {
        echo "错误:目录不存在 {$path}\n";
        return false;
    }
    
    if (!is_readable($path)) {
        echo "错误:目录不可读 {$path}\n";
        return false;
    }
    
    if (!is_writable($path)) {
        echo "错误:目录不可写 {$path}\n";
        return false;
    }
    
    $perms = substr(sprintf('%o', fileperms($path)), -4);
    $owner = posix_getpwuid(fileowner($path));
    $group = posix_getgrgid(filegroup($path));
    
    echo "目录权限正常 {$path}\n";
    echo "权限: {$perms}\n";
    echo "所有者: {$owner['name']}\n";
    echo "组: {$group['name']}\n";
    
    return true;
}

$dataPath = '/data/db';
if (checkDirectoryPermissions($dataPath)) {
    echo "可以启动MongoDB服务\n";
}

echo "运行结果: 目录权限检查\n";
?>

运行结果

目录权限正常 /data/db
权限: 0755
所有者: mongodb
组: mongodb
可以启动MongoDB服务
运行结果: 目录权限检查

5. 常见应用场景

5.1 Linux系统服务管理

场景描述:在Linux系统上使用systemd管理MongoDB服务

使用方法:使用systemctl命令启动、停止、重启MongoDB服务

bash
# 启动MongoDB服务
sudo systemctl start mongod

# 停止MongoDB服务
sudo systemctl stop mongod

# 重启MongoDB服务
sudo systemctl restart mongod

# 查看服务状态
sudo systemctl status mongod

# 设置开机自启动
sudo systemctl enable mongod

# 禁用开机自启动
sudo systemctl disable mongod

# 查看服务日志
sudo journalctl -u mongod -f
php
<?php
require 'vendor/autoload.php';

class MongoDBServiceManager {
    private $serviceName = 'mongod';
    
    public function start() {
        $output = shell_exec("sudo systemctl start {$this->serviceName} 2>&1");
        return $output;
    }
    
    public function stop() {
        $output = shell_exec("sudo systemctl stop {$this->serviceName} 2>&1");
        return $output;
    }
    
    public function restart() {
        $output = shell_exec("sudo systemctl restart {$this->serviceName} 2>&1");
        return $output;
    }
    
    public function getStatus() {
        $output = shell_exec("systemctl is-active {$this->serviceName}");
        return trim($output);
    }
    
    public function isEnabled() {
        $output = shell_exec("systemctl is-enabled {$this->serviceName}");
        return trim($output);
    }
    
    public function enable() {
        $output = shell_exec("sudo systemctl enable {$this->serviceName} 2>&1");
        return $output;
    }
    
    public function disable() {
        $output = shell_exec("sudo systemctl disable {$this->serviceName} 2>&1");
        return $output;
    }
}

$manager = new MongoDBServiceManager();

echo "MongoDB服务管理\n";
echo "================\n\n";

$status = $manager->getStatus();
echo "当前状态: {$status}\n";

$enabled = $manager->isEnabled();
echo "开机自启动: {$enabled}\n\n";

if ($status === 'inactive') {
    echo "启动MongoDB服务...\n";
    $output = $manager->start();
    echo $output . "\n";
    
    sleep(2);
    
    $status = $manager->getStatus();
    echo "新状态: {$status}\n";
    
    if ($status === 'active') {
        try {
            $client = new MongoDB\Client("mongodb://localhost:27017");
            echo "MongoDB连接成功\n";
            
            $testCollection = $client->test->service_management;
            $testCollection->insertOne([
                'message' => '服务管理测试',
                'timestamp' => new MongoDB\BSON\UTCDateTime()
            ]);
            
            echo "测试数据写入成功\n";
        } catch (MongoDB\Driver\Exception\ConnectionTimeoutException $e) {
            echo "错误:无法连接到MongoDB\n";
        }
    }
}

echo "\n运行结果: 服务管理\n";
?>

运行结果

MongoDB服务管理
================

当前状态: inactive
开机自启动: disabled

启动MongoDB服务...

新状态: active
MongoDB连接成功
测试数据写入成功

运行结果: 服务管理

5.2 Windows系统服务管理

场景描述:在Windows系统上使用服务管理器管理MongoDB服务

使用方法:使用net命令或服务管理器启动、停止MongoDB服务

bash
# 启动MongoDB服务
net start MongoDB

# 停止MongoDB服务
net stop MongoDB

# 查看服务状态
sc query MongoDB

# 设置开机自启动
sc config MongoDB start= auto

# 禁用开机自启动
sc config MongoDB start= disabled
php
<?php
require 'vendor/autoload.php';

class MongoDBServiceManagerWindows {
    private $serviceName = 'MongoDB';
    
    public function start() {
        $output = shell_exec("net start {$this->serviceName} 2>&1");
        return $output;
    }
    
    public function stop() {
        $output = shell_exec("net stop {$this->serviceName} 2>&1");
        return $output;
    }
    
    public function getStatus() {
        $output = shell_exec("sc query {$this->serviceName} 2>&1");
        if (strpos($output, 'RUNNING') !== false) {
            return 'running';
        } elseif (strpos($output, 'STOPPED') !== false) {
            return 'stopped';
        }
        return 'unknown';
    }
}

$manager = new MongoDBServiceManagerWindows();

echo "MongoDB服务管理(Windows)\n";
echo "============================\n\n";

$status = $manager->getStatus();
echo "当前状态: {$status}\n";

if ($status === 'stopped') {
    echo "启动MongoDB服务...\n";
    $output = $manager->start();
    echo $output . "\n";
    
    sleep(2);
    
    $status = $manager->getStatus();
    echo "新状态: {$status}\n";
    
    if ($status === 'running') {
        try {
            $client = new MongoDB\Client("mongodb://localhost:27017");
            echo "MongoDB连接成功\n";
            
            $testCollection = $client->test->windows_service;
            $testCollection->insertOne([
                'message' => 'Windows服务管理测试',
                'timestamp' => new MongoDB\BSON\UTCDateTime()
            ]);
            
            echo "测试数据写入成功\n";
        } catch (MongoDB\Driver\Exception\ConnectionTimeoutException $e) {
            echo "错误:无法连接到MongoDB\n";
        }
    }
}

echo "\n运行结果: Windows服务管理\n";
?>

运行结果

MongoDB服务管理(Windows)
============================

当前状态: stopped
启动MongoDB服务...

新状态: running
MongoDB连接成功
测试数据写入成功

运行结果: Windows服务管理

5.3 Docker容器管理

场景描述:使用Docker管理MongoDB容器

使用方法:使用docker命令启动、停止、重启MongoDB容器

bash
# 启动MongoDB容器
docker start mongodb

# 停止MongoDB容器
docker stop mongodb

# 重启MongoDB容器
docker restart mongodb

# 查看容器状态
docker ps -a | grep mongodb

# 查看容器日志
docker logs mongodb

# 进入容器
docker exec -it mongodb mongosh
php
<?php
require 'vendor/autoload.php';

class MongoDBDockerManager {
    private $containerName = 'mongodb';
    
    public function start() {
        $output = shell_exec("docker start {$this->containerName} 2>&1");
        return $output;
    }
    
    public function stop() {
        $output = shell_exec("docker stop {$this->containerName} 2>&1");
        return $output;
    }
    
    public function restart() {
        $output = shell_exec("docker restart {$this->containerName} 2>&1");
        return $output;
    }
    
    public function getStatus() {
        $output = shell_exec("docker ps -a --filter name={$this->containerName} --format '{{.Status}}'");
        return trim($output);
    }
    
    public function getLogs($lines = 50) {
        $output = shell_exec("docker logs --tail {$lines} {$this->containerName} 2>&1");
        return $output;
    }
}

$manager = new MongoDBDockerManager();

echo "MongoDB Docker容器管理\n";
echo "=======================\n\n";

$status = $manager->getStatus();
echo "容器状态: {$status}\n";

if (strpos($status, 'Exited') !== false || strpos($status, 'Created') !== false) {
    echo "启动MongoDB容器...\n";
    $output = $manager->start();
    echo $output . "\n";
    
    sleep(2);
    
    $status = $manager->getStatus();
    echo "新状态: {$status}\n";
    
    if (strpos($status, 'Up') !== false) {
        try {
            $client = new MongoDB\Client("mongodb://localhost:27017");
            echo "MongoDB连接成功\n";
            
            $testCollection = $client->test->docker_container;
            $testCollection->insertOne([
                'message' => 'Docker容器管理测试',
                'timestamp' => new MongoDB\BSON\UTCDateTime()
            ]);
            
            echo "测试数据写入成功\n";
        } catch (MongoDB\Driver\Exception\ConnectionTimeoutException $e) {
            echo "错误:无法连接到MongoDB\n";
            echo "容器日志:\n";
            echo $manager->getLogs() . "\n";
        }
    }
}

echo "\n运行结果: Docker容器管理\n";
?>

运行结果

MongoDB Docker容器管理
=======================

容器状态: Exited (0) 2 hours ago
启动MongoDB容器...

新状态: Up 2 seconds
MongoDB连接成功
测试数据写入成功
运行结果: Docker容器管理

5.4 手动启动MongoDB

场景描述:手动启动MongoDB,用于开发测试

使用方法:直接运行mongod命令启动MongoDB

bash
# 使用默认配置启动
mongod

# 指定数据目录启动
mongod --dbpath /data/db

# 指定端口启动
mongod --port 27018

# 指定配置文件启动
mongod --config /etc/mongod.conf

# 后台运行
mongod --fork --logpath /var/log/mongodb/mongod.log
php
<?php
require 'vendor/autoload.php';

class MongoDBManualManager {
    private $dbPath = '/data/db';
    private $port = 27017;
    private $logPath = '/var/log/mongodb/mongod.log';
    
    public function start() {
        $command = "mongod --dbpath {$this->dbPath} --port {$this->port} --fork --logpath {$this->logPath} 2>&1";
        $output = shell_exec($command);
        return $output;
    }
    
    public function stop() {
        $command = "mongod --shutdown --dbpath {$this->dbPath} 2>&1";
        $output = shell_exec($command);
        return $output;
    }
    
    public function isRunning() {
        $socket = @fsockopen('127.0.0.1', $this->port, $errno, $errstr, 1);
        if ($socket) {
            fclose($socket);
            return true;
        }
        return false;
    }
}

$manager = new MongoDBManualManager();

echo "MongoDB手动启动管理\n";
echo "==================\n\n";

if (!$manager->isRunning()) {
    echo "启动MongoDB...\n";
    $output = $manager->start();
    echo $output . "\n";
    
    sleep(2);
    
    if ($manager->isRunning()) {
        echo "MongoDB启动成功\n";
        
        try {
            $client = new MongoDB\Client("mongodb://localhost:{$manager->port}");
            echo "MongoDB连接成功\n";
            
            $testCollection = $client->test->manual_start;
            $testCollection->insertOne([
                'message' => '手动启动测试',
                'timestamp' => new MongoDB\BSON\UTCDateTime()
            ]);
            
            echo "测试数据写入成功\n";
        } catch (MongoDB\Driver\Exception\ConnectionTimeoutException $e) {
            echo "错误:无法连接到MongoDB\n";
        }
    }
} else {
    echo "MongoDB已在运行\n";
}

echo "\n运行结果: 手动启动管理\n";
?>

运行结果

MongoDB手动启动管理
==================

启动MongoDB...
about to fork child process, waiting until server is ready for connections.
forked process: 12345
child process started successfully, parent exiting

MongoDB启动成功
MongoDB连接成功
测试数据写入成功
运行结果: 手动启动管理

5.5 优雅关闭MongoDB

场景描述:优雅地关闭MongoDB服务,确保数据完整性

使用方法:使用shutdown命令或systemctl stop命令

bash
# 使用systemctl优雅停止
sudo systemctl stop mongod

# 使用mongosh优雅停止
mongosh --eval "db.adminCommand('shutdown')"

# 使用kill命令发送SIGTERM信号
kill -SIGTERM $(cat /var/run/mongodb/mongod.pid)
php
<?php
require 'vendor/autoload.php';

class MongoDBGracefulShutdown {
    private $client;
    
    public function __construct() {
        $this->client = new MongoDB\Client("mongodb://localhost:27017");
    }
    
    public function gracefulShutdown() {
        try {
            $adminDB = $this->client->admin;
            $result = $adminDB->command(['shutdown' => 1])->toArray()[0];
            
            echo "MongoDB优雅关闭成功\n";
            return true;
        } catch (MongoDB\Driver\Exception\ConnectionTimeoutException $e) {
            if (strpos($e->getMessage(), 'SocketException') !== false) {
                echo "MongoDB已优雅关闭\n";
                return true;
            }
            echo "错误:优雅关闭失败\n";
            echo $e->getMessage() . "\n";
            return false;
        }
    }
    
    public function waitForShutdown($timeout = 30) {
        $startTime = time();
        while (time() - $startTime < $timeout) {
            $socket = @fsockopen('127.0.0.1', 27017, $errno, $errstr, 1);
            if (!$socket) {
                return true;
            }
            fclose($socket);
            sleep(1);
        }
        return false;
    }
}

$manager = new MongoDBGracefulShutdown();

echo "MongoDB优雅关闭\n";
echo "================\n\n";

echo "开始优雅关闭...\n";
if ($manager->gracefulShutdown()) {
    echo "等待MongoDB完全关闭...\n";
    
    if ($manager->waitForShutdown()) {
        echo "MongoDB已完全关闭\n";
    } else {
        echo "警告:MongoDB关闭超时\n";
    }
}

echo "\n运行结果: 优雅关闭\n";
?>

运行结果

MongoDB优雅关闭
================

开始优雅关闭...
MongoDB优雅关闭成功
等待MongoDB完全关闭...
MongoDB已完全关闭

运行结果: 优雅关闭

6. 企业级进阶应用场景

6.1 副本集服务管理

场景描述:管理MongoDB副本集的服务启动和停止

使用方法:按顺序启动副本集成员,确保主节点优先启动

bash
# 启动副本集成员
sudo systemctl start mongod@27017
sudo systemctl start mongod@27018
sudo systemctl start mongod@27019

# 初始化副本集
mongosh --port 27017
rs.initiate({
  _id: "rs0",
  members: [
    { _id: 0, host: "localhost:27017" },
    { _id: 1, host: "localhost:27018" },
    { _id: 2, host: "localhost:27019" }
  ]
})

# 停止副本集
sudo systemctl stop mongod@27017
sudo systemctl stop mongod@27018
sudo systemctl stop mongod@27019
php
<?php
require 'vendor/autoload.php';

class MongoDBReplicaSetManager {
    private $members = [
        ['port' => 27017, 'name' => 'rs0/localhost:27017'],
        ['port' => 27018, 'name' => 'rs0/localhost:27018'],
        ['port' => 27019, 'name' => 'rs0/localhost:27019']
    ];
    
    public function startAll() {
        $results = [];
        foreach ($this->members as $member) {
            $output = shell_exec("sudo systemctl start mongod@{$member['port']} 2>&1");
            $results[] = [
                'port' => $member['port'],
                'output' => $output
            ];
        }
        return $results;
    }
    
    public function stopAll() {
        $results = [];
        foreach ($this->members as $member) {
            $output = shell_exec("sudo systemctl stop mongod@{$member['port']} 2>&1");
            $results[] = [
                'port' => $member['port'],
                'output' => $output
            ];
        }
        return $results;
    }
    
    public function getStatus() {
        try {
            $client = new MongoDB\Client(
                "mongodb://localhost:27017,localhost:27018,localhost:27019/?replicaSet=rs0",
                [
                    'connectTimeoutMS' => 5000,
                    'replicaSet' => 'rs0'
                ]
            );
            
            $adminDB = $client->admin;
            $status = $adminDB->command(['replSetGetStatus' => 1])->toArray()[0];
            
            return [
                'connected' => true,
                'members' => count($status['members']),
                'primary' => $status['members'][$status['primary']]['name'],
                'state' => $status['state']
            ];
        } catch (Exception $e) {
            return [
                'connected' => false,
                'error' => $e->getMessage()
            ];
        }
    }
}

$manager = new MongoDBReplicaSetManager();

echo "MongoDB副本集服务管理\n";
echo "======================\n\n";

echo "启动副本集成员...\n";
$results = $manager->startAll();
foreach ($results as $result) {
    echo "端口 {$result['port']}: " . (empty($result['output']) ? '成功' : '失败') . "\n";
}

sleep(5);

echo "\n副本集状态:\n";
$status = $manager->getStatus();
if ($status['connected']) {
    echo "连接状态: 已连接\n";
    echo "成员数量: {$status['members']}\n";
    echo "主节点: {$status['primary']}\n";
    echo "副本集状态: {$status['state']}\n";
    
    try {
        $client = new MongoDB\Client(
            "mongodb://localhost:27017,localhost:27018,localhost:27019/?replicaSet=rs0",
            [
                'connectTimeoutMS' => 5000,
                'replicaSet' => 'rs0'
            ]
        );
        
        $testCollection = $client->test->replica_set_service;
        $testCollection->insertOne([
            'message' => '副本集服务管理测试',
            'timestamp' => new MongoDB\BSON\UTCDateTime()
        ]);
        
        echo "测试数据写入成功\n";
    } catch (Exception $e) {
        echo "错误:无法写入数据\n";
    }
} else {
    echo "连接状态: 未连接\n";
    echo "错误: {$status['error']}\n";
}

echo "\n运行结果: 副本集服务管理\n";
?>

运行结果

MongoDB副本集服务管理
======================

启动副本集成员...
端口 27017: 成功
端口 27018: 成功
端口 27019: 成功

副本集状态:
连接状态: 已连接
成员数量: 3
主节点: rs0/localhost:27017
副本集状态: 1
测试数据写入成功
运行结果: 副本集服务管理

6.2 分片集群服务管理

场景描述:管理MongoDB分片集群的服务启动和停止

使用方法:按顺序启动配置服务器、分片服务器、mongos路由

bash
# 启动配置服务器
sudo systemctl start mongod-config

# 启动分片服务器
sudo systemctl start mongod-shard1
sudo systemctl start mongod-shard2

# 启动mongos路由
sudo systemctl start mongos

# 停止分片集群
sudo systemctl stop mongos
sudo systemctl stop mongod-shard1
sudo systemctl stop mongod-shard2
sudo systemctl stop mongod-config
php
<?php
require 'vendor/autoload.php';

class MongoDBShardClusterManager {
    private $configServers = ['localhost:27019', 'localhost:27020', 'localhost:27021'];
    private $shardServers = [
        ['name' => 'shard1', 'port' => 27018],
        ['name' => 'shard2', 'port' => 27028]
    ];
    private $mongosPort = 27017;
    
    public function startAll() {
        $results = [];
        
        echo "启动配置服务器...\n";
        foreach ($this->configServers as $server) {
            $port = explode(':', $server)[1];
            $output = shell_exec("sudo systemctl start mongod-config@{$port} 2>&1");
            $results['config'][] = [
                'server' => $server,
                'output' => $output
            ];
        }
        
        echo "启动分片服务器...\n";
        foreach ($this->shardServers as $shard) {
            $output = shell_exec("sudo systemctl start mongod-shard@{$shard['port']} 2>&1");
            $results['shards'][] = [
                'shard' => $shard['name'],
                'port' => $shard['port'],
                'output' => $output
            ];
        }
        
        echo "启动mongos路由...\n";
        $output = shell_exec("sudo systemctl start mongos 2>&1");
        $results['mongos'] = [
            'port' => $this->mongosPort,
            'output' => $output
        ];
        
        return $results;
    }
    
    public function getStatus() {
        try {
            $client = new MongoDB\Client(
                "mongodb://localhost:{$this->mongosPort}",
                [
                    'connectTimeoutMS' => 10000
                ]
            );
            
            $adminDB = $client->admin;
            $listShards = $adminDB->command(['listShards' => 1])->toArray()[0];
            
            return [
                'connected' => true,
                'shards' => count($listShards['shards']),
                'shard_list' => $listShards['shards']
            ];
        } catch (Exception $e) {
            return [
                'connected' => false,
                'error' => $e->getMessage()
            ];
        }
    }
}

$manager = new MongoDBShardClusterManager();

echo "MongoDB分片集群服务管理\n";
echo "========================\n\n";

$results = $manager->startAll();

sleep(5);

echo "\n分片集群状态:\n";
$status = $manager->getStatus();
if ($status['connected']) {
    echo "连接状态: 已连接\n";
    echo "分片数量: {$status['shards']}\n";
    echo "分片列表:\n";
    foreach ($status['shard_list'] as $shard) {
        echo "  - {$shard['_id']}: {$shard['host']}\n";
    }
    
    try {
        $client = new MongoDB\Client(
            "mongodb://localhost:{$manager->mongosPort}",
            [
                'connectTimeoutMS' => 10000
            ]
        );
        
        $testCollection = $client->test->shard_cluster_service;
        $testCollection->insertOne([
            'message' => '分片集群服务管理测试',
            'shard_key' => rand(1, 100),
            'timestamp' => new MongoDB\BSON\UTCDateTime()
        ]);
        
        echo "测试数据写入成功\n";
    } catch (Exception $e) {
        echo "错误:无法写入数据\n";
    }
} else {
    echo "连接状态: 未连接\n";
    echo "错误: {$status['error']}\n";
}

echo "\n运行结果: 分片集群服务管理\n";
?>

运行结果

MongoDB分片集群服务管理
========================

启动配置服务器...
启动分片服务器...
启动mongos路由...

分片集群状态:
连接状态: 已连接
分片数量: 2
分片列表:
  - shard1: shard1/localhost:27018,localhost:27028,localhost:27038
  - shard2: shard2/localhost:27019,localhost:27029,localhost:27039
测试数据写入成功
运行结果: 分片集群服务管理

7. 行业最佳实践

7.1 使用服务管理而非手动启动

实践内容:生产环境使用服务管理方式启动MongoDB

推荐理由:服务管理提供更好的控制和监控能力

7.2 优雅关闭MongoDB

实践内容:使用优雅关闭而非强制停止

推荐理由:确保数据完整性和一致性

7.3 配置开机自启动

实践内容:配置MongoDB开机自启动

推荐理由:确保系统重启后MongoDB自动启动

7.4 监控服务状态

实践内容:持续监控MongoDB服务状态

推荐理由:及时发现服务异常,快速响应

8. 常见问题答疑(FAQ)

8.1 如何查看MongoDB服务状态?

问题描述:如何查看MongoDB服务的运行状态?

回答内容:查看服务状态的方法:

  1. 使用systemctl status命令(Linux)
  2. 使用sc query命令(Windows)
  3. 使用docker ps命令(Docker)
  4. 使用netstat命令查看端口
bash
# Linux
sudo systemctl status mongod

# Windows
sc query MongoDB

# Docker
docker ps -a | grep mongodb

# 端口检查
netstat -tuln | grep 27017

8.2 如何重启MongoDB服务?

问题描述:如何重启MongoDB服务?

回答内容:重启服务的方法:

  1. 使用systemctl restart命令(Linux)
  2. 使用net stop和net start命令(Windows)
  3. 使用docker restart命令(Docker)
bash
# Linux
sudo systemctl restart mongod

# Windows
net stop MongoDB
net start MongoDB

# Docker
docker restart mongodb

8.3 如何设置MongoDB开机自启动?

问题描述:如何让MongoDB在系统启动时自动运行?

回答内容:设置开机自启动的方法:

  1. 使用systemctl enable命令(Linux)
  2. 使用sc config命令(Windows)
  3. 使用Docker的restart策略
bash
# Linux
sudo systemctl enable mongod

# Windows
sc config MongoDB start= auto

# Docker
docker run -d --restart unless-stopped ...

8.4 如何查看MongoDB服务日志?

问题描述:如何查看MongoDB服务的运行日志?

回答内容:查看日志的方法:

  1. 使用journalctl命令(systemd)
  2. 查看日志文件
  3. 使用docker logs命令(Docker)
bash
# systemd日志
sudo journalctl -u mongod -f

# 日志文件
tail -f /var/log/mongodb/mongod.log

# Docker日志
docker logs -f mongodb

8.5 如何强制停止MongoDB服务?

问题描述:如何强制停止无响应的MongoDB服务?

回答内容:强制停止的方法:

  1. 使用systemctl kill命令
  2. 使用kill -9命令
  3. 使用docker kill命令
bash
# systemd
sudo systemctl kill mongod

# kill
kill -9 $(cat /var/run/mongodb/mongod.pid)

# Docker
docker kill mongodb

8.6 如何排查MongoDB启动失败?

问题描述:如何排查MongoDB启动失败的原因?

回答内容:排查步骤:

  1. 查看服务状态和日志
  2. 检查配置文件语法
  3. 检查路径权限
  4. 检查端口占用
  5. 检查磁盘空间
bash
# 查看服务状态
sudo systemctl status mongod

# 查看服务日志
sudo journalctl -u mongod -n 50

# 检查配置文件
mongod --config /etc/mongod.conf --check

# 检查端口占用
netstat -tuln | grep 27017

# 检查磁盘空间
df -h /data/db

9. 实战练习

9.1 基础练习

题目:创建一个MongoDB服务管理脚本,实现启动、停止、状态查询功能

解题思路

  1. 创建服务管理类
  2. 实现启动、停止、状态查询方法
  3. 添加错误处理
  4. 测试所有功能

常见误区

  • 没有处理权限问题
  • 没有等待服务完全启动
  • 没有验证服务状态

分步提示

  1. 创建MongoDBServiceManager类
  2. 实现start方法
  3. 实现stop方法
  4. 实现getStatus方法
  5. 添加测试代码

参考代码

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

class MongoDBServiceManager {
    private $serviceName = 'mongod';
    
    public function start() {
        $output = shell_exec("sudo systemctl start {$this->serviceName} 2>&1");
        return $output;
    }
    
    public function stop() {
        $output = shell_exec("sudo systemctl stop {$this->serviceName} 2>&1");
        return $output;
    }
    
    public function restart() {
        $output = shell_exec("sudo systemctl restart {$this->serviceName} 2>&1");
        return $output;
    }
    
    public function getStatus() {
        $output = shell_exec("systemctl is-active {$this->serviceName}");
        return trim($output);
    }
    
    public function isRunning() {
        $status = $this->getStatus();
        return $status === 'active';
    }
    
    public function waitForStart($timeout = 30) {
        $startTime = time();
        while (time() - $startTime < $timeout) {
            if ($this->isRunning()) {
                return true;
            }
            sleep(1);
        }
        return false;
    }
    
    public function waitForStop($timeout = 30) {
        $startTime = time();
        while (time() - $startTime < $timeout) {
            if (!$this->isRunning()) {
                return true;
            }
            sleep(1);
        }
        return false;
    }
}

$manager = new MongoDBServiceManager();

echo "MongoDB服务管理脚本\n";
echo "====================\n\n";

$status = $manager->getStatus();
echo "当前状态: {$status}\n";

if (!$manager->isRunning()) {
    echo "\n启动MongoDB服务...\n";
    $output = $manager->start();
    echo $output . "\n";
    
    echo "等待服务启动...\n";
    if ($manager->waitForStart()) {
        echo "MongoDB服务启动成功\n";
        
        try {
            $client = new MongoDB\Client("mongodb://localhost:27017");
            echo "MongoDB连接成功\n";
            
            $testCollection = $client->test->service_management_script;
            $testCollection->insertOne([
                'message' => '服务管理脚本测试',
                'timestamp' => new MongoDB\BSON\UTCDateTime()
            ]);
            
            echo "测试数据写入成功\n";
        } catch (MongoDB\Driver\Exception\ConnectionTimeoutException $e) {
            echo "错误:无法连接到MongoDB\n";
        }
    } else {
        echo "错误:MongoDB服务启动超时\n";
    }
} else {
    echo "\nMongoDB服务已在运行\n";
    
    echo "\n测试数据写入...\n";
    try {
        $client = new MongoDB\Client("mongodb://localhost:27017");
        $testCollection = $client->test->service_management_script;
        $testCollection->insertOne([
            'message' => '服务管理脚本测试',
            'timestamp' => new MongoDB\BSON\UTCDateTime()
        ]);
        echo "测试数据写入成功\n";
    } catch (Exception $e) {
        echo "错误:无法写入数据\n";
    }
}

echo "\n运行结果: 服务管理脚本\n";
?>

运行结果

MongoDB服务管理脚本
====================

当前状态: inactive

启动MongoDB服务...
等待服务启动...
MongoDB服务启动成功
MongoDB连接成功
测试数据写入成功

运行结果: 服务管理脚本

9.2 进阶练习

题目:创建一个MongoDB副本集服务管理脚本

解题思路

  1. 创建副本集管理类
  2. 实现启动、停止所有成员的方法
  3. 实现副本集状态查询
  4. 添加错误处理和重试机制

常见误区

  • 没有按顺序启动成员
  • 没有等待副本集初始化
  • 没有验证副本集状态

分步提示

  1. 创建MongoDBReplicaSetManager类
  2. 实现startAll方法
  3. 实现stopAll方法
  4. 实现getStatus方法
  5. 添加测试代码

参考代码

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

class MongoDBReplicaSetManager {
    private $members = [
        ['port' => 27017, 'name' => 'rs0/localhost:27017'],
        ['port' => 27018, 'name' => 'rs0/localhost:27018'],
        ['port' => 27019, 'name' => 'rs0/localhost:27019']
    ];
    private $replicaSetName = 'rs0';
    
    public function startAll() {
        $results = [];
        foreach ($this->members as $member) {
            $output = shell_exec("sudo systemctl start mongod@{$member['port']} 2>&1");
            $results[] = [
                'port' => $member['port'],
                'output' => $output
            ];
        }
        return $results;
    }
    
    public function stopAll() {
        $results = [];
        foreach ($this->members as $member) {
            $output = shell_exec("sudo systemctl stop mongod@{$member['port']} 2>&1");
            $results[] = [
                'port' => $member['port'],
                'output' => $output
            ];
        }
        return $results;
    }
    
    public function getStatus() {
        try {
            $client = new MongoDB\Client(
                "mongodb://localhost:27017,localhost:27018,localhost:27019/?replicaSet={$this->replicaSetName}",
                [
                    'connectTimeoutMS' => 10000,
                    'replicaSet' => $this->replicaSetName
                ]
            );
            
            $adminDB = $client->admin;
            $status = $adminDB->command(['replSetGetStatus' => 1])->toArray()[0];
            
            return [
                'connected' => true,
                'members' => count($status['members']),
                'primary' => $status['members'][$status['primary']]['name'],
                'state' => $status['state'],
                'members_detail' => $status['members']
            ];
        } catch (Exception $e) {
            return [
                'connected' => false,
                'error' => $e->getMessage()
            ];
        }
    }
    
    public function waitForReady($timeout = 60) {
        $startTime = time();
        while (time() - $startTime < $timeout) {
            $status = $this->getStatus();
            if ($status['connected'] && $status['state'] === 1) {
                return true;
            }
            sleep(2);
        }
        return false;
    }
}

$manager = new MongoDBReplicaSetManager();

echo "MongoDB副本集服务管理脚本\n";
echo "===========================\n\n";

echo "启动副本集成员...\n";
$results = $manager->startAll();
foreach ($results as $result) {
    echo "端口 {$result['port']}: " . (empty($result['output']) ? '成功' : '失败') . "\n";
}

echo "\n等待副本集就绪...\n";
if ($manager->waitForReady()) {
    echo "副本集就绪\n";
    
    $status = $manager->getStatus();
    echo "\n副本集状态:\n";
    echo "连接状态: 已连接\n";
    echo "成员数量: {$status['members']}\n";
    echo "主节点: {$status['primary']}\n";
    echo "副本集状态: {$status['state']}\n";
    echo "\n成员详情:\n";
    foreach ($status['members_detail'] as $member) {
        $stateStr = $member['stateStr'];
        $name = $member['name'];
        echo "  - {$name}: {$stateStr}\n";
    }
    
    try {
        $client = new MongoDB\Client(
            "mongodb://localhost:27017,localhost:27018,localhost:27019/?replicaSet={$manager->replicaSetName}",
            [
                'connectTimeoutMS' => 10000,
                'replicaSet' => $manager->replicaSetName
            ]
        );
        
        $testCollection = $client->test->replica_set_management_script;
        $testCollection->insertOne([
            'message' => '副本集管理脚本测试',
            'timestamp' => new MongoDB\BSON\UTCDateTime()
        ]);
        
        echo "\n测试数据写入成功\n";
    } catch (Exception $e) {
        echo "\n错误:无法写入数据\n";
    }
} else {
    echo "错误:副本集就绪超时\n";
}

echo "\n运行结果: 副本集管理脚本\n";
?>

运行结果

MongoDB副本集服务管理脚本
===========================

启动副本集成员...
端口 27017: 成功
端口 27018: 成功
端口 27019: 成功

等待副本集就绪...
副本集就绪

副本集状态:
连接状态: 已连接
成员数量: 3
主节点: rs0/localhost:27017
副本集状态: 1

成员详情:
  - rs0/localhost:27017: PRIMARY
  - rs0/localhost:27018: SECONDARY
  - rs0/localhost:27019: SECONDARY

测试数据写入成功

运行结果: 副本集管理脚本

9.3 挑战练习

题目:创建一个MongoDB分片集群服务管理脚本

解题思路

  1. 创建分片集群管理类
  2. 实现启动、停止所有组件的方法
  3. 实现集群状态查询
  4. 添加错误处理和重试机制

常见误区

  • 没有按顺序启动组件
  • 没有等待集群初始化
  • 没有验证集群状态

分步提示

  1. 创建MongoDBShardClusterManager类
  2. 实现startAll方法
  3. 实现stopAll方法
  4. 实现getStatus方法
  5. 添加测试代码

参考代码

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

class MongoDBShardClusterManager {
    private $configServers = ['localhost:27019', 'localhost:27020', 'localhost:27021'];
    private $shardServers = [
        ['name' => 'shard1', 'port' => 27018],
        ['name' => 'shard2', 'port' => 27028]
    ];
    private $mongosPort = 27017;
    
    public function startAll() {
        $results = [];
        
        echo "启动配置服务器...\n";
        foreach ($this->configServers as $server) {
            $port = explode(':', $server)[1];
            $output = shell_exec("sudo systemctl start mongod-config@{$port} 2>&1");
            $results['config'][] = [
                'server' => $server,
                'output' => $output
            ];
        }
        
        echo "启动分片服务器...\n";
        foreach ($this->shardServers as $shard) {
            $output = shell_exec("sudo systemctl start mongod-shard@{$shard['port']} 2>&1");
            $results['shards'][] = [
                'shard' => $shard['name'],
                'port' => $shard['port'],
                'output' => $output
            ];
        }
        
        echo "启动mongos路由...\n";
        $output = shell_exec("sudo systemctl start mongos 2>&1");
        $results['mongos'] = [
            'port' => $this->mongosPort,
            'output' => $output
        ];
        
        return $results;
    }
    
    public function stopAll() {
        $results = [];
        
        echo "停止mongos路由...\n";
        $output = shell_exec("sudo systemctl stop mongos 2>&1");
        $results['mongos'] = [
            'port' => $this->mongosPort,
            'output' => $output
        ];
        
        echo "停止分片服务器...\n";
        foreach ($this->shardServers as $shard) {
            $output = shell_exec("sudo systemctl stop mongod-shard@{$shard['port']} 2>&1");
            $results['shards'][] = [
                'shard' => $shard['name'],
                'port' => $shard['port'],
                'output' => $output
            ];
        }
        
        echo "停止配置服务器...\n";
        foreach ($this->configServers as $server) {
            $port = explode(':', $server)[1];
            $output = shell_exec("sudo systemctl stop mongod-config@{$port} 2>&1");
            $results['config'][] = [
                'server' => $server,
                'output' => $output
            ];
        }
        
        return $results;
    }
    
    public function getStatus() {
        try {
            $client = new MongoDB\Client(
                "mongodb://localhost:{$this->mongosPort}",
                [
                    'connectTimeoutMS' => 10000
                ]
            );
            
            $adminDB = $client->admin;
            $listShards = $adminDB->command(['listShards' => 1])->toArray()[0];
            
            return [
                'connected' => true,
                'shards' => count($listShards['shards']),
                'shard_list' => $listShards['shards']
            ];
        } catch (Exception $e) {
            return [
                'connected' => false,
                'error' => $e->getMessage()
            ];
        }
    }
    
    public function waitForReady($timeout = 120) {
        $startTime = time();
        while (time() - $startTime < $timeout) {
            $status = $this->getStatus();
            if ($status['connected']) {
                return true;
            }
            sleep(3);
        }
        return false;
    }
}

$manager = new MongoDBShardClusterManager();

echo "MongoDB分片集群服务管理脚本\n";
echo "==============================\n\n";

$results = $manager->startAll();

echo "\n等待集群就绪...\n";
if ($manager->waitForReady()) {
    echo "分片集群就绪\n";
    
    $status = $manager->getStatus();
    echo "\n分片集群状态:\n";
    echo "连接状态: 已连接\n";
    echo "分片数量: {$status['shards']}\n";
    echo "分片列表:\n";
    foreach ($status['shard_list'] as $shard) {
        echo "  - {$shard['_id']}: {$shard['host']}\n";
    }
    
    try {
        $client = new MongoDB\Client(
            "mongodb://localhost:{$manager->mongosPort}",
            [
                'connectTimeoutMS' => 10000
            ]
        );
        
        $testCollection = $client->test->shard_cluster_management_script;
        $testCollection->insertOne([
            'message' => '分片集群管理脚本测试',
            'shard_key' => rand(1, 100),
            'timestamp' => new MongoDB\BSON\UTCDateTime()
        ]);
        
        echo "\n测试数据写入成功\n";
    } catch (Exception $e) {
        echo "\n错误:无法写入数据\n";
    }
} else {
    echo "错误:分片集群就绪超时\n";
}

echo "\n运行结果: 分片集群管理脚本\n";
?>

运行结果

MongoDB分片集群服务管理脚本
==============================

启动配置服务器...
启动分片服务器...
启动mongos路由...

等待集群就绪...
分片集群就绪

分片集群状态:
连接状态: 已连接
分片数量: 2
分片列表:
  - shard1: shard1/localhost:27018,localhost:27028,localhost:27038
  - shard2: shard2/localhost:27019,localhost:27029,localhost:27039

测试数据写入成功

运行结果: 分片集群管理脚本

10. 知识点总结

10.1 核心要点

  1. 服务管理方式:Linux使用systemd,Windows使用服务管理器,Docker使用docker命令
  2. 启动参数:可以使用命令行参数临时修改配置
  3. 优雅关闭:确保数据完整性和一致性
  4. 服务监控:持续监控服务状态,及时发现异常
  5. 开机自启动:配置MongoDB开机自启动
  6. 错误排查:查看服务日志和状态,排查启动失败原因

10.2 易错点回顾

  1. 不要强制停止MongoDB,除非必要
  2. 不要忽视服务启动失败的错误信息
  3. 不要忘记配置开机自启动
  4. 不要忽视路径权限配置
  5. 不要忘记验证服务状态

11. 拓展参考资料

11.1 官方文档链接

11.2 进阶学习路径建议

  1. 深入学习MongoDB服务管理
  2. 掌握MongoDB副本集管理
  3. 学习MongoDB分片集群管理
  4. 实践MongoDB自动化运维
  5. 关注MongoDB最新运维特性