如何监控 MongoDB 的性能

作者选择了 开放式互联网 / 自由言论基金作为 写给捐赠计划的一部分接受捐款。

介绍

监控是数据库管理的一个关键部分,因为它允许您了解数据库的性能和整体健康状况。通过监控数据库的性能,您可以更好地了解其当前容量,观察其工作负载如何随着时间的推移而变化,并计划在数据库开始接近其极限时扩展数据库。

MongoDB配备了您可以使用的各种工具和实用程序来观察数据库的性能。 在本教程中,您将学习如何使用内置的命令和工具来监控数据库指标。

前提条件

要遵循本教程,您将需要:

<$>[注] **注:**有关如何配置您的服务器,安装MongoDB和安全MongoDB安装的链接教程参考Ubuntu 20.04.本教程专注于MongoDB本身,而不是潜在的操作系统。

步骤 1 – 准备测试数据

为了解释如何监控 MongoDB 的性能,此步骤概述了如何打开 MongoDB 壳以连接到本地安装的 MongoDB 实例并在其内创建样本集合。

要创建本指南中使用的样本集合,请连接到 MongoDB 壳作为您的管理用户。本教程遵循前提条件 MongoDB 安全教程的惯例,并假定该管理用户的名称是 AdminSammy,其身份验证数据库是 admin

1mongo -u AdminSammy -p --authenticationDatabase admin

在安装过程中输入密码设置以获取存取壳. 提供密码后,您将看到>提示符号。

<$>[注] 注: 新连接时,MongoDB 壳将默认连接到测试数据库,您可以安全地使用此数据库来实验 MongoDB 和 MongoDB 壳。

或者,您可以切换到另一个数据库以运行本教程中提供的所有示例命令. 要切换到另一个数据库,请运行使用命令,然后是您的数据库名称:

1use database_name

美元

数据库监控在处理小数据集时并不太实用或有用,因为数据库系统只需要扫描给定的任何查询的几个记录。

为此,本指南中的示例是指包含大量文件的名为帐户的样本集合,每个文件代表一个单独的银行账户,具有随机生成的账户余额。

1[label An example bank account document]
2{
3    "number": "1000-987321",
4    "currency": "USD",
5    "balance": 431233431
6}

本示例文件包含以下信息:

  • 数字:此字段代表给定帐户的帐号。在本集合中,每个帐号将有前缀 1000- 随后增加的数字标识符。
  • 货币:此字段表示每个帐户的余额存储在哪种货币中。

不要手动插入大量的文档,您可以执行以下JavaScript代码,同时创建一个名为帐户的集合,并插入一百万这样的文档:

1for (let i = 1; i <= 1000000; ++i) {
2    db.accounts.insertOne({
3        "number": "1000-" + i,
4        "currency": i > 500000 ? "USD" : "EUR",
5        "balance": Math.random() * 100000
6    })
7}

此代码执行一个循环,连续运行一百万次。每次循环迭代时,它在帐户集合中执行一个insertOne()方法,以插入一个新的文档。在每个迭代中,该方法会给由1000-前缀组成的字段一个值,该循环第一次迭代时,这个字段的值将设置为1000-1;最后一次迭代时,它将设置为1000-1000000

货币总是以USD为代表,以数字高于500000的帐户和以EUR为代表,以数字较低的帐户。

<$>[注] 注: 运行此循环可能需要很长时间,甚至超过10分钟。

输出将告知您成功,并返回最后插入的文档的ObjectId:

1[secondary_label Output]
2{
3        "acknowledged" : true,
4        "insertedId" : ObjectId("61a38a4beedf737ac8e54e82")
5}

您可以通过运行无参数的 count() 方法来验证文档是否正确插入,该方法将检索集合中的文档数目:

1db.accounts.count()
1[secondary_label Output]
21000000

在此步骤中,您已成功创建了作为本指南中使用的测试数据的示例文档列表,以解释 MongoDB 提供的性能监控工具。

步骤 2 – 检查服务器使用统计

MongoDB 会自动跟踪一些有用的性能统计数据,定期检查这些数据是监控数据库的一种基本方法. 请注意,这些统计数据不会提供对数据库正在发生什么情况的实时见解,但它们可以帮助确定数据库的性能以及是否有任何即将到来的问题。

