#!/bin/bash

# MongoDB巡检脚本
# 支持单机、副本集和分片集群

set -euo pipefail

# 默认配置
MONGO_HOST="localhost"
MONGO_PORT="27017"
MONGO_USER=""
MONGO_PASSWORD=""
MONGO_AUTH_DB="admin"
MONGO_SSL=false
CHECK_REPLICA=true
CHECK_SHARDING=true
OUTPUT_DIR="/tmp/mongodb_healthcheck_$(date +%Y%m%d_%H%M%S)"
REPORT_FILE="${OUTPUT_DIR}/mongodb_health_report.txt"
JSON_FILE="${OUTPUT_DIR}/mongodb_details.json"

# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color

# 显示用法
usage() {
    echo "用法: $0 [选项]"
    echo "选项:"
    echo "  -h, --host <host>          MongoDB主机 (默认: localhost)"
    echo "  -p, --port <port>          MongoDB端口 (默认: 27017)"
    echo "  -u, --user <username>      MongoDB用户名"
    echo "  -p, --password <password>  MongoDB密码"
    echo "  -a, --auth-db <db>         认证数据库 (默认: admin)"
    echo "  --ssl                      使用SSL连接"
    echo "  --no-replica               不检查副本集"
    echo "  --no-shard                 不检查分片"
    echo "  -o, --output <dir>         输出目录"
    echo "  --help                     显示帮助信息"
    echo ""
    echo "示例:"
    echo "  $0 -h 127.0.0.1 -p 27017 -u admin -p password"
    echo "  $0 --ssl --no-shard"
}

# 构建mongo连接字符串
build_mongo_uri() {
    local uri="mongodb://"
    
    if [ -n "$MONGO_USER" ] && [ -n "$MONGO_PASSWORD" ]; then
        uri="${uri}${MONGO_USER}:${MONGO_PASSWORD}@"
    fi
    
    uri="${uri}${MONGO_HOST}:${MONGO_PORT}"
    
    if [ -n "$MONGO_AUTH_DB" ]; then
        uri="${uri}/?authSource=${MONGO_AUTH_DB}"
    fi
    
    if [ "$MONGO_SSL" = true ]; then
        uri="${uri}&ssl=true"
    fi
    
    echo "$uri"
}

# 执行MongoDB命令
run_mongo_command() {
    local command="$1"
    local uri=$(build_mongo_uri)
    
    if [ "$MONGO_SSL" = true ]; then
        mongo --quiet --ssl --host "$uri" --eval "$command"
    else
        mongo --quiet "$uri" --eval "$command"
    fi
}

# 检查MongoDB连接
check_connection() {
    echo -e "${BLUE}=== 检查MongoDB连接 ===${NC}"
    
    if ! result=$(run_mongo_command "db.adminCommand('ping')" 2>/dev/null); then
        echo -e "${RED}错误: 无法连接到MongoDB${NC}"
        exit 1
    fi
    
    if echo "$result" | grep -q '"ok" : 1'; then
        echo -e "${GREEN}✓ MongoDB连接成功${NC}"
    else
        echo -e "${RED}✗ MongoDB连接失败${NC}"
        exit 1
    fi
}

# 获取服务器信息
get_server_info() {
    echo -e "${BLUE}=== 服务器信息 ===${NC}"
    
    local info=$(run_mongo_command "db.hostInfo()")
    local version=$(run_mongo_command "db.version()")
    local build_info=$(run_mongo_command "db.serverBuildInfo()")
    
    echo "MongoDB版本: $version"
    echo "主机名: $(echo "$info" | grep 'hostname' | cut -d'"' -f4)"
    echo "CPU架构: $(echo "$info" | grep 'architecture' | cut -d'"' -f4)"
    echo "操作系统: $(echo "$info" | grep 'name' | cut -d'"' -f4)"
    echo "内存: $(echo "$info" | grep 'memSizeMB' | awk '{print $3}') MB"
    echo "启动时间: $(run_mongo_command "db.serverStatus().uptimeEstimate") 秒"
}

