Carpe Diem

備忘録

MongoDBでindexを整理する際に使ったコマンド

概要

MongoDBで使っていないindexを整理する際に使ったコマンドのチートシートです。

バックアップ用途の吐き出しコマンドとスプレッドシートで確認しやすくするためのコマンドを主に書きます。

環境

  • MongoDB v3.6.23

コマンド

以下にそれぞれのケースでのスクリプトを書きます。実行時は

$ mongo mongodb://{YOUR_HOSTNAME}:27017/{YOUR_DB_NAME} < script.js > output.txt

といった形で行います。

indexの一覧

db.getCollectionNames().forEach(function(collection) {
  indexes = db.getCollection(collection).getIndexes();
  print("Indexes on " + collection + ":");
  printjson(indexes);
});

以下のような出力になります。

Indexes on foobar:
[
        {
                "v" : 1,
                "key" : {
                        "_id" : 1
                },
                "name" : "_id_",
                "ns" : "myDB.foobar"
        },
        {
                "v" : 1,
                "key" : {
                        "expiresAt" : 1,
                        "updatedAt" : -1
                },
                "name" : "expiresAt_1_updatedAt_-1",
                "ns" : "myDB.foobar",
                "background" : true
        }
]

スプレッドシート吐き出し用

db.getCollectionNames().forEach(function(collection) {
  indexes = db.getCollection(collection).getIndexes();
  indexes.forEach(function (index) {
    print(collection + "," + index.name);
  });
});

コレクションごとのインデックスサイズの確認

indexのstats情報から取得します。

db.getCollectionNames().forEach(function(collection) {
  stats = db.getCollection(collection).stats();
  print("DB Stats on " + collection + ":");
  printjson(stats);
});

以下のような出力になります。

DB Stats for foobar:
{
        "sharded" : true,
        "capped" : false,
        "ns" : "myDB.foobar",
        "count" : 3797,
        "size" : 753132,
        "storageSize" : 385024,
        "totalIndexSize" : 774144,
        "indexSizes" : {
                "_id_" : 311296,
                "expiresAt_1_updatedAt_-1" : 86016,
        },
        "avgObjSize" : 198,
        "nindexes" : 4,
        "nchunks" : 1,
        "shards" : {
                "production_1" : {
                        "ns" : "myDB.foobar",
                        "size" : 753132,
                        "count" : 3797,
...

スプレッドシート吐き出し用

indexSizesのみ分かれば良いので以下のように整形します。

db.getCollectionNames().forEach(function(collection) {
  stats = db.getCollection(collection).stats();
  idx = stats.indexSizes;
  Object.keys(idx).forEach(function(key) {
    print(collection + "," + key + "," + idx[key]);
  });
});

インデックスの参照回数

indexが使われているかどうかを確認します。

この値はmongodが再起動するとリセットされます

db.getCollectionNames().forEach(function(collection) {
  indexes = db.getCollection(collection).aggregate({$indexStats:{}});
  print("Indexes on " + collection + ":");
  printjson(indexes._batch);
});

以下のような出力になります。

Indexes for foobar:
[
        {
                "name" : "expiresAt_1_updatedAt_-1",
                "key" : {
                        "expiresAt" : 1,
                        "updatedAt" : -1
                },
                "host" : "mongod-shard001:27000",
                "accesses" : {
                        "ops" : NumberLong(0),
                        "since" : ISODate("2022-01-26T05:11:08.186Z")
                }
        },
        {
                "name" : "_id_",
                "key" : {
                        "_id" : 1
                },
                "host" : "mongod-shard002:27000",
                "accesses" : {
                        "ops" : NumberLong(103),
                        "since" : ISODate("2022-01-26T05:11:08.186Z")
                }
        },
...

accesses.opsが参照回数です。
上記のアウトプットではexpiresAt_1_updatedAt_-1は再起動後参照されていないことが分かります。

accessesはユーザリクエストのみカウントされます

スプレッドシート吐き出し用

accesses.opsが参照回数なので以下のように整形します。
注意としてシャード構成の場合各シャード毎に表示されるので、合算するようにします。

db.getCollectionNames().forEach(function (collection) {
  stats = db.getCollection(collection).aggregate({ $indexStats: {} });
  var list = {};
  stats.forEach(function(index) {
    if (list[index.name]) {
      list[index.name] += index.accesses.ops;
    } else {
      list[index.name] = index.accesses.ops;
    }
  });
  Object.keys(list).forEach(function (key) {
    print(collection + "," + key + "," + list[key]);
  });
});

その他

4.0以前はindexの削除にDBロックがかかる

db.collection.dropIndex() obtains an exclusive lock on the specified collection for the duration of the operation. All subsequent operations on the collection must wait until db.collection.dropIndex() releases the lock.

Prior to MongoDB 4.2, db.collection.dropIndex() obtained an exclusive lock on the parent database, blocking all operations on the database and all its collections until the operation completed.

ref: db.collection.dropIndex() — MongoDB Manual

とあるようにDBロックがかかるので、できれば影響の少ない時間帯に実施しましょう。
ただ十数GBのindexを削除した際は数十msのオーダーだったので、そこまで懸念する必要はないかもです。

まとめ

indexを整理する際のチートシートを残しました。

参考