跳到主要内容

MongoDB 重建索引

  1. 进入存储组件容器

    docker exec -it $(docker ps | grep mingdaoyun-sc | awk '{print $1}') bash
  1. 创建 reIndex.js 脚本文件,重建 mdwsrows 库下的用户自建索引

    reIndex.js 脚本文件容
    var targetDbName = "mdwsrows";  // 目标数据库

    // 集合白名单,白名单中的集合不重建索引
    var collectionWhitelist = [
    "discussion",
    "rowrelations",
    "workSheetRowTopic",
    "workSheetTopic",
    "wslogs"
    ];

    // 索引白名单,白名单中的索引不重建
    var indexWhitelist = [
    "_id_",
    "idx_ctime",
    "idx_utime",
    "uk_rowid",
    "idx_tp_status",
    "idx_thirdprimary"
    ];

    // 格式化时间函数
    function formatDateTime() {
    var now = new Date();
    var utc8Time = new Date(now.getTime() + (8 * 60 * 60 * 1000));
    return utc8Time.toISOString().replace('Z', '+08:00');
    }

    // 格式化输出函数
    function printHeader(text) {
    print("\n" + "=".repeat(100));
    print(text);
    print("=".repeat(100));
    }

    function printSection(text) {
    print("\n" + "-".repeat(80));
    print(text);
    print("-".repeat(80));
    }

    function printTimedAction(time, action) {
    print(`\n[${time}] ${action}`);
    }

    // 格式化 JSON,保持一致的缩进
    function formatJSON(obj, indent = 5) {
    return JSON.stringify(obj, null, 2)
    .split('\n')
    .map((line, i) => i === 0 ? line : ' '.repeat(indent) + line)
    .join('\n');
    }

    // 格式化 createIndex 命令
    function formatCreateIndexCommand(collName, key, options) {
    return `db.${collName}.createIndex(${formatJSON(key)},\n ${formatJSON(options)})`;
    }

    function printCommand(command) {
    print(" └─ Execute:");
    print(" " + command);
    }

    function printCompletion(seconds) {
    print(` └─ ✓ Completed in ${seconds.toFixed(3)} seconds\n`);
    }

    // 连接到指定数据库
    var targetDb = db.getSiblingDB(targetDbName);
    var startTime = formatDateTime();

    printHeader("MongoDB Index Rebuild Process");
    print(`\n• Start Time: ${startTime}`);
    print(`• Target Database: ${targetDb.getName()}`);

    // 获取所有要重建所有的集合(过滤集合白名单)
    var collections = targetDb.getCollectionNames().filter(function(collName) {
    return !collectionWhitelist.includes(collName) &&
    !collName.startsWith('system.');
    });

    print(`• Total Collections: ${collections.length}`);

    collections.forEach(function(collName, index) {
    var coll = targetDb.getCollection(collName);
    var stats = coll.stats();

    // 输出集合信息和进度
    printSection(`Processing Collection [${index + 1}/${collections.length}]: ${collName}`);
    print(`\n• Document Count: ${stats.count}`);
    print(`• Storage Size: ${stats.storageSize} bytes`);

    // 获取需要重建的索引(过滤索引白名单)
    var indexes = coll.getIndexes();
    var rebuildIndexes = indexes.filter(function(idx) {
    return !indexWhitelist.includes(idx.name);
    });

    if (rebuildIndexes.length === 0) {
    print("\n✓ No indexes need to be rebuilt.");
    return;
    }

    // 输出索引重建计划
    print(`\n• Indexes to Rebuild (${rebuildIndexes.length}):`);
    rebuildIndexes.forEach(function(idx) {
    print(` ├─ Name: ${idx.name.padEnd(20)}`);
    print(` │ Key: ${JSON.stringify(idx.key)}`);
    });

    // 重建每个索引
    rebuildIndexes.forEach(function(idx) {
    var key = idx.key;
    var options = {};

    // 复制索引配置(排除系统属性)
    for (var prop in idx) {
    if (!["v", "ns", "background"].includes(prop)) {
    options[prop] = idx[prop];
    }
    }
    options.background = true; // 后台构建索引

    try {
    // 删除旧索引
    var dropTime = formatDateTime();
    printTimedAction(dropTime, `Dropping Index: ${idx.name}`);
    printCommand(`db.${collName}.dropIndex("${idx.name}")`);

    var dropStart = new Date();
    coll.dropIndex(idx.name);
    var dropEnd = new Date();
    printCompletion((dropEnd - dropStart)/1000);

    // 创建新索引
    var createTime = formatDateTime();
    printTimedAction(createTime, `Creating Index: ${idx.name}`);
    printCommand(formatCreateIndexCommand(collName, key, options));

    var createStart = new Date();
    coll.createIndex(key, options);
    var createEnd = new Date();
    printCompletion((createEnd - createStart)/1000);

    } catch (e) {
    print(` └─ ✗ Error: ${e.message}`);
    print(" Skipping this index...\n");
    }
    });
    });

    var endTime = formatDateTime();
    printHeader("Process Completed");
    print(`\n• End Time: ${endTime}`);
  1. 执行脚本

    nohup mongo mongodb://127.0.0.1:27017/admin --quiet reIndex.js >> reIndex_output.log 2>&1 &
    • 执行日志会输出到 reIndex_output.log 文件
  1. 观察日志,等待脚本执行完成即可(日志结尾会输出 Process CompleteEnd Time

  2. 创建 reIndexWithCmd.js 脚本文件,重建部分 HAP 系统依赖索引

    reIndexWithCmd.js 脚本文件容
    // ====================================================================
    // CONFIGURATION
    // ====================================================================
    // 在这里配置您需要重建索引的数据库及其白名单集合
    // 格式: "数据库名": ["要跳过的集合1", "要跳过的集合2..."]
    // 如果某个库下所有集合都需要重建索引,请使用空数组 []
    var targetDatabases = {
    "mdpost": [],
    "MDHistory": []
    };
    // ====================================================================


    // 格式化输出函数 (无需修改)
    function printHeader(text) {
    print("\n" + "=".repeat(100));
    print(text);
    print("=".repeat(100));
    }

    function printSection(text) {
    print("\n" + "-".repeat(80));
    print(text);
    print("-".repeat(80));
    }

    function formatDateTime() {
    var now = new Date();
    var utc8Time = new Date(now.getTime() + (8 * 60 * 60 * 1000));
    return utc8Time.toISOString().replace('Z', '+08:00');
    }

    function formatJSON(obj, indent = 5) {
    if (obj === undefined || obj === null) {
    return "Not available";
    }
    return JSON.stringify(obj, null, 2)
    .split('\n')
    .map((line, i) => i === 0 ? line : ' '.repeat(indent) + line)
    .join('\n');
    }

    function formatFileSize(bytes) {
    if (bytes === undefined || bytes === null) return "N/A";
    return (bytes / 1024 / 1024).toFixed(2) + " MB";
    }

    function printTimedAction(time, action) {
    print(`\n[${time}] ${action}`);
    }

    function printCompletion(seconds) {
    print(` └─ ✓ Completed in ${seconds.toFixed(3)} seconds\n`);
    }

    // ====================================================================
    // SCRIPT EXECUTION
    // ====================================================================

    var overallStartTime = formatDateTime();
    printHeader("MongoDB Multi-Database Index Rebuild Process Started");
    print(`\n• Overall Start Time: ${overallStartTime}`);
    print(`• Databases to Process: ${Object.keys(targetDatabases).join(', ')}`);

    // 遍历配置中的所有数据库
    for (var dbName in targetDatabases) {
    if (targetDatabases.hasOwnProperty(dbName)) {

    printHeader(`Processing Database: [ ${dbName} ]`);

    // 获取目标数据库连接
    var currentDb = db.getSiblingDB(dbName);

    // 获取当前数据库的集合黑名单(即需要跳过的集合列表)
    var excludedCollections = targetDatabases[dbName];

    print(`\n• Target Database: ${currentDb.getName()}`);
    print(`• Excluded Collections: ${formatJSON(excludedCollections)}`);

    // 获取所有集合
    var collections = currentDb.getCollectionNames();
    // 过滤掉黑名单中的集合和系统集合
    var validCollections = collections.filter(collection =>
    !excludedCollections.includes(collection) && !collection.startsWith('system.')
    );

    print(`• Total Collections to Process in this DB: ${validCollections.length}`);

    if (validCollections.length === 0) {
    print("\nNo collections to process in this database. Moving to the next one.");
    continue;
    }

    validCollections.forEach(function(collection, index) {
    try {
    // 获取集合统计信息
    var stats = currentDb[collection].stats();

    // 输出集合信息和进度
    printSection(`Processing Collection [${index + 1}/${validCollections.length}]: ${collection}`);
    print(`\n• Collection Statistics:`);
    print(` ├─ Storage Size: ${formatFileSize(stats.storageSize)}`);
    print(` └─ Document Count: ${(stats.count || 0).toLocaleString()}`);

    // 获取当前索引信息
    var indexes = currentDb[collection].getIndexes();
    print(`\n• Current Indexes (${indexes.length}):`);
    indexes.forEach(function(idx, i) {
    const isLast = i === indexes.length - 1;
    print(` ${isLast ? '└' : '├'}─ Name: ${idx.name.padEnd(20)}`);
    print(` ${isLast ? ' ' : '│'} Key: ${formatJSON(idx.key)}`);
    });

    // 执行 reIndex
    var execTime = formatDateTime();
    printTimedAction(execTime, "Executing reIndex()");
    print(" └─ Execute:");
    print(` db.getSiblingDB('${dbName}').getCollection('${collection}').reIndex()`);

    var startExec = new Date();
    var result = currentDb[collection].reIndex();
    var endExec = new Date();

    if (result.ok === 1) {
    printCompletion((endExec - startExec)/1000);

    print("• Operation Results:");
    print(` ├─ Previous Index Count: ${result.nIndexesWas}`);
    print(` ├─ Current Index Count: ${result.nIndexes}`);

    if (result.operationTime !== undefined) {
    print(` ├─ Operation Time: ${formatJSON(result.operationTime)}`);
    } else {
    print(` ├─ Operation Time: Not available (non-replica set deployment)`);
    }

    if (result.$clusterTime !== undefined) {
    print(` └─ Cluster Time: ${formatJSON(result.$clusterTime)}`);
    } else {
    print(` └─ Cluster Time: Not available (non-replica set deployment)`);
    }

    } else {
    print(` └─ ✗ ReIndex failed: ${formatJSON(result)}`);
    }
    } catch (e) {
    print(` └─ ✗ Error: ${e.message}`);
    print(" Skipping this collection...\n");
    }
    });
    }
    }

    var overallEndTime = formatDateTime();
    printHeader("Process Completed");
    print(`\n• Overall End Time: ${overallEndTime}`);

  1. 执行脚本

    nohup mongo mongodb://127.0.0.1:27017/admin --quiet reIndexWithCmd.js >> reIndexWithCmd_output.log 2>&1 &
    • 执行日志会输出到 reIndexWithCmd_output.log 文件
  1. 观察日志,等待脚本执行完成即可(日志结尾会输出 Process CompleteEnd Time