# 检查副本集状态
check_replica_set() {
    if [ "$CHECK_REPLICA" = false ]; then
        return
    fi
    
    echo -e "${BLUE}=== 副本集状态检查 ===${NC}"
    
    local rs_status=$(run_mongo_command "rs.status()" 2>/dev/null || echo "NOT_REPLICA_SET")
    
    if echo "$rs_status" | grep -q "NOT_REPLICA_SET"; then
        echo -e "${YELLOW}⚠ 未配置副本集${NC}"
        return
    fi
    
    local rs_config=$(run_mongo_command "rs.conf()")
    local primary=$(echo "$rs_status" | grep '"stateStr" : "PRIMARY"' -B5 | grep 'name' | cut -d'"' -f4)
    local secondaries=$(echo "$rs_status" | grep '"stateStr" : "SECONDARY"' -B5 | grep 'name' | cut -d'"' -f4)
    
    echo "副本集名称: $(echo "$rs_config" | grep '_id' | cut -d'"' -f4)"
    echo "主节点: ${primary:-无}"
    echo "从节点: ${secondaries:-无}"
    
    # 检查副本集健康状态
    local healthy_members=$(echo "$rs_status" | grep '"health" : 1' | wc -l)
    local total_members=$(echo "$rs_status" | grep '"name" :' | wc -l)
    
    if [ "$healthy_members" -eq "$total_members" ]; then
        echo -e "${GREEN}✓ 所有副本集成员健康${NC}"
    else
        echo -e "${RED}✗ 有副本集成员不健康${NC}"
    fi
}

# 检查分片状态
check_sharding() {
    if [ "$CHECK_SHARDING" = false ]; then
        return
    fi
    
    echo -e "${BLUE}=== 分片集群状态检查 ===${NC}"
    
    local sharding_status=$(run_mongo_command "sh.status()" 2>/dev/null || echo "NOT_SHARDED")
    
    if echo "$sharding_status" | grep -q "NOT_SHARDED"; then
        echo -e "${YELLOW}⚠ 未启用分片${NC}"
        return
    fi
    
    local shards=$(run_mongo_command "db.adminCommand({ listShards: 1 })")
    local shard_count=$(echo "$shards" | grep '"host" :' | wc -l)
    
    echo "分片数量: $shard_count"
    echo "分片列表:"
    echo "$shards" | grep '"host" :' | cut -d'"' -f4
    
    # 检查配置服务器
    local config_status=$(run_mongo_command "db.adminCommand({ getCmdLineOpts: 1 })")
    if echo "$config_status" | grep -q "configsvr"; then
        echo -e "${GREEN}✓ 配置服务器运行中${NC}"
    else
        echo -e "${YELLOW}⚠ 未检测到配置服务器${NC}"
    fi
}

# 检查数据库状态
check_databases() {
    echo -e "${BLUE}=== 数据库状态检查 ===${NC}"
    
    local dbs=$(run_mongo_command "show dbs")
    local db_count=$(echo "$dbs" | grep -v "admin\|config\|local" | wc -l)
    
    echo "数据库数量: $db_count"
    echo "数据库列表:"
    echo "$dbs" | awk '{print "  " $1 " - " $2}'
}

# 检查集合状态
check_collections() {
    echo -e "${BLUE}=== 集合状态检查 ===${NC}"
    
    local dbs=$(run_mongo_command "show dbs" | grep -v "admin\|config\|local" | awk '{print $1}')
    
    for db in $dbs; do
        echo "数据库: $db"
        local collections=$(run_mongo_command "use $db; show collections")
        local coll_count=$(echo "$collections" | wc -l)
        
        echo "  集合数量: $coll_count"
        if [ "$coll_count" -gt 10 ]; then
            echo "  前10个集合:"
            echo "$collections" | head -10 | awk '{print "    " $1}'
        else
            echo "$collections" | awk '{print "    " $1}'
        fi
    done
}

