🎉

ひとりMongoDB University 12/13 - データーベースのプロファイリング

2020/12/13に公開

この記録は、アドベントカレンダー形式の、MongoDB Universityの学習コースの記録、13日目になります!
ただいまコース: M103[1]を進めています。目標は12/25までにChapter1の完走!

Chapter 1: Profiling the Database (動画)

データベースの状態を分析するためには、ログからだけでなく、プロファイルを使う方法があります。
今回の動画ではその紹介。

  • ログファイルの特徴
    • データベースのイベントに関する全ての情報が記録される
    • 非常に量が多い&ファイルや標準出力での取得なので、加工をしないと構造的に扱えない
    • データベースが健全かどうかを確認するためのもの
    • どんなオペレーションが行われたかに着目
  • プロファイルは特定の用途に絞ってデータを収集
    • プロファイル用のデータベース&コレクションにデータを溜めることができる
    • どのクエリが遅いのか、といったことを突き止めるためには、プロファイルが有効

プロファイルの取得単位

  • プロセス全体でなく、データベース単位でデータを蓄積する

    • 該当するデータベースごとに、プロファイルデータを保持したコレクションを作成する
    • db.systemというコレクションになる
  • 取得対象

    • CRUDオペレーション
    • 管理上の操作
    • 設定変更・調整の操作
  • プロファイルのレベル

    • 0: 取得しない
    • 1: 標準
    • 2: デバッグレベル。トランザクションの多いデータベースだと非常に大きくなっちゃうので注意!
      • データベースの負荷も上がるので値の設定には注意!

動画に沿ってMongoDBのコンテナで確認

  • Dockerで起動したMongoDBのコンテナ内にアタッチ
  • mongoコマンドでMongo shellを起動
# 既存のデータベースの確認
> show databases
admin   0.000GB
config  0.000GB
local   0.000GB

# 存在しないデータベースであっても、use データベース名 でネームスペースを切り替え
> use newDB
switched to db newDB
> show databases
admin   0.000GB
config  0.000GB
local   0.000GB

# show collectionsと同じ内容を db.runCommand() で実行
# まだコレクションは存在しない (ns = NameSpace: newDB)
> db.runCommand({listCollections: 1})
{
        "cursor" : {
                "id" : NumberLong(0),
                "ns" : "newDB.$cmd.listCollections",
                "firstBatch" : [ ]
        },
        "ok" : 1
}
> db.getProfilingLevel()
0
> db.setProfilingLevel( 1 )
{ "was" : 0, "slowms" : 100, "sampleRate" : 1, "ok" : 1 }
> show collections
system.profile

# 上記と同様に、コレクション表示をdb.runCommand() で実行
> db.runCommand({listCollections: 1})
{
        "cursor" : {
                "id" : NumberLong(0),
                "ns" : "newDB.$cmd.listCollections",
                "firstBatch" : [
                        {
                                "name" : "system.profile",
                                "type" : "collection",
                                "options" : {
                                        "capped" : true,
                                        "size" : 1048576
                                },
                                "info" : {
                                        "readOnly" : false,
                                        "uuid" : UUID("eec9f873-195f-4d8d-a6cc-2cf2c3b3c4c6")
                                }
                        }
                ]
        },
        "ok" : 1
}

> result = db.runCommand({listCollections: 1})
> result.cursor.firstBatch[0].name
system.profile

# JavaScriptが使えるのでチェック
> result.cursor.firstBatch.map((object) => object.name )
[ "system.profile" ]

# レベル調整
> db.setProfilingLevel( 1, { slowms: 0 } )
{ "was" : 1, "slowms" : 100, "sampleRate" : 1, "ok" : 1 }

# 新しいコレクションにデータを挿入してみる(勝手にコレクションは作られます)
> db.new_collection.insert( { "a": 1 } )
WriteResult({ "nInserted" : 1 })

> show collections
new_collection # 追加された
system.profile

# 同じく確認
> result = db.runCommand({listCollections: 1})
> result.cursor.firstBatch.map((object) => object.name )
[ "new_collection", "system.profile" ]

