为了监视MongoDB的慢查询,我开发了一个Mackerel插件
首先
我制作了一个能够可视化MongoDB慢查询数量的mackerel插件。
使用此插件,可以通过图表显示每分钟慢查询的数量。
获取慢查询的方法
要在MongoDB中获取慢查询数量,应该怎么做呢?
通过在MongoDB中启用Database Profiler,可以收集CRUD操作的信息并保留在system.profile集合中。
在配置文件(官方文档)中,有以下的性能分析器设置值。
slowms
を超えたオペレーションだけ収集。2allすべてのオペレーションを収集。我們將設定監視慢查詢數量的一級警戒級別,並配上適當的閾值。
db.setProfilingLevel(1, { slowms: 50 })
如果使用配置文件进行配置(根据官方文档),应按以下方式设置。
operationProfiling:
mode: slowOp
slowOpThresholdMs: 50
通过这个设置,超过50毫秒的操作将被保存在“system.profile“集合中。
如果您想获取慢查询的详细信息,请使用以下查询。
db.system.profile.find()
这次的目的是通过mackerel插件来可视化慢查询数,所以使用以下的查询来计算最近1分钟的慢查询数(实际上是通过MongoDB的Go驱动程序mgo获取)。
db.system.profile.count({"ts" : { "$gt" : new Date(new Date() - 60 * 1000) } })
【注意】系统配置档案是一个限制收集的集合。
system.profile 默认是一个大小为1MB的固定集合(Capped Collection)。当集合大小超过1MB时,旧的文档会自动删除。根据官方文档,通常可以保存几千个操作信息,但在我的环境中大约只能保存1,000个(取决于每个操作的数据长度)。
换句话说,我们在这个插件中计算的是最近一分钟的查询次数,但是如果在一分钟内发生了大约几百到几千次的慢查询,那么无法正确地获取数值。
根据情况和个人偏好,可以考虑以下解决方案,请自行选择。
-
- 提高阈值,抑制慢查询的发生数量,修改system.profile的大小(官方文档)。
如果步骤过于复杂(特别是如果将secondary也纳入考虑),要注意不要太大,以免增加对system.profile的查询负担。
可以通过sampleRate选项来调整需要进行profile的操作的比例。
MongoDB 3.6及更高版本必需。虽然如此,慢查询数目看起来会比较少。
顺便提一句,已经有关于允许通过配置文件更改system.profile大小的工单了,希望有人能够来实施一下呢。
制作一款鲭鱼插件。
我找到了获取慢查询数量的方法,现在只需要编写Mackerel插件了。
如果按照以下文档的指示,就能符合 Mackerel 插件的规格要求。
请提供以下链接对应的中文版本:
因为之前几乎没有写过Go,并且从未使用过ghr,所以我在以下几个方面遇到了困难。
-
- Goプロジェクトを$GOPATH/src以下に配置しないとgoxzが動かない
- ghrの以下のエラーで小一時間ハマっていたがGithub Tokenのパーミッションが不十分なだけだった
Failed to create GitHub release page: failed to create a release: POST https://api.github.com/repos/rinmu/mackerel-plugin-mongodb-slow-queries/releases: 404 Not Found []
代码说明
由于整体上符合go-mackerel-plugin的接口,因此在这里只解释获取慢查询数量部分。
session, err := mgo.Dial(m.URL)
if err != nil {
return nil, err
}
session.SetMode(mgo.Nearest, true)
collection := session.DB(m.Database).C("system.profile")
one_minute_ago := time.Now().Add(time.Duration(-1) * time.Minute)
count, err := collection.Find(bson.M{"ts": bson.M{"$gt": one_minute_ago}}).Count()
if err != nil {
return nil, err
}
获取会话
session, err := mgo.Dial(m.URL)
サードパーティ製のMongoDBドライバmgoを使う。
公式のmongo-go-driverを使いたいけどまだベータ版ぽいので保留。
read preferenceを指定
session.SetMode(mgo.Nearest, true)
レプリカセットでの運用を想定してread preferenceを指定している。
Nearestを指定するとprimary/secondaryに関係なく最も近い(ネットワークレイテンシの低い)メンバーから取得する。
【补充】关于system.profile
system.prifileはレプリカセットの各メンバーが個別に保有するコレクションであり、レプリケーションされない。system.profileはそのメンバーに対して実行されたオペレーション情報を保持するのでこれは当然である。
Nearestを指定することにより、各メンバー(サーバー)上でpluginを実行することで、そのメンバーに対して発行されたスロークエリ数を取得できる。
スロークエリ数を取得
collection := session.DB(m.Database).C("system.profile")
one_minute_ago := time.Now().Add(time.Duration(-1) * time.Minute)
count, err := collection.Find(bson.M{"ts": bson.M{"$gt": one_minute_ago}}).Count()
与前面的查询相同。
用法
检查性能分析器的设置。如果已被禁用,请启用它。
> db.getProfilingStatus()
{ "was" : 0, "slowms" : 100, "sampleRate" : 1 }
> db.setProfilingLevel(1, {slowms: 50})
{ "was" : 0, "slowms" : 100, "sampleRate" : 1, "ok" : 1 }
> db.getProfilingStatus()
{ "was" : 1, "slowms" : 50, "sampleRate" : 1 }
如果没有MKR,请安装(官方文档)。
使用mkr工具安装mackrel-plugin-mongodb-slow-queries插件。
sudo mkr plugin install rinmu/mackerel-plugin-mongodb-slow-queries
在配置文件中添加内容。
[plugin.metrics.mongodb-slow-queries]
command = "/path/to/mackerel-plugin-mongodb-slow-queries -database=your_database_name"
重新加载mackerel-agent
sudo /etc/init.d/mackerel-agent reload
感受到的情绪
-
- Goでの開発が初めてだったためハマりどころはあったが、mackerelの公式ページとヘルパーライブラリが充実しているのでプラグイン開発〜公開のハードルは低いと感じた。
- スロークエリ数の他に、全クエリの平均実行時間も可視化したかった。が、MongoDBのDatabase Profilerの仕様上、レベルを2にして平均値を取ることになるため、スロークエリのみ取得のレベル1と両立できない。しきい値をプラグイン側に持たせてクエリで絞ることで両立は可能だが、system.profileのサイズ上限とクエリの実行時間に懸念があったのでひとまず断念した。