# 检查性能指标
check_performance() {
    echo -e "${BLUE}=== 性能指标检查 ===${NC}"
    
    local server_status=$(run_mongo_command "db.serverStatus()")
    
    # 连接数
    local connections=$(echo "$server_status" | grep '"current" :' | head -1 | awk '{print $3}')
    local available=$(echo "$server_status" | grep '"available" :' | awk '{print $3}')
    echo "当前连接数: $connections / $available"
    
    # 内存使用
    local memory=$(echo "$server_status" | grep '"resident" :' | awk '{print $3}')
    echo "常驻内存: ${memory}MB"
    
    # 操作计数器
    local inserts=$(echo "$server_status" | grep '"insert" :' | awk '{print $3}')
    local queries=$(echo "$server_status" | grep '"query" :' | awk '{print $3}')
    local updates=$(echo "$server_status" | grep '"update" :' | awk '{print $3}')
    local deletes=$(echo "$server_status" | grep '"delete" :' | awk '{print $3}')
    echo "操作统计 - 插入: $inserts, 查询: $queries, 更新: $updates, 删除: $deletes"
}

# 检查索引状态
check_indexes() {
    echo -e "${BLUE}=== 索引状态检查 ===${NC}"
    
    local dbs=$(run_mongo_command "show dbs" | grep -v "admin\|config\|local" | awk '{print $1}')
    
    for db in $dbs; do
        echo "数据库: $db"
        local collections=$(run_mongo_command "use $db; show collections")
        
        for coll in $collections; do
            local indexes=$(run_mongo_command "use $db; db.$coll.getIndexes().length")
            echo "  集合: $coll - 索引数: ${indexes:-0}"
        done
    done
}

# 检查慢查询
check_slow_queries() {
    echo -e "${BLUE}=== 慢查询检查 ===${NC}"
    
    local profile_status=$(run_mongo_command "db.getProfilingStatus()")
    local level=$(echo "$profile_status" | grep '"was" :' | awk '{print $3}')
    local slowms=$(echo "$profile_status" | grep '"slowms" :' | awk '{print $3}')
    
    echo "Profiling级别: $level"
    echo "慢查询阈值: ${slowms}ms"
    
    if [ "$level" -gt 0 ]; then
        local slow_queries=$(run_mongo_command "db.system.profile.find({ millis: { \\\$gt: 100 } }).count()")
        echo "超过100ms的慢查询数量: $slow_queries"
    else
        echo -e "${YELLOW}⚠ Profiling未启用,无法统计慢查询${NC}"
    fi
}

# 检查备份状态
check_backup() {
    echo -e "${BLUE}=== 备份状态检查 ===${NC}"
    
    # 检查是否配置了OPLOG(副本集)
    local oplog_size=$(run_mongo_command "db.getReplicationInfo().logSizeMB" 2>/dev/null || echo "0")
    if [ "$oplog_size" -gt 0 ]; then
        echo "OPLOG大小: ${oplog_size}MB"
    else
        echo -e "${YELLOW}⚠ 未检测到OPLOG(可能不是副本集)${NC}"
    fi
    
    # 检查数据目录权限
    local data_dir=$(run_mongo_command "db.serverCmdLineOpts().parsed.storage.dbPath" 2>/dev/null || echo "未知")
    echo "数据目录: $data_dir"
}

# 生成详细报告
generate_detailed_report() {
    echo -e "${BLUE}=== 生成详细报告 ===${NC}"
    
    local commands=(
        "db.serverStatus()"
        "db.stats()"
        "db.adminCommand({ getCmdLineOpts: 1 })"
        "db.adminCommand({ hostInfo: 1 })"
        "db.adminCommand({ buildInfo: 1 })"
    )
    
    for cmd in "${commands[@]}"; do
        echo "执行: $cmd" >> "$JSON_FILE"
        run_mongo_command "printjson($cmd)" >> "$JSON_FILE"
        echo "" >> "$JSON_FILE"
    done
    
    echo -e "${GREEN}✓ 详细报告已保存到: $JSON_FILE${NC}"
}

