python备份mongodb数据库全量备份

python备份mongodb数据库全量备份

2025-12-02

import os
import json
from pymongo import MongoClient
from bson import ObjectId, datetime
from typing import Optional, Dict, List


# -------------------------- 自定义 JSON 编码器(处理 MongoDB 特殊类型)--------------------------
class MongoJSONEncoder(json.JSONEncoder):
    """解决 MongoDB 特殊数据类型(ObjectId、datetime 等)无法直接序列化的问题"""

    def default(self, obj):
        if isinstance(obj, ObjectId):
            return str(obj)  # ObjectId 转为字符串
        elif isinstance(obj, datetime.datetime):
            return obj.strftime("%Y-%m-%d %H:%M:%S")  # datetime 转为字符串
        elif isinstance(obj, datetime.date):
            return obj.strftime("%Y-%m-%d")  # date 转为字符串
        elif isinstance(obj, bytes):
            return obj.decode("utf-8", errors="ignore")  # bytes 转为字符串
        elif isinstance(obj, (set, frozenset)):
            return list(obj)  # 集合转为列表
        # 其他类型默认处理
        return super(MongoJSONEncoder, self).default(obj)


# -------------------------- 核心导出函数 --------------------------
def export_mongodb_to_json(
    mongo_uri: str = "mongodb://localhost:27017/",
    username: Optional[str] = None,
    password: Optional[str] = None,
    auth_db: str = "admin",
    output_root: str = "./mongodb_export",
    batch_size: int = 1000,
    indent: int = 2,
) -> None:
    """
    导出 MongoDB 所有数据库和集合到 JSON 文件

    参数说明:
    - mongo_uri: MongoDB 连接地址(默认本地)
    - username: 认证用户名(可选)
    - password: 认证密码(可选)
    - auth_db: 认证数据库(默认 admin)
    - output_root: 导出根目录(默认 ./mongodb_export)
    - batch_size: 分批导出大小(避免大数据量占用过多内存)
    - indent: JSON 格式化缩进(0 为紧凑模式,默认 2 空格)
    """
    # 1. 连接 MongoDB
    try:
        client_kwargs = {}
        if username and password:
            client_kwargs["username"] = username
            client_kwargs["password"] = password
            client_kwargs["authSource"] = auth_db

        client = MongoClient(mongo_uri, **client_kwargs)
        print(f"✅ 成功连接 MongoDB: {mongo_uri}")
    except Exception as e:
        print(f"❌ MongoDB 连接失败: {str(e)}")
        return

    # 2. 创建根导出目录
    os.makedirs(output_root, exist_ok=True)
    print(f"📂 导出根目录: {os.path.abspath(output_root)}")

    # 3. 遍历所有数据库(排除系统数据库)
    system_dbs = {"admin", "local", "config"}
    all_dbs = [db for db in client.list_database_names() if db not in system_dbs]

    if not all_dbs:
        print("⚠️  未找到非系统数据库")
        client.close()
        return

    print(f"📊 发现 {len(all_dbs)} 个非系统数据库: {all_dbs}")

    # 4. 遍历每个数据库的所有集合
    for db_name in all_dbs:
        # 创建数据库对应的文件夹
        db_dir = os.path.join(output_root, db_name)
        os.makedirs(db_dir, exist_ok=True)
        print(f"\n========== 正在导出数据库: {db_name} (目录: {db_dir}) ==========")

        db = client[db_name]
        # 获取所有集合(排除系统集合,如 system.indexes)
        collections = [
            col for col in db.list_collection_names() if not col.startswith("system.")
        ]

        if not collections:
            print(f"⚠️  数据库 {db_name} 无有效集合,跳过")
            continue

        # 5. 导出每个集合到 JSON 文件
        for col_name in collections:
            col = db[col_name]
            total_count = col.count_documents({})  # 集合总文档数
            print(f"📄 集合 {col_name} (共 {total_count} 条数据)")

            # JSON 文件路径
            json_file = os.path.join(db_dir, f"{col_name}.json")

            # 分批查询并写入(避免大数据量内存溢出)
            with open(json_file, "w", encoding="utf-8") as f:
                f.write("[")  # JSON 数组开始
                first_batch = True  # 标记是否为第一批(避免末尾多余逗号)

                for skip in range(0, total_count, batch_size):
                    # 分批查询数据
                    batch_docs = list(col.find().skip(skip).limit(batch_size))

                    # 序列化并写入
                    for doc in batch_docs:
                        if not first_batch:
                            f.write(",")  # 非第一批前加逗号
                        # 使用自定义编码器序列化 MongoDB 特殊类型
                        json.dump(
                            doc,
                            f,
                            cls=MongoJSONEncoder,
                            ensure_ascii=False,
                            indent=indent,
                        )
                        first_batch = False

                        if indent == 0:
                            f.write("\n")  # 紧凑模式下换行,避免一行过长

                f.write("]")  # JSON 数组结束

            print(f"✅ 导出完成: {json_file}")

    # 6. 关闭连接
    client.close()
    print(f"\n🎉 所有数据库导出完成!根目录: {os.path.abspath(output_root)}")


# -------------------------- 执行导出 --------------------------
if __name__ == "__main__":
    # 配置参数(根据实际情况修改)
    CONFIG = {
        "mongo_uri": "mongodb://192.168.28.99:27017/",  # 本地 MongoDB 地址
        # "mongo_uri": "mongodb://192.168.1.100:27017/",  # 远程 MongoDB 地址
        "username": None,  # 无认证则设为 None
        "password": None,  # 无认证则设为 None
        # "username": "your_admin",  # 有认证时填写用户名
        # "password": "your_password",  # 有认证时填写密码
        "auth_db": "admin",  # 认证数据库(默认 admin)
        "output_root": "./mongodb_export",  # 导出根目录
        "batch_size": 1000,  # 每批导出 1000 条(可根据内存调整)
        "indent": 4,  # JSON 缩进(0=紧凑,2=美观)
    }

    # 执行导出
    export_mongodb_to_json(**CONFIG)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值