# プロファイル取得を有効にしているので、systemコレクションにデータが出来ているハズ!
> db.system.profile.find().count()
1

# 1件を確認したので、プロファイルを取得してみる
> db.system.profile.find().pretty()
{
        "op" : "insert",
        "ns" : "newDB.new_collection",
        "command" : {
                "insert" : "new_collection",
                "ordered" : true,
                "lsid" : {
                        "id" : UUID("690c46ea-4270-4e79-b63d-f0d8eecb07ea")
                },
                "$db" : "newDB"
        },
        "ninserted" : 1,
        "keysInserted" : 1,
        "numYield" : 0,
        "locks" : {
                "ParallelBatchWriterMode" : {
                        "acquireCount" : {
                                "r" : NumberLong(5)
                        }
                },
                "ReplicationStateTransition" : {
                        "acquireCount" : {
                                "w" : NumberLong(6)
                        }
                },
                "Global" : {
                        "acquireCount" : {
                                "r" : NumberLong(3),
                                "w" : NumberLong(3)
                        }
                },
                "Database" : {
                        "acquireCount" : {
                                "r" : NumberLong(2),
                                "w" : NumberLong(3)
                        }
                },
                "Collection" : {
                        "acquireCount" : {
                                "r" : NumberLong(1),
                                "w" : NumberLong(3)
                        }
                },
                "Mutex" : {
                        "acquireCount" : {
                                "r" : NumberLong(5)
                        }
                }
        },
        "flowControl" : {
                "acquireCount" : NumberLong(3),
                "timeAcquiringMicros" : NumberLong(4)
        },
        "storage" : {

        },
        "responseLength" : 45,
        "protocol" : "op_msg",
        "millis" : 41,
        "ts" : ISODate("2020-12-13T03:01:18.304Z"),
        "client" : "127.0.0.1",
        "appName" : "MongoDB Shell",
        "allUsers" : [ ],
        "user" : ""
}
{
        "op" : "command",
        "ns" : "newDB.system.profile",
        "command" : {
                "count" : "system.profile",
                "query" : {

                },
                "lsid" : {
                        "id" : UUID("690c46ea-4270-4e79-b63d-f0d8eecb07ea")
                },
                "$db" : "newDB"
        },
        "keysExamined" : 0,
        "docsExamined" : 0,
        "numYield" : 0,
        "locks" : {
                "ReplicationStateTransition" : {
                        "acquireCount" : {
                                "w" : NumberLong(2)
                        }
                },
                "Global" : {
                        "acquireCount" : {
                                "r" : NumberLong(2)
                        }
                },
                "Database" : {
                        "acquireCount" : {
                                "r" : NumberLong(1)
                        }
                },
                "Collection" : {
                        "acquireCount" : {
                                "r" : NumberLong(1)
                        }
                },
                "Mutex" : {
                        "acquireCount" : {
                                "r" : NumberLong(1)
                        }
                }
        },
        "flowControl" : {

        },
        "responseLength" : 45,
        "protocol" : "op_msg",
        "millis" : 0,
        "planSummary" : "RECORD_STORE_FAST_COUNT",
        "execStats" : {
                "stage" : "RECORD_STORE_FAST_COUNT",
                "nReturned" : 0,
                "executionTimeMillisEstimate" : 0,
                "works" : 1,
                "advanced" : 0,
                "needTime" : 0,
                "needYield" : 0,
                "saveState" : 0,
                "restoreState" : 0,
                "isEOF" : 1,
                "nCounted" : 1,
                "nSkipped" : 0
        },
        "ts" : ISODate("2020-12-13T03:03:15.211Z"),
        "client" : "127.0.0.1",
        "appName" : "MongoDB Shell",
        "allUsers" : [ ],
        "user" : ""
}

# 検索もしてみる
> db.new_collection.find({ a: 1 })
{ "_id" : ObjectId("5fd583fe3d930029a247d9aa"), "a" : 1 }