# 汇总检查结果
summary() {
    echo -e "${CYAN}=== 巡检总结 ===${NC}"
    echo "检查时间: $(date)"
    echo "MongoDB主机: $MONGO_HOST:$MONGO_PORT"
    echo "报告文件: $REPORT_FILE"
    echo "详细数据: $JSON_FILE"
    echo -e "${GREEN}MongoDB巡检完成!${NC}"
}

# 主函数
main() {
    # 创建输出目录
    mkdir -p "$OUTPUT_DIR"
    
    # 重定向输出到报告文件
    exec > >(tee "$REPORT_FILE") 2>&1
    
    echo -e "${GREEN}开始MongoDB巡检...${NC}"
    echo "开始时间: $(date)"
    echo "输出目录: $OUTPUT_DIR"
    echo ""
    
    # 执行各项检查
    check_connection
    echo ""
    
    get_server_info
    echo ""
    
    check_replica_set
    echo ""
    
    check_sharding
    echo ""
    
    check_databases
    echo ""
    
    check_collections
    echo ""
    
    check_performance
    echo ""
    
    check_indexes
    echo ""
    
    check_slow_queries
    echo ""
    
    check_backup
    echo ""
    
    generate_detailed_report
    echo ""
    
    summary
}

# 解析参数
while [[ $# -gt 0 ]]; do
    case $1 in
        -h|--host)
            MONGO_HOST="$2"
            shift 2
            ;;
        -p|--port)
            MONGO_PORT="$2"
            shift 2
            ;;
        -u|--user)
            MONGO_USER="$2"
            shift 2
            ;;
        -p|--password)
            MONGO_PASSWORD="$2"
            shift 2
            ;;
        -a|--auth-db)
            MONGO_AUTH_DB="$2"
            shift 2
            ;;
        --ssl)
            MONGO_SSL=true
            shift
            ;;
        --no-replica)
            CHECK_REPLICA=false
            shift
            ;;
        --no-shard)
            CHECK_SHARDING=false
            shift
            ;;
        -o|--output)
            OUTPUT_DIR="$2"
            shift 2
            ;;
        --help)
            usage
            exit 0
            ;;
        *)
            echo "未知选项: $1"
            usage
            exit 1
            ;;
    esac
done

# 执行主函数
main "$@"


#使用。
# 赋予执行权限
chmod +x mongodb_healthcheck.sh

# 基本用法(无认证)
./mongodb_healthcheck.sh

# 带认证连接
./mongodb_healthcheck.sh -h 127.0.0.1 -p 27017 -u admin -p password

# 使用SSL连接
./mongodb_healthcheck.sh --ssl -h mongodb.example.com

# 跳过分片检查
./mongodb_healthcheck.sh --no-shard

# 指定输出目录
./mongodb_healthcheck.sh -o /path/to/report
巡检内容:
✅ 连接检查 - 测试MongoDB连接状态
✅ 服务器信息 - 版本、主机信息、运行时间
✅ 副本集状态 - 主从节点、健康状态
✅ 分片状态 - 分片数量、配置服务器
✅ 数据库状态 - 数据库列表和大小
✅ 集合状态 - 集合数量和列表
✅ 性能指标 - 连接数、内存使用、操作统计
✅ 索引状态 - 各集合索引数量
✅ 慢查询 - Profiling状态和慢查询统计
✅ 备份状态 - OPLOG大小、数据目录

输出文件:
mongodb_health_report.txt - 文本格式巡检报告
mongodb_details.json - JSON格式详细数据
依赖要求:
MongoDB客户端工具 (mongo)
连接到MongoDB的权限
读取服务器状态命令的权限
这个脚本提供了全面的MongoDB健康检查,可以帮助DBA和运维人员快速了解数据库状态。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