<$>[警告] 警告:本指南描述的MongoDB监控命令返回有关您的数据库及其性能的潜在敏感信息。

具体来说,在本步骤中描述的serverStatus()方法以及在下一步中突出的mongostatmongotop命令都需要用户获得clusterMonitor角色才能运行它们。

假设您遵循了关于如何在Ubuntu 20.04上安全MongoDB的前提教程(https://andsky.com/tech/tutorials/how-to-secure-mongodb-on-ubuntu-20-04),并作为您在该指南中创建的管理用户连接到您的MongoDB实例,您将需要授予它这些额外的角色,并跟随本指南中的示例。

首先,切换到用户的身份验证数据库. 在下面的示例中,这是admin,但如果不同,则连接到自己的用户身份验证数据库:

1use admin
1[secondary_label Output]
2switched to db admin

然后运行grantRolesToUser()方法,并在您创建帐户集合的数据库上授予用户clusterMonitor角色以及dbAdmin角色:下面的示例假定帐户集合位于测试数据库中:

1db.grantRolesToUser(
2"AdminSammy",
3[
4"clusterMonitor",
5{ role : "dbAdmin", db : "test" }
6]
7)

请注意,一般认为有专门用于特定目的的用户配置文件更安全。这样,没有用户将有不必要的广泛的权限。

下面的示例创建了一个名为 MonitorSammy的MongoDB用户,并授予他们您需要的角色,以及本教程中的示例,请注意,它还包括readWriteAnyDatabase角色,这将允许该用户读取和写入集群中的任何数据库:

1db.createUser(
2{
3user: "MonitorSammy",
4pwd: passwordPrompt(),
5roles: [ { role : "dbAdmin", db : "test" }, "clusterMonitor", "readWriteAnyDatabase" ]
6}
7)

授予您的用户相应的角色后,返回您的帐户集合存储的数据库:

1use test
1[secondary_label Output]
2switched to db test

美元

首先,通过执行stats()方法来检查总体数据库统计数据:

1db.stats(1024*1024)

此方法的参数(1024*1024)是尺度因子,并告诉 MongoDB 以 Megabytes 返回存储信息。

stats() 方法返回了与当前数据库相关的一些重要统计数据的简短、简要的输出:

 1[secondary_label Output]
 2{
 3        "db" : "test",
 4        "collections" : 3,
 5        "views" : 0,
 6        "objects" : 1000017,
 7        "avgObjSize" : 80.8896048767171,
 8        "dataSize" : 77.14365005493164,
 9        "storageSize" : 24.109375,
10        "indexes" : 4,
11        "indexSize" : 9.9765625,
12        "totalSize" : 34.0859375,
13        "scaleFactor" : 1048576,
14        "fsUsedSize" : 4238.12109375,
15        "fsTotalSize" : 24635.703125,
16        "ok" : 1
17}

此输出提供了该 MongoDB 实例存储的数据的概述. 在此输出中返回的以下密钥可能特别有用:

*对象密钥显示数据库中的文件总数. 您可以使用此值来评估数据库的大小,并在时间的推移中观察到数据库的增长。 *avgObjectSize显示这些文档的平均大小,了解数据库是否在大型和复杂的文档上运行。

通过stats()方法返回的这些信息可以帮助您了解当前存储在您的数据库中的数据量,但它不提供其性能或现有问题的见解。

1db.serverStatus()

此方法的输出很长,提供了大量的服务器使用信息:

 1[secondary_label Output]
 2{
 3        "host" : "ubuntu-mongo-rs",
 4        "version" : "4.4.6",
 5        "process" : "mongod",
 6        "pid" : NumberLong(658997),
 7        "uptime" : 976,
 8        . . .
 9        "ok" : 1
10}

虽然所有这些信息都可能有用,但本指南将专注于三个部分,首先,找到这个输出的连接部分:

 1[secondary_label Output]
 2        . . .
 3        "connections" : {
 4                "current" : 4,
 5                "available" : 51196,
 6                "totalCreated" : 4,
 7                "active" : 2,
 8                "exhaustIsMaster" : 1,
 9                "exhaustHello" : 0,
10                "awaitingTopologyChanges" : 1
11        },
12        . . .

每个数据库服务器一次只能支持这么多连接. 当前键显示当前连接到数据库的客户端数,而可用是数据库可用的剩余未使用的连接数。

大多数应用程序旨在重复使用现有连接,并且不经常打开多个连接,因此,如果没有预期,那么大量连接可能是客户端如何访问服务器的错误配置的令人担忧的迹象。

如果通过执行工作负载的性质预测到大量连接,您可能会考虑将一个或多个分片添加到分裂集群中,以便在多个 MongoDB 实例中分配流量。

接下来,找到输出的globalLock部分,该部分涉及整个数据库服务器的全球锁:

 1[secondary_label Output]
 2        . . .
 3        "globalLock" : {
 4                "totalTime" : NumberLong(975312000),
 5                "currentQueue" : {
 6                        "total" : 0,
 7                        "readers" : 0,
 8                        "writers" : 0
 9                },
10                "activeClients" : {
11                        "total" : 0,
12                        "readers" : 0,
13                        "writers" : 0
14                }
15        },

MongoDB 使用锁定来确保在执行多个操作时数据的一致性,保证两个查询不会同时修改相同的数据。

currentQueue.total 值显示在锁中等待释放的查询数量,以便执行这些查询. 如果这个值高,则意味着数据库的性能受到影响,并且需要更长的时间来完成查询。

这种情况往往源于许多长期的查询,这些查询被锁定,并可能表明索引的非有效使用或不良设计的查询,包括其他可能性。

最后,找到选票人的部分:

1[secondary_label Output]
2        "opcounters" : {
3                "insert" : NumberLong(10000007),
4                "query" : NumberLong(6),
5                "update" : NumberLong(6),
6                "delete" : NumberLong(0),
7                "getmore" : NumberLong(0),
8                "command" : NumberLong(1298)
9        },

这个serverStatus()输出部分可以帮助您了解数据库服务器是否主要用于读取或写入,或者是否使用它是平衡的。

重写的数据库可以受益于通过 sharding的水平扩展,同样,重读的 MongoDB 数据库通常会受益于 复制

这些统计数据可以给出服务器是如何使用的,以及在访问它们时是否存在长排列等性能问题的一般观点,但是,它们不提供有关服务器是如何使用的任何实时信息。

步骤 3 – 使用mongostatmongotop获取实时数据库统计

虽然用于访问 MongoDB 服务器统计数据的命令可以提供回顾服务器如何使用的见解,但它们不能提供当前最活跃的收藏或正在执行的查询类型的实时信息。

MongoDB提供两个实时监控的有用的系统工具,分析数据库活动并不断更新其提供的信息:mongostat和mongotop。mongostat提供了MongoDB实例的当前状态的简要概述,而mongotop跟踪该实例花费多少时间阅读和写作操作。

要使用mongostat,保持当前的 MongoDB 壳连接,并打开另一个终端窗口以访问您的服务器壳。

1[environment second]
2mongostat -u AdminSammy --authenticationDatabase admin

如前所述,mongostat需要高级权限. 如果您已在 MongoDB 实例上启用了身份验证并设置了具有相应角色的用户,那么您将需要通过提供其用户名和身份验证数据库(如本示例所示),然后在被要求时输入其密码来身份验证该用户。

在默认配置中,mongostat在一秒间隔内打印当前执行查询的计数:

1[environment second]
2[secondary_label Output]
3insert query update delete getmore command dirty used flushes vsize res qrw arw net_in net_out conn time
4    *0    *0     *0     *0 0 1|0 0.0% 38.7%       0 1.54G 210M 0|0 1|0 223b 84.4k 7 Nov 28 15:40:40.621
5    *0    *0     *0     *0 0 2|0 0.0% 38.7%       0 1.54G 210M 0|0 1|0 224b 84.8k 7 Nov 28 15:40:41.619
6    *0    *0     *0     *0 0 1|0 0.0% 38.7%       0 1.54G 210M 0|0 1|0 223b 84.5k 7 Nov 28 15:40:42.621
7    *0    *0     *0     *0 0 3|0 0.0% 38.7%       0 1.54G 210M 0|0 1|0 365b 85.0k 7 Nov 28 15:40:43.619

如果mongostat输出显示给定查询类型的值为0,则表示数据库没有运行此类型的操作。

您仍然应该将第一个终端窗口打开并连接到您的 MongoDB 壳,将更多测试文档插入帐户集合,并检查mongostat是否会注意到活动:

1for (let i = 1; i <= 10000; ++i) {
2    db.accounts.insertOne({
3        "number": "2000-" + i,
4        "currency": "USD",
5        "balance": Math.random() * 100000
6    })
7}

这是一个类似于你在步骤 1 中运行的循环的for循环,但这次循环只插入了 10000 个条目。

当新文档被插入时,请检查mongostat输出:

 1[environment second]
 2[secondary_label Output]
 3. . .
 4    *0    *0     *0     *0 0 1|0 0.0% 38.7%       0 1.54G 210M 0|0 1|0 112b 42.5k 4 Nov 28 15:50:33.294
 5    *0    *0     *0     *0 0 0|0 0.0% 38.7%       0 1.54G 210M 0|0 1|0 111b 42.2k 4 Nov 28 15:50:34.295
 6   755    *0     *0     *0 0 1|0 0.1% 38.8%       0 1.54G 210M 0|0 1|0 154k 79.4k 4 Nov 28 15:50:35.294
 7  2853    *0     *0     *0 0 0|0 0.4% 39.1%       0 1.54G 211M 0|0 1|0 585k 182k 4 Nov 28 15:50:36.295
 8  2791    *0     *0     *0 0 1|0 0.7% 39.4%       0 1.54G 212M 0|0 1|0 572k 179k 4 Nov 28 15:50:37.293
 9  2849    *0     *0     *0 0 0|0 1.0% 39.7%       0 1.54G 213M 0|0 1|0 584k 182k 4 Nov 28 15:50:38.296
10   745    *0     *0     *0 0 2|0 1.1% 39.8%       0 1.54G 213M 0|0 1|0 153k 79.2k 4 Nov 28 15:50:39.294
11    *0    *0     *0     *0 0 0|0 1.1% 39.8%       0 1.54G 213M 0|0 1|0 111b 42.2k 4 Nov 28 15:50:40.295
12    *0    *0     *0     *0 0 2|0 1.1% 39.8%       0 1.54G 213M 0|0 1|0 167b 42.7k 4 Nov 28 15:50:41.293
13. . .

插入列中显示向数据库插入新数据的查询数量,这些值在几秒钟内较高。由于mongostat在一秒钟间隔内显示数据,您不仅可以找到与其他类型的数据库操作相关的插入比例,而且还可以找到数据库插入新数据的速度。

您可以使用mongotop来监控数据库服务器的当前工作负载,按查询类型组合。

停止mongostat在你的第二个终端窗口中运行,点击CTRL + C。然后在同一个终端中运行mongotop

1[environment second]
2mongotop -u AdminSammy --authenticationDatabase admin

mongotop输出数据库中所有集合的列表,并伴随着阅读、写作和时间窗口内的总时间。

 1[environment second]
 2[secondary_label Output]
 32021-11-28T15:54:42.290+0000 connected to: mongodb://localhost/
 4
 5                    ns total read write 2021-11-28T15:54:43Z
 6    admin.system.roles 0ms 0ms 0ms
 7  admin.system.version 0ms 0ms 0ms
 8config.system.sessions 0ms 0ms 0ms
 9   config.transactions 0ms 0ms 0ms
10  local.system.replset 0ms 0ms 0ms
11         test.accounts 0ms 0ms 0ms
12
13. . .

尝试将更多文档插入数据库,看看活动是否在mongotop中注册。在MongoDB壳中,执行以下for循环;在这样做之后,观察mongotop运行的终端窗口:

1for (let i = 1; i <= 10000; ++i) {
2    db.accounts.insertOne({
3        "number": "3000-" + i,
4        "currency": "USD",
5        "balance": Math.random() * 100000
6    })
7}

这一次,活动将在mongotop统计数据中可见:

 1[environment second]
 2[secondary_label Output]
 3. . .
 4                    ns total read write 2021-11-28T15:57:27Z
 5         test.accounts 127ms 0ms 127ms
 6  admin.$cmd.aggregate 0ms 0ms 0ms
 7    admin.system.roles 0ms 0ms 0ms
 8  admin.system.version 0ms 0ms 0ms
 9config.system.sessions 0ms 0ms 0ms
10   config.transactions 0ms 0ms 0ms
11  local.system.replset 0ms 0ms 0ms
12
13                    ns total read write 2021-11-28T15:57:28Z
14         test.accounts 130ms 0ms 130ms
15  admin.$cmd.aggregate 0ms 0ms 0ms
16    admin.system.roles 0ms 0ms 0ms
17  admin.system.version 0ms 0ms 0ms
18config.system.sessions 0ms 0ms 0ms
19   config.transactions 0ms 0ms 0ms
20  local.system.replset 0ms 0ms 0ms
21. . .

在这里,mongotop显示,所有数据库活动发生在测试数据库中的帐户集合中,并且时间窗口中的所有操作都是写操作。

mongostat一样,您可以通过按CTRL + C来阻止mongotop运行。

当在峰值负载期间观察到时,您可以使用mongotop来监控数据库活动如何在不同集合中传播,以帮助您更好地了解扩展的方案和计划。

步骤 4 — 使用 MongoDB 数据库配置文件来识别缓慢查询

虽然规模化数据库(横向或垂直)往往是解决性能瓶颈的解决方案,但其原因可能不是数据库的局限性,而是与方案或查询设计有关的问题。

如果查询运行时间过长,原因可能是查询本身中的索引或错误的无效使用。

您可能会通过手动执行测试查询并检查哪些测试查询表现不佳来找到罪魁祸首,尽管这将是非常无聊的。

MongoDB 的数据库配置文件可以记录查询和执行情况的统计数据,当它们符合某些条件时,这些条件中最重要的是查询的执行时间:如果查询需要更长的时间来执行,则配置文件会自动将查询标记为有问题的。

在使用配置文件之前,请执行以下查询. 此查询将检索您插入的帐户之一,尽管它可能看起来并不那么简单:

1db.accounts.find({"number": "1000-20"})

该命令将检索您要求的确切帐户:

1[secondary_label Output]
2{ "_id" : ObjectId("61a38fd5eedf737ac8e54e96"), "number" : "1000-20", "currency" : "EUR", "balance" : 24101.14770458518 }

您可能已经注意到,该查询并未立即执行,MongoDB 需要一两分钟才能找到该帐户. 在现实世界的应用程序中,可能有很多类型的查询表现不佳,并且在实践中您可能没有注意到它们的低性能。

您可以配置 MongoDB 以帮助您确定哪些查询需要比预期更长时间。

1db.setProfilingLevel(1, { slowms: 100 })

setProfilingLevel() 方法需要两个参数. 首先是配置级别,可以是 012:

  • 0 禁用配置文件
  • 1 仅在满足条件的缓慢查询时启用配置文件
  • 2 启用所有查询

在此示例中,分析器将分析运行超过 100 毫秒的查询,根据第二个参数 { slowms: 100 } 定义。

<$>[注] **注:**使用配置文件会降低性能,因为MongoDB现在必须分析查询,并执行查询。

您可以通过配置为仅配置特定百分比的查询或按查询类型进行过滤来进一步定制所登录的查询子集。

此方法會返回成功訊息:

1[secondary_label Output]
2{ "was" : 0, "slowms" : 100, "sampleRate" : 1, "ok" : 1 }

从现在开始,数据库配置将被启用,MongoDB将积极监控您执行的每个查询,以找到需要超过100毫秒的时间完成的查询。

尝试执行几个不同的查询,首先使用计数命令来查找帐户集合中的文档数量:

1db.accounts.count()

此命令将快速返回集合中的文档数量:

1[secondary_label Output]
21020000

然后,尝试查看收藏中出现的前三个银行账户:

1db.accounts.find().limit(3)

再一次,数据库将快速返回结果:

1[secondary_label Output]
2{ "_id" : ObjectId("61ef40640f2ba52efc56ee17"), "number" : "1000-1", "currency" : "EUR", "balance" : 25393.132960293842 }
3{ "_id" : ObjectId("61ef40640f2ba52efc56ee18"), "number" : "1000-2", "currency" : "EUR", "balance" : 63629.42056192393 }
4{ "_id" : ObjectId("61ef40640f2ba52efc56ee19"), "number" : "1000-3", "currency" : "EUR", "balance" : 75602.12331602155 }

最后,再次运行特定银行账户的搜索查询:

1db.accounts.find({"number": "1000-20"})

此查询将返回结果,但与之前一样,比以前的操作需要更长的时间或更长的时间:

1[secondary_label Output]
2{ "_id" : ObjectId("61a38fd5eedf737ac8e54e96"), "number" : "1000-20", "currency" : "EUR", "balance" : 24101.14770458518 }

虽然查询速度明显较慢,但配置文件不会产生任何输出,相反,有关缓慢操作的详细信息会被记录在名为system.profile的数据库内的一个特殊集合中,该集合是一个覆盖的集合,规模永远不会超过1 MB,这意味着它将始终包含最新的缓慢查询的列表。

要获取由配置文件识别的查询信息,您必须以以下方式查询system.profile集合:

1db.system.profile.find().sort({ "ts" : -1 }).pretty()

此查询使用find()方法,如往常一样。 它还包括一个包含{"ts" : -1 }作为参数的sort条款。 这将首先对最新查询设置的结果进行排序。

每个缓慢查询都被表示为一个常规的文档,而system.profile就像任何常规的集合一样,这意味着您可以过滤结果,排序它们,甚至在汇总管道中使用它们来进一步缩小或分析配置器识别的查询列表。

请注意,结果仅由一个文件组成,另外两个查询执行得足够快,以免触发配置文件:

 1[secondary_label Output]
 2{
 3        "op" : "query",
 4        "ns" : "test.accounts",
 5        "command" : {
 6                "find" : "accounts",
 7                "filter" : {
 8                        "number" : "1000-20"
 9                },
10                . . .
11        },
12        "nreturned" : 1,
13        "keysExamined" : 0,
14        "docsExamined" : 1030000,
15        . . .
16        "millis" : 434,
17        "planSummary" : "COLLSCAN",
18        . . .
19}

此输出提供了有关缓慢查询的执行的一些细节:

  • 联合国 " op " 键表明这一信息代表了何种操作。 这里,它是一个query',因为它代表了一个操作,在这个操作中,你利用find()'从数据库取回数据。
  • `ns'键表示哪些数据库和收藏参与了操作。 如产出所示,这项行动询问 " 测试 " 数据库中的 " 账户 " 收集情况。
  • command' key 提供了查询本身的进一步信息。 在这种情况下, " filter " 子键包含整个过滤文件。 使用来自opcommand`字段的信息,您可以重建相关的查询。
  • 在 " millis " 字段中,你会找到完成查询的确切时间。 在这个例子中,几乎只有半秒。
  • docsExcined'字段提供扫描后返回结果集的文件数量。 *未返回 ' 表示返回的查询文件数量。 在这个例子中,在100多万个扫描器中,只有一个文件被退回。
  • PlanSummary' 显示用于执行查询的MongoDB方法。 COLLSCAN ' 相当于全面收集扫描,即它逐一浏览收集中的每一份文件,以找到匹配的银行账户。 (英语)

总的来说,这些信息强调了一个索引的必要性,可以帮助MongoDB更快地执行这个查询。数据库必须查看整个集合以找到一个单一的文档,这表明审查和返回文档数量之间的巨大差异,以及执行策略。

在此特定示例中,创建一个索引以支持基于数字字段过滤数据的查询将为此类查询的性能提供即时提升。

要完成配置文件会话,您可以通过将配置文件级别设置为零来禁用配置文件:

1db.setProfilingLevel(0)

操作将成功通过确认消息:

1[secondary_label Output]
2{ "was" : 1, "slowms" : 100, "sampleRate" : 1, "ok" : 1 }

现在数据库恢复正常运作,没有幕后进行配置。

每当您怀疑缓慢查询可能会对数据库的性能产生负面影响时,您可以使用数据库配置文件来找到它们并更好地了解它们的结构和如何执行。

结论

通过遵循本指南,您了解如何找到MongoDB的服务器统计数据,以及如何使用mongotop,mongostat等诊断工具,以及MongoDB的数据库配置机制,您可以使用这些工具更好地了解您的数据库的工作负载,确定哪些集合最活跃,以及服务器是否主要执行写作或读取。

这些只是您可以使用的工具和技术的选择,以监控您的 MongoDB 安装的健康和性能,并对其采取行动. 这些工具可以进一步配置和定制,以便为您提供更有针对性的服务器性能见解。

Published At
Categories with 技术
comments powered by Disqus