了解应用程序的动态
1、查看当前操作
发现慢操作的一个简单方法是查看正在运行的操作。速度慢的操作耗时更长,更有可能被发现。虽然并不能完全保证,但这是一个不错的开始,可以看到是什么导致应用程序变慢。
要查看正在运行的操作,可以使用 db.currentOp() 函数:
js
> db.currentOp()
{
"inprog": [{
"type" : "op",
"host" : "eoinbrazil-laptop-osx:27017",
"desc" : "conn3",
"connectionId" : 3,
"client" : "127.0.0.1:57181",
"appName" : "MongoDB Shell",
"clientMetadata" : {
"application" : {
"name" : "MongoDB Shell"
},
"driver" : {
"name" : "MongoDB Internal Client",
"version" : "4.2.0"
},
"os" : {
"type" : "Darwin",
"name" : "Mac OS X",
"architecture" : "x86_64",
"version" : "18.7.0"
}
},
"active" : true,
"currentOpTime" : "2019-09-03T23:25:46.380+0100",
"opid" : 13594,
"lsid" : {
"id" : UUID("63b7df66-ca97-41f4-a245-eba825485147"),
"uid" : BinData(0,"47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=")
},
"secs_running" : NumberLong(0),
"microsecs_running" : NumberLong(969),
"op" : "insert",
"ns" : "sample_mflix.items",
"command" : {
"insert" : "items",
"ordered" : false,
"lsid" : {
"id" : UUID("63b7df66-ca97-41f4-a245-eba825485147")
},
"$readPreference" : {
"mode" : "secondaryPreferred"
},
"$db" : "sample_mflix"
},
"numYields" : 0,
"locks" : {
"ParallelBatchWriterMode" : "r",
"ReplicationStateTransition" : "w",
"Global" : "w",
"Database" : "w",
"Collection" : "w"
},
"waitingForLock" : false,
"lockStats" : {
"ParallelBatchWriterMode" : {
"acquireCount" : {
"r" : NumberLong(4)
}
},
"ReplicationStateTransition" : {
"acquireCount" : {
"w" : NumberLong(4)
}
},
"Global" : {
"acquireCount" : {
"w" : NumberLong(4)
}
},
"Database" : {
"acquireCount" : {
"w" : NumberLong(4)
}
},
"Collection" : {
"acquireCount" : {
"w" : NumberLong(4)
}
},
"Mutex" : {
"acquireCount" : {
"r" : NumberLong(196)
}
}
},
"waitingForFlowControl" : false,
"flowControlStats" : {
"acquireCount" : NumberLong(4)
}
}],
"ok": 1
}
这个命令会显示数据库正在执行的操作列表。以下是输出中一些比较重要的字段。
"opid":操作的唯一标识符。可以使用这个字段来终止操作"active":操作是否正在运行。如果该字段为 false,则意味着操作已经让出或正在等待其他操作交出锁。"secs_running":操作的持续时间(秒)。可以使用这个字段来查找耗时过长的查询。"microsecs_running":操作的持续时间(微秒)。可以使用这个字段来查找耗时过长的查询。"op":操作类型。这个字段通常为"query"、"insert"、"update" 或 "remove"。注意,数据库命令是作为查询处理的。"desc":客户端的标识符。这个字段可以与日志中的消息相关联。在上面的示例中,与连接相关的每个日志消息都会以conn3 作为前缀,因此可以用它来筛选日志以获取相关信息。"locks": 描述操作所获取的锁的类型。"waitingForLock":操作当前是否处于阻塞中并等待获取锁。"numYields":操作释放锁以允许其他操作进行的次数。通常,搜索文档(查询、更新和删除)的任何操作都可能会让出锁。一个操作只有在有其他操作进入队列并等待获取它的锁时才会让出自己的锁。基本上,如果没有操作处于"waitingForLock" 状态,则当前操作不会让出锁。"lockstats.timeAcquiringMicros":操作为了获取锁所花费的时间。
可以通过过滤 currentOp 来查找满足特定条件的操作,比如特定命名空间上的操作,或者已经运行了一定时间的操作。可以通过传入一个查询参数来过滤结果:
js
> db.currentOp(
{
"active" : true,
"secs_running" : { "$gt" : 3 },
"ns" : /^db1\./
}
)
可以使用所有普通的查询运算符对 currentOp 中的任何字段进行查询。
1.1、寻找有问题的操作
db.currentOp() 最常见的用途是查找慢操作。可以使用上一节描述的过滤技术来查找所有耗时超过一定时间的查询,这些查询可能缺少索引或对不适当的字段进行了过滤。
有时我们会发现正在运行着意想不到的查询,这通常是因为某个应用程序服务器运行着旧版本的或者存在漏洞版本的软件。"client" 字段可以帮助跟踪这些不明操作的来源。
1.2、终止操作
如果找到了想要停止的操作,那么可以将 "opid" 作为参数传递给 db.killOp() 来终止它:
js
> db.killOp(123)
并不是所有的操作都能被终止。通常来说,只有当操作让出时,才能终止操作,因此更新、查找和删除操作都可以被终止,但持有或等待锁的操作不能被终止。
当向一个操作发送了"终止"消息后,它在db.currentOp() 的输出中就会有一个 "killed" 字段。然而,只有从当前操作列表中消失,它才会真正被终止。
在 MongoDB 4.0 中,killOP 方法得到了扩展,允许它在mongos 上运行。现在可以终止在集群多个分片中运行的查询(读操作)了。在以前的版本中,此操作需要到每个分片的主节点 mongod 上手动发出终止命令。
1.3、假象
在查找耗时过长的操作时,可能会看到结果中列出的一些长时间运行的内部操作。MongoDB 可能会长时间运行若干请求,这取决于你的设置。最常见的是复制线程(它会尽可能长时间地持续从同步源获取更多操作)和用于分片的回写监听器。任何在 local. oplog.rs 上长时间运行的请求以及任何回写监听命令都可以被忽略。
如果这些操作被终止了,MongoDB 则会重新启动它们。然而,通常来说不应该这样做。终止复制线程会使复制操作短暂地中止,而终止回写监听器可能会导致 mongos遗漏正常的写入错误。
1.4、防止幻象操作
有一个奇怪的、特定于 MongoDB 的问题有可能会遇到,特别是在将数据批量加载到集合中时。假设现在有一个任务是在 MongoDB 中进行上千条更新操作,而 MongoDB 在执行时一度停滞不前。然后你迅速停止了这个任务并终止了当前正在进行的所有更新。然而,在终止了旧的更新后,仍然可以看到新的更新继续出现,即使任务已经不再运行。
如果在加载数据时使用了未确认写入的机制,那么应用程序触发写操作的速度可能比 MongoDB 处理它们的速度更快。如果 MongoDB 中的请求发生了堆积,那么这些写操作将堆积在操作系统的套接字缓冲区中。当终止MongoDB 正在进行的写操作时,就会让 MongoDB 开始处理缓冲区中的写操作。即使客户端停止发送写操作,MongoDB 也会处理那些写入缓冲区的操作,因为它们已经被"接收"了(只是没有被处理)。
防止这些幻像写入的最好方法是执行写入确认机制:让每次写操作都等待,直到前一个写操作完成,而不是仅仅等到前一个写操作处于数据库服务器的缓冲区中就开始下一次写入。
2、使用系统分析器
要查找速度较慢的操作,可以使用系统分析器,它会在一个特殊的 system.profile 集合中对操作进行记录。分析器可以提供大量关于耗时过长操作的信息,但这是有代价的:它会降低 mongod 的整体性能。因此,可能只需要定期打开分析器来捕获一部分流量。如果系统已经负载过重,则建议使用本章描述的另一种技术来对问题进行诊断。
默认情况下,分析器是关闭的,不会记录任何东西。可以在 shell 中运行 db.setProfilingLevel() 来开启分析器:
js
> db.setProfilingLevel(2)
{ "was" : 0, "slowms" : 100, "ok" : 1 }
级别 2 的意思是"记录所有信息"。数据库接收到的每一个读写请求都会被记录在当前数据库的 system.profile集合中。分析器是在数据库粒度上开启的,并会造成严重的性能损失:每次写操作都要花费额外的写入时间,每次读操作都必须等待写锁(因为它必须在 system.profile集合中写入一条记录)。然而,它会提供一个系统正在做什么的详尽清单:
js
> db.foo.insert({x:1})
> db.foo.update({},{$set:{x:2}})
> db.foo.remove()
> db.system.profile.find().pretty()
{
"op" : "insert",
"ns" : "sample_mflix.foo",
"command" : {
"insert" : "foo",
"ordered" : true,
"lsid" : {
"id" : UUID("63b7df66-ca97-41f4-a245-eba825485147")
},
"$readPreference" : {
"mode" : "secondaryPreferred"
},
"$db" : "sample_mflix"
},
"ninserted" : 1,
"keysInserted" : 1,
"numYield" : 0,
"locks" : { ... },
"flowControl" : {
"acquireCount" : NumberLong(3)
},
"responseLength" : 45,
"protocol" : "op_msg",
"millis" : 33,
"client" : "127.0.0.1",
"appName" : "MongoDB Shell",
"allUsers" : [ ],
"user" : ""
}
{
"op" : "update",
"ns" : "sample_mflix.foo",
"command" : {
"q" : {
},
"u" : {
"$set" : {
"x" : 2
}
},
"multi" : false,
"upsert" : false
},
"keysExamined" : 0,
"docsExamined" : 1,
"nMatched" : 1,
"nModified" : 1,
"numYield" : 0,
"locks" : { ... },
"flowControl" : {
"acquireCount" : NumberLong(1)
},
"millis" : 0,
"planSummary" : "COLLSCAN",
"execStats" : { ...
"inputStage" : {
...
}
},
"ts" : ISODate("2019-09-03T22:39:33.856Z"),
"client" : "127.0.0.1",
"appName" : "MongoDB Shell",
"allUsers" : [ ],
"user" : ""
}
{
"op" : "remove",
"ns" : "sample_mflix.foo",
"command" : {
"q" : {
},
"limit" : 0
},
"keysExamined" : 0,
"docsExamined" : 1,
"ndeleted" : 1,
"keysDeleted" : 1,
"numYield" : 0,
"locks" : { ... },
"flowControl" : {
"acquireCount" : NumberLong(1)
},
"millis" : 0,
"planSummary" : "COLLSCAN",
"execStats" : { ...
"inputStage" : { ... }
},
"ts" : ISODate("2019-09-03T22:39:33.858Z"),
"client" : "127.0.0.1",
"appName" : "MongoDB Shell",
"allUsers" : [ ],
"user" : ""
}
可以使用 "client" 字段查看哪些用户向数据库发送了哪些操作。如果使用了身份验证,则还可以看到每个操作是由哪个用户运行的。
通常来说,我们只关心那些较慢的操作,而并不关心数据库正在执行的其他大多数操作。为此,可以将分析级别设置为 1。默认情况下,级别 1 会对耗时超过 100 毫秒的操作进行记录。还可以指定第二个参数,它可以定义"慢"的标准。以下命令会记录所有耗时超过 500 毫秒的操作:
js
> db.setProfilingLevel(1, 500)
{ "was" : 2, "slowms" : 100, "ok" : 1 }
要关闭分析器,可以将分析级别设置为 0:
js
> db.setProfilingLevel(0)
{ "was" : 1, "slowms" : 500, "ok" : 1 }
将 slowms 设置为较低的值通常不是一个好主意。即使分析器处于关闭状态,slowms 也会对 mongod 产生影响:因为它决定了在日志中打印慢速操作的阈值。因此,如果将 slowms 设置为 2,那么每个耗时超过两毫秒的操作都将显示在日志中,即使分析器是关闭的。因此,如果调低了 slowms 来分析某些东西,那么可能需要在关闭分析器之前将它重新调高。
可以使用 db.getProfilingLevel() 来查看当前的分析级别。分析级别不是持久的:重新启动数据库会清除级别的设定值。
也有用于配置分析级别的命令行选项,即 --profile +level 和 --slowms + time,但是提升分析级别通常是临时的调试措施,而不应该长期添加到配置中。
在 MongoDB 4.2 中,通过添加 queryHash 和planCacheKey 字段,分析器条目和诊断日志消息被扩展为读/写操作,以帮助提高对慢查询的识别。queryHash字符串表示查询形状的哈希值,并且仅依赖于查询的形状。每个查询形状都与一个 queryHash 相关联,从而更容易突显出使用相同形状的查询。planCacheKey 是与查询所关联的计划缓存条目中键的哈希值,它包含了查询形状以及该形状当前可用索引的详细信息。这有助于将分析器中的可用信息关联起来,进行查询性能方面的诊断。
如果启用了分析器而 system.profile 集合并不存在,那么 MongoDB 会为其创建一个小的固定集合(几兆字节的大小)。如果想长时间运行分析器,那么这样的方式可能没有足够的空间来记录更多的操作。这时可以关闭分析 器,删除并重新创建一个选定容量的 system. profile 固定集合。然后在数据库上重新启用分析器。
3、计算大小
3.1、文档
获取文档大小的最简单方法是使用 shell 的Object.bsonsize() 函数。传入任何文档以获取其存储在MongoDB 中的大小。
例如,可以看到将 _id 存储为 ObjectId 比将它们存储为字符串更高效:
js
> Object.bsonsize({_id:ObjectId()})
22
> // ""+ObjectId()将ObjectId转换为字符串
> Object.bsonsize({_id:""+ObjectId()})
39
更实用的做法是直接从集合中传入文档:
js
> Object.bsonsize(db.users.findOne())
这个方法可以显示出文档在磁盘上占用了多少字节。不过,这并不包括填充或索引,而二者通常是影响集合大小的重要因素。
3.2、集合
stats 函数可以查看整个集合的信息:
js
>db.movies.stats()
{
"ns" : "sample_mflix.movies",
"size" : 65782298,
"count" : 45993,
"avgObjSize" : 1430,
"storageSize" : 45445120,
"capped" : false,
"wiredTiger" : {
"metadata" : {
"formatVersion" : 1
},
"creationString" : "access_pattern_hint=none,allocation_size=4KB,\
app_metadata=(formatVersion=1),assert=(commit_timestamp=none,\
read_timestamp=none),block_allocation=best,block_compressor=\
snappy,cache_resident=false,checksum=on,colgroups=,collator=,\
columns=,dictionary=0,encryption=(keyid=,name=),exclusive=\
false,extractor=,format=btree,huffman_key=,huffman_value=,\
ignore_in_memory_cache_size=false,immutable=false,internal_item_\
max=0,internal_key_max=0,internal_key_truncate=true,internal_\
page_max=4KB,key_format=q,key_gap=10,leaf_item_max=0,leaf_key_\
max=0,leaf_page_max=32KB,leaf_value_max=64MB,log=(enabled=true),\
lsm=(auto_throttle=true,bloom=true,bloom_bit_count=16,bloom_\
config=,bloom_hash_count=8,bloom_oldest=false,chunk_count_limit\
=0,chunk_max=5GB,chunk_size=10MB,merge_custom=(prefix=,start_\
generation=0,suffix=),merge_max=15,merge_min=0),memory_page_image\
_max=0,memory_page_max=10m,os_cache_dirty_max=0,os_cache_max=0,\
prefix_compression=false,prefix_compression_min=4,source=,split_\
deepen_min_child=0,split_deepen_per_child=0,split_pct=90,type=file,\
value_format=u",
"type" : "file",
"uri" : "statistics:table:collection-14--2146526997547809066",
"LSM" : {
"bloom filter false positives" : 0,
"bloom filter hits" : 0,
"bloom filter misses" : 0,
"bloom filter pages evicted from cache" : 0,
"bloom filter pages read into cache" : 0,
"bloom filters in the LSM tree" : 0,
"chunks in the LSM tree" : 0,
"highest merge generation in the LSM tree" : 0,
"queries that could have benefited from a Bloom filter
that did not exist" : 0,
"sleep for LSM checkpoint throttle" : 0,
"sleep for LSM merge throttle" : 0,
"total size of bloom filters" : 0
},
"block-manager" : {
"allocations requiring file extension" : 0,
"blocks allocated" : 1358,
"blocks freed" : 1322,
"checkpoint size" : 39219200,
"file allocation unit size" : 4096,
"file bytes available for reuse" : 6209536,
"file magic number" : 120897,
"file major version number" : 1,
"file size in bytes" : 45445120,
"minor version number" : 0
},
"btree" : {
"btree checkpoint generation" : 22,
"column-store fixed-size leaf pages" : 0,
"column-store internal pages" : 0,
"column-store variable-size RLE encoded values" : 0,
"column-store variable-size deleted values" : 0,
"column-store variable-size leaf pages" : 0,
"fixed-record size" : 0,
"maximum internal page key size" : 368,
"maximum internal page size" : 4096,
"maximum leaf page key size" : 2867,
"maximum leaf page size" : 32768,
"maximum leaf page value size" : 67108864,
"maximum tree depth" : 0,
"number of key/value pairs" : 0,
"overflow pages" : 0,
"pages rewritten by compaction" : 1312,
"row-store empty values" : 0,
"row-store internal pages" : 0,
"row-store leaf pages" : 0
},
"cache" : {
"bytes currently in the cache" : 40481692,
"bytes dirty in the cache cumulative" : 40992192,
"bytes read into cache" : 37064798,
"bytes written from cache" : 37019396,
"checkpoint blocked page eviction" : 0,
"data source pages selected for eviction unable to be evicted" : 32,
"eviction walk passes of a file" : 0,
"eviction walk target pages histogram - 0-9" : 0,
"eviction walk target pages histogram - 10-31" : 0,
"eviction walk target pages histogram - 128 and higher" : 0,
"eviction walk target pages histogram - 32-63" : 0,
"eviction walk target pages histogram - 64-128" : 0,
"eviction walks abandoned" : 0,
"eviction walks gave up because they restarted their walk twice" : 0,
"eviction walks gave up because they saw too many pages
and found no candidates" : 0,
"eviction walks gave up because they saw too many pages
and found too few candidates" : 0,
"eviction walks reached end of tree" : 0,
"eviction walks started from root of tree" : 0,
"eviction walks started from saved location in tree" : 0,
"hazard pointer blocked page eviction" : 0,
"in-memory page passed criteria to be split" : 0,
"in-memory page splits" : 0,
"internal pages evicted" : 8,
"internal pages split during eviction" : 0,
"leaf pages split during eviction" : 0,
"modified pages evicted" : 1312,
"overflow pages read into cache" : 0,
"page split during eviction deepened the tree" : 0,
"page written requiring cache overflow records" : 0,
"pages read into cache" : 1330,
"pages read into cache after truncate" : 0,
"pages read into cache after truncate in prepare state" : 0,
"pages read into cache requiring cache overflow entries" : 0,
"pages requested from the cache" : 3383,
"pages seen by eviction walk" : 0,
"pages written from cache" : 1334,
"pages written requiring in-memory restoration" : 0,
"tracked dirty bytes in the cache" : 0,
"unmodified pages evicted" : 8
},
"cache_walk" : {
"Average difference between current eviction generation
when the page was last considered" : 0,
"Average on-disk page image size seen" : 0,
"Average time in cache for pages that have been visited
by the eviction server" : 0,
"Average time in cache for pages that have not been visited
by the eviction server" : 0,
"Clean pages currently in cache" : 0,
"Current eviction generation" : 0,
"Dirty pages currently in cache" : 0,
"Entries in the root page" : 0,
"Internal pages currently in cache" : 0,
"Leaf pages currently in cache" : 0,
"Maximum difference between current eviction generation
when the page was last considered" : 0,
"Maximum page size seen" : 0,
"Minimum on-disk page image size seen" : 0,
"Number of pages never visited by eviction server" : 0,
"On-disk page image sizes smaller than a single allocation unit" : 0,
"Pages created in memory and never written" : 0,
"Pages currently queued for eviction" : 0,
"Pages that could not be queued for eviction" : 0,
"Refs skipped during cache traversal" : 0,
"Size of the root page" : 0,
"Total number of pages currently in cache" : 0
},
"compression" : {
"compressed page maximum internal page size
prior to compression" : 4096,
"compressed page maximum leaf page size
prior to compression " : 131072,
"compressed pages read" : 1313,
"compressed pages written" : 1311,
"page written failed to compress" : 1,
"page written was too small to compress" : 22
},
"cursor" : {
"bulk loaded cursor insert calls" : 0,
"cache cursors reuse count" : 0,
"close calls that result in cache" : 0,
"create calls" : 1,
"insert calls" : 0,
"insert key and value bytes" : 0,
"modify" : 0,
"modify key and value bytes affected" : 0,
"modify value bytes modified" : 0,
"next calls" : 0,
"open cursor count" : 0,
"operation restarted" : 0,
"prev calls" : 1,
"remove calls" : 0,
"remove key bytes removed" : 0,
"reserve calls" : 0,
"reset calls" : 2,
"search calls" : 0,
"search near calls" : 0,
"truncate calls" : 0,
"update calls" : 0,
"update key and value bytes" : 0,
"update value size change" : 0
},
"reconciliation" : {
"dictionary matches" : 0,
"fast-path pages deleted" : 0,
"internal page key bytes discarded using suffix compression" : 0,
"internal page multi-block writes" : 0,
"internal-page overflow keys" : 0,
"leaf page key bytes discarded using prefix compression" : 0,
"leaf page multi-block writes" : 0,
"leaf-page overflow keys" : 0,
"maximum blocks required for a page" : 1,
"overflow values written" : 0,
"page checksum matches" : 0,
"page reconciliation calls" : 1334,
"page reconciliation calls for eviction" : 1312,
"pages deleted" : 0
},
"session" : {
"object compaction" : 4
},
"transaction" : {
"update conflicts" : 0
}
},
"nindexes" : 5,
"indexBuilds" : [ ],
"totalIndexSize" : 46292992,
"indexSizes" : {
"_id_" : 446464,
"$**_text" : 44474368,
"genres_1_imdb.rating_1_metacritic_1" : 724992,
"tomatoes_rating" : 307200,
"getMovies" : 339968
},
"scaleFactor" : 1,
"ok" : 1
}
stats 的返回结果中首先是命名空间("sample_mflix.movies"),然后是集合中所有文档的计数。接下来的两个字段与集合的大小有关。如果对集合中的每个元素调用 Object. bsonsize() 并将所有结果相加,就会得到 "size" 的值:它是集合中的文档在未压缩时占用内存的实际字节数。同样,如果将 "avgObjSize"和 "count" 相乘,也可以得到内存中未压缩的 "size"值。
正如前面所提到的,文档字节总数会忽略压缩集合所节省的空间。而 "storageSize" 是一个比 "size" 更小的值,可以反映出压缩所节省的空间。
"nindexes" 是集合中索引的个数。索引在创建完成后才会被计算在 "nindexes" 中,并且只有出现在这个列表中之后才可以被使用。通常,索引会比它们存储的数据量大很多。可以使用右平衡索引来最小化这个空闲空间(参见5.1.2 节)。随机分布的索引通常大约有 50% 的空闲空间,而升序索引会有 10% 的空闲空间。
随着集合不断增长,阅读数十亿字节或更大字节的 stats输出可能会变得困难。因此,可以传入一个缩放因子作为参数:1024 表示千字节,1024*1024 表示兆字节,以此类推。例如,以下命令会以 TB 为单位获取集合的统计数据。
js
> db.big.stats(1024*1024*1024*1024)
3.3、数据库
数据库的 stats 函数与集合类似:
js
> db.stats()
{
"db" : "sample_mflix",
"collections" : 5,
"views" : 0,
"objects" : 98308,
"avgObjSize" : 819.8680982219148,
"dataSize" : 80599593,
"storageSize" : 53620736,
"numExtents" : 0,
"indexes" : 12,
"indexSize" : 47001600,
"scaleFactor" : 1,
"fsUsedSize" : 355637043200,
"fsTotalSize" : 499963174912,
"ok" : 1
}
首先,返回的是数据库的名称、其中的集合数量以及数据库中视图的数量。"objects" 是这个数据库中所有集合的文档总数。
文档中的大部分内容是有关数据大小的信息。"fsUsedSize" 应该总是最大的:它是 MongoDB 实例用于存储数据所占用文件系统中磁盘容量的总和。"fsUsedSize" 表示 MongoDB 当前在该文件系统中使用的总空间。这个值应该对应于数据目录中所有文件所使用的总空间。
第二大的字段通常是 "dataSize",它是该数据库中未压缩数据的大小。这个值不等于 "storageSize",因为数据在 WiredTiger 中通常会被压缩。"indexSize" 是该数据库中所有索引所占用的空间。
db.stats() 可以像集合的 stats 函数一样接收一个缩放因子参数。如果在不存在的数据库上调用 db.stats(),那么这些值都将为零。请记住,在锁占用率较高的系统中列出数据库信息会非常慢,并可能阻塞其他操作。应该尽量避免这样做。
4、使用mongotop和mongostat
MongoDB 附带了一些命令行工具,可以每隔几秒打印一次统计信息来确定它在做什么。
-
`mongotop 类似于 Unix 中的 top 工具:它可以从总体上给出哪些集合最繁忙。还可以运行 mongotop --locks来获取每个数据库的锁统计信息。
-
mongostat提供了整个服务器范围的信息。默认情况下,mongostat 每秒打印一次统计信息列表,不过可以在命令行中传递不同的秒数来对此进行配置。每个字段给出了自上次打印以来操作发生次数的统计。 -
insert/query/update/delete/getmore/command:每种操作发生次数的简单计数。 -
flushes:mongod 将数据刷新到磁盘的次数。 -
mapped:mongod 所映射的内存数量。这大约等于数据目录的大小。 -
vsize:mongod 所使用的虚拟内存数量。这通常是数据目录大小的两倍(一倍用于映射文件,一倍用于记录日志)。 -
res:mongod 正在使用的内存数量。这通常应该尽可能接近机器的所有内存。 -
locked db:在上一个时间片中锁定时间最长的数据库。这个百分比是根据数据库被锁定的时间结合全局锁被持有的时间来计算的,这意味着该值可能超过 100%。 -
idx miss %:导致缺页错误的索引访问百分比(由于要查找的索引条目或索引内容不在内存中,因此 mongod 必须到磁盘中去读取)。这是输出中其名称最让人困惑的字段。 -
qr|qw:读操作和写操作的队列大小(比如有多少读操作和写操作正处于阻塞中,等待被处理)。 -
ar|aw:有多少活跃的客户端(比如当前执行读操作和写操作的客户端)。 -
netIn:MongoDB 计算的网络字节数(可能与操作系统的测量结果不同)。 -
netOut:网络传出的字节数,由 MongoDB 进行统计。 -
conn:此服务器打开的连接数,包括传入的连接数和传出的连接数。 -
time:进行这些统计所花费的时间。
可以在副本集或分片集群上运行 mongostat。如果使用--discover 选项,那么 mongostat 会尝试通过最初连接到的成员查找副本集或分片集群中的所有成员,并针对每台服务器每秒输出一行信息。对于大型集群,这可能难以管理,但对于小型集群可能很有用,而且还可以使用一些工具将输出的信息以更可读的形式进行呈现。
如果想快速了解数据库正在做什么,那么 mongostat 是一个很好的方法。但是对于长期监控,最好使用MongoDB Atlas 或 Ops Manager。