MongoDB 数据collection,(表)如果出现数据碎片该怎么办,在MongoDB中存在一个命令可以对表进行碎片整理,这个命令对文档docuemnt在collection中进行重新的组织和排序,使得数据在磁盘上的存储更加连续,
那么问题来了,我怎么判断MongoDB的表,collection存在碎片的问题。这里MongoDB并没有明确的信息直接告知你某个表存在磁盘碎片信息或者百分比之类的,但我们可以通过db.表名.stats() 中的block-manager 来进行判断。
方法1:blocks freed / blocks allocated > 50% 方法2:file bytes available for reuse / file size in bytes > 50%
blocks allocated 是指新分配的块数,blocks freed 是指释放的块数。释放的块表示那些已经不再被使用的数据空间。当blocks freed 越来越多说明有越来越多的数据块不被使用,而blocks allocated 还在持续的快速增长的情况下,说明释放的数据块并未直接被重用,而是继续的分配新的磁盘空间来进行新的数据的存储,所以这就值得惊醒了。
第二个方法是可以进行重用的磁盘空间和总体的磁盘空间的比值,一般可重用的磁盘空间特别大,说明这些都是碎片,且无法利用,同时当文件尺寸较大时这就证明磁盘碎片比较严重了。
我们下面做一个例子,这是一个collection,进行stats()后取的block-manager的信息,这个表非常小,部分情况展示的不是很明显。
"block-manager" : {
"allocations requiring file extension" : 8,
"blocks allocated" : 8,
"blocks freed" : 1,
"checkpoint size" : 4096,
"file allocation unit size" : 4096,
"file bytes available for reuse" : 16384,
"file magic number" : 120897,
"file major version number" : 1,
"file size in bytes" : 36864,
"minor version number" : 0
},
db.runCommand({compact:'cities',force:true})
{
"ok" : 1,
"$clusterTime" : {
"clusterTime" : Timestamp(1639496884, 1),
"signature" : {
"hash" : BinData(0,"xiNUFIQ/RLMeBHYA3GP3XUHQ1Kk="),
"keyId" : NumberLong("7040270890788978692")
}
},
"operationTime" : Timestamp(1639496884, 1)
通过上面的compact 命令来对表进行操作,这里如果在主库进行操作则会必须用到force:true否则不予操作。大家可以观察,下面的图中,在进行命令后,file size in bytes 和 filebytes available for reuse 都变小了。
1 整体的文件尺寸变小了,释放了空间给系统
2 碎片变小了,整体文件变得更紧凑了
通过第一个命令可以达到我们的需求,但需要注意的是数据库的版本,如果你还使用MongoDB4.4以前的版本,那么这里非常不建议进行相关的工作,因为会锁表。
所以对数据库进行此项操作的最低版本是MongoDB 4.4
除此之外,相信网上还有一些知识说,可以使用这个命令,db.runCommand({repairDatabase:1}),说通过repairDatabase的命令可以直接修复数据库,且回收磁盘,重建数据文件。
但这些操作中没有说明,使用这个操作会Down机。具体请看下面的操作,在第二节点执行此命令,从库直接挂掉。且这个节点根本就没有压力,也没有数据量的情况下,这个命令都能让系统直接挂掉。所以网上的东西,不能轻易相信,需要进行尝试,尤其这样的命令,且不可在生产系统上进行操作。
repl:SECONDARY> rs.secondaryOk()
repl:SECONDARY>
repl:SECONDARY>
repl:SECONDARY> db.runCommand({repairDatabase:1})
2021-12-14T16:12:09.749-0500 I NETWORK [js] DBClientConnection failed to receive message from 192.168.198.100:27027 - HostUnreachable: Connection closed by peer
2021-12-14T16:12:09.750-0500 E QUERY [js] uncaught exception: Error: error doing query: failed: network error while attempting to run command 'repairDatabase' on host '192.168.198.100:27027' :
DB.prototype.runCommand@src/mongo/shell/db.js:169:19
@(shell):1:1
2021-12-14T16:12:09.752-0500 I NETWORK [js] trying reconnect to 192.168.198.100:27027 failed
2021-12-14T16:12:09.753-0500 I NETWORK [js] reconnect 192.168.198.100:27027 failed failed
2021-12-14T16:12:09.757-0500 I NETWORK [js] trying reconnect to 192.168.198.100:27027 failed
2021-12-14T16:12:09.757-0500 I NETWORK [js] reconnect 192.168.198.100:27027 failed failed
>
>
>
> exit
bye
2021-12-14T16:12:29.559-0500 I NETWORK [js] trying reconnect to 192.168.198.100:27027 failed
2021-12-14T16:12:29.559-0500 I NETWORK [js] reconnect 192.168.198.100:27027 failed failed
2021-12-14T16:12:29.559-0500 I QUERY [js] Failed to end session { id: UUID("14f45c1d-5c15-4620-bae4-2398fbee8eab") } due to SocketException: socket exception [CONNECT_ERROR] server [couldn't connect to server 192.168.198.100:27027, connection attempt failed: SocketException: Error connecting to 192.168.198.100:27027 :: caused by :: Connection refused]
最后有没有更狠的招数,当然有,比如你在系统中在加入一个节点,然后一个一个更换现有的节点,在数据重新再新节点初始化的过程中,相当于进行了碎片的整理,当然这个过程会比较麻烦且慢,但这也是一个安全的方案,具体的操作就不在这里进行赘述了。