说明
针对搭建的容器集群进行一些分析,问题的表现是存储的空间过大(和单机Mongo对比)。内容牵扯的比较多,所以我就按处理问题的思路一步步写。
内容
针对相同的数据集
集群空间现状:
单机版现状:
把配置服务器,副本和仲裁都不算,单是两个分片加起来,数据量是 2.3G, 几乎是单机的2.4倍。
1 Compact
关于Compact可以参考这篇文章,因为以前也发生过,所以我想是否是因为Balance后开启导致的物理空间不释放。
Mongos不可以执行Compact操作,要到对应分片的主节点操作
docker exec -it shardsvr10 bash
# 估计一下执行Compact会带来的收益
db.test.stats().wiredTiger["block-manager"]["file bytes available for reuse"]
377339904(大约300M,其实可以stats(1024*1024)这样直接就是M了 )
# 执行compact, 对应的集合会阻塞访问
db.runCommand({compact:'test', force:true})
释放只会,shard1变为1.2G,所以总体上数据是2G,是单机版的两倍
结论:Compact不是导致集群存储空间大的主要原因
2 查看数据库大小
之前分别打开过集群和单机各个分片的数据库描述,结论是关于核心数据的表大小,两边也是一致的
rs_shardsvr1:PRIMARY> db.test.stats()
{
"ns" : "test.test",
"size" : 637257701,
"count" : 674086,
"avgObjSize" : 945,
"storageSize" : 619929600,
"freeStorageSize" : 377339904,
"capped" : false,
然后我用show dbs,发现集群的local特别大而单机版的local为0:
shard1的
rs_shardsvr1:PRIMARY> show dbs;
admin 0.000GB
config 0.001GB
local 0.553GB
test 0.608GB
单机的
> show dbs;
admin 0.000GB
config 0.000GB
local 0.000GB
test 0.442GB
其中单机teset比较小的原因我猜是因为启动时并未使用配置文件,关于test库的一些相关大小没有统计,而是归于共享的部分。
tips
如果没有挂载配置,那么mongod会使用默认的配置启动/etc/mongod.conf.orig
,长这个样子
# mongod.conf
# for documentation of all options, see:
# http://docs.mongodb.org/manual/reference/configuration-options/
# Where and how to store data.
storage:
dbPath: /var/lib/mongodb
journal:
enabled: true
# engine:
# wiredTiger:
# where to write logging data.
systemLog:
destination: file
logAppend: true
path: /var/log/mongodb/mongod.log
# network interfaces
net:
port: 27017
bindIp: 127.0.0.1
# how the process runs
processManagement:
timeZoneInfo: /usr/share/zoneinfo
#security:
#operationProfiling:
#replication:
#sharding:
## Enterprise-Only Options:
#auditLog:
#snmp:
而本次集群启动时挂载(… mongo -f /etc/mongod.conf)的配置长这样:
# Where and how to store data.
storage:
dbPath: /home/mongod/db #分片数据库路径
journal:
enabled: true
directoryPerDB: true
# engine:
# mmapv1:
# wiredTiger:
# where to write logging data.
systemLog:
destination: file
logAppend: true
path: /home/mongod/log/mongod.log # 分片日志
# network interfaces
net:
bindIpAll: true
# how the process runs
#processManagement:
# fork: true
# set auth?
setParameter:
enableLocalhostAuthBypass: false
#operationProfiling:
replication:
replSetName: rs_shardsvr1 #分片服务名称
sharding:
clusterRole: shardsvr
其中directoryPerDB
参数会按数据库区分,其中一个replication
参数后续会讨论到。
言归正传,发现集群的shard,local库占据了几乎和数据一样大的空间,查看里面的内容如下
rs_shardsvr1:PRIMARY> use local
switched to db local
rs_shardsvr1:PRIMARY> show collections;
oplog.rs
replset.election
replset.initialSyncId
replset.minvalid
replset.oplogTruncateAfterPoint
startup_log
system.replset
system.rollback.id
system.tenantMigration.oplogView
system.views
可以看到,oplog
相关的表特别大, 存储接近565M,基本上和dbs的空间对上了(0.553G),有点小差距不知道是为啥。
rs_shardsvr1:PRIMARY> db.oplog.rs.dataSize()
1631615139
rs_shardsvr1:PRIMARY> db.oplog.rs.storageSize()
593305600
所以问题出在oplog上,关于这个可以参考
参考1参考2,里面有提到 oplog是Replica set模式中的核心部分,通过它来同步不同服务器之间的数据,以日志的形式保存数据,默认是采用空闲空间的5%来存储,也就是说,如果你又100G的空间,他默认会划分5G来存储这个部分的日志空间,不过这个参数是可以直接设置的。如果超过你直接设置的百分比,他会反复使用,不会新增空间。参考3如何去设置oplog
我查看了一下当前机器分配的大小(26G), 目前空闲的空间大约515G,差不多。
rs_shardsvr1:PRIMARY> db.getReplicationInfo()
{
"logSizeMB" : 26518.6025390625,
"usedMB" : 1556.01,
"timeDiff" : 71040,
"timeDiffHours" : 19.73,
"tFirst" : "Tue Jun 28 2022 07:36:24 GMT+0000 (UTC)",
"tLast" : "Wed Jun 29 2022 03:20:24 GMT+0000 (UTC)",
"now" : "Wed Jun 29 2022 03:20:26 GMT+0000 (UTC)"
}
rs_shardsvr1:PRIMARY> db.printReplicationInfo()
configured oplog size: 26518.6025390625MB
log length start to end: 71080secs (19.74hrs)
oplog first event time: Tue Jun 28 2022 07:36:24 GMT+0000 (UTC)
oplog last event time: Wed Jun 29 2022 03:21:04 GMT+0000 (UTC)
now: Wed Jun 29 2022 03:21:09 GMT+0000 (UTC)
参考3其中有提到,在配置文件中指定大小(也就是启动时指定的那个文件)
#配置文件中指定oplog大小
replication:
oplogSizeMB: 4096
replSetName: repset
当然,后续的实验我计划会给容器限制内存、磁盘,所以不设置没关系。但是oplog不能太小,否则会出问题。
结论:oplog解释了翻倍的原因,而且随着业务增长,不会出现一直翻倍的情况。
3 数据整体还是比数据大一倍
其实可以看到,数据库文件中,journal的体积和数据文件(440M)差不多大小
journal文件存储的是对数据库文件(dbname.ns、dbname.<#>系列文件)的修改日志,包括写操作和创建文件操作。对数据库文件的写操作会记录一个WriteIntent,创建数据库文件会记录一个DurOp。WriteIntent记录了写操作的指针和长度,可以定位到修改的数据文件的位置和长度。DurOp由一个操作码来确定是什么操作,不同的操作,日志的格式不一样。每个WriteIntent或者DurOp都会形成一个JEntry。
简单来说,journal有两个作用,一方面用于数据故障保护,另一方面似乎是可以驻留内存中加速的。
总体上疑问解开了