# この時のプロファイルも確認
# 操作はquery
#
{
        "op" : "query",
        "ns" : "newDB.system.profile",
        "command" : {
                "find" : "system.profile",
                "filter" : {

                },
                "lsid" : {
                        "id" : UUID("690c46ea-4270-4e79-b63d-f0d8eecb07ea")
                },
                "$db" : "newDB"
        },
        "keysExamined" : 0,
        "docsExamined" : 7,
        "cursorExhausted" : true,
        "numYield" : 0,
        "nreturned" : 7,
        "locks" : {
                "ReplicationStateTransition" : {
                        "acquireCount" : {
                                "w" : NumberLong(2)
                        }
                },
                "Global" : {
                        "acquireCount" : {
                                "r" : NumberLong(2)
                        }
                },
                "Database" : {
                        "acquireCount" : {
                                "r" : NumberLong(1)
                        }
                },
                "Collection" : {
                        "acquireCount" : {
                                "r" : NumberLong(1)
                        }
                },
                "Mutex" : {
                        "acquireCount" : {
                                "r" : NumberLong(1)
                        }
                }
        },
        "flowControl" : {

        },
        "storage" : {

        },
        "responseLength" : 6406,
        "protocol" : "op_msg",
        "millis" : 0,
        "planSummary" : "COLLSCAN", # スキャンの方法とか
        "execStats" : {
                "stage" : "COLLSCAN",
                "nReturned" : 7,
                "executionTimeMillisEstimate" : 0,
                "works" : 9,
                "advanced" : 7,
                "needTime" : 1,
                "needYield" : 0,
                "saveState" : 0,
                "restoreState" : 0,
                "isEOF" : 1,
                "direction" : "forward",
                "docsExamined" : 7
        },
        "ts" : ISODate("2020-12-13T03:09:23.177Z"),
        "client" : "127.0.0.1",
        "appName" : "MongoDB Shell",
        "allUsers" : [ ],
        "user" : ""
}

以上。

Profiling the Database (クイズ)

Problem

Which of the following events are captured by the profiler?

こたえ

  • CRUD operations
  • Administrative operations
  • Configuration operations

ネットワークタイムアウトや、WiredTigerのストレージデータのイベントはプロファイラではなく、ログから。

Lab: Logging to a Different Facility (演習問題)

1 設定を調整して起動

  • ログを /var/mongodb/logs/mongod.log に指定
  • mongod is forked and run as a daemon (デーモンモードで動かすこと)

2 起動したら、テスト接続用のユーザを作るスクリプトを流すこと
3 テストを実行すること

設定

デーモンモードだと、起動を --fork オプションをつけるか、設定に以下のように追記する。

processManagement:
  fork: true

上記を踏まえた設定ファイル。

mongo.conf
storage:
  dbPath: /var/mongodb/db
net:
  bindIp: localhost
  port: 27000
security:
  authorization: enabled
systemLog:
  path: "/var/mongodb/logs/mongod.log"
  destination: "file"
processManagement:
  fork: true

この状態で起動。

bash-4.4# mongod -f mongod.conf
about to fork child process, waiting until server is ready for connections.
forked process: 351
child process started successfully, parent exiting

テストが通りました!

今日の進捗

シェルを使って操作しながら動画を見て進めているので、進度は遅め。でもたぶん理解はなんとかなると思います。
3つほど進みました。Chapter1の残りは、あと9つ。1日3つくらいのペースで終わらせたい!

また、気になっているテストの実行方式。
「Run Test」ボタンを押すとサーバ側にUserIDと対象のテストIDが送信されるみたいです。
結果をJSONで受け取り、フロントエンドで描画(成功なら緑)

ターミナルへの出力は結果を表示する感じ。
次回はもう少しWeb IDEを操作している時の通信を眺めてみよう。

きょうのzenn

しばし同じ方法で進めています!毎日変わる絵文字が楽しい。

脚注
  1. M103: Basic Cluster Administration のコースになります。コースを開始すると、完了までの期限は2ヶ月以内です。 ↩︎

Discussion