背景
有一张1T+的大集合,集合记录数不多,70万条,放任的情况下,他居然给我爆盘(94%)了
调查研究发现,其实它存储的都是临时数据,保留最近一个月的记录即可,那按日期保留最后一个月就好
PS:若非临时数据,就要另当别论了。。
那问题来了,日期没有索引,那么大的量的情况,怎么办呢?
本身磁盘就快爆了,我还给大集合加索引么?显然异想天开,而且性能上也很难支撑呀;
通过研究发现,其实它的_id大有所为,他们有个特点,值只会越来越大,通过搜索不断的尝试,我们找到了近一个月比较贴合的一个ID值
由此开始,可进行下一步了,当然操作之前,申请停机及停应用是必不可少的环节
【操作之前:一定要反复确认及提前拉数据出来测试及验证,并连通应用一起测试。。。数据处理无小事,谨慎再谨慎!!!】
集合说明:
其中,original_collection
是原始集合的名称,date_field
是存储日期的字段的名称,new_collection
是新集合的名称。
1. 连接到mongoDB
-- 连接到mongoDB
#./mongo
-- 查询有哪些数据库
>show dbs
admin 1083.340GB
config 0.000GB
local 0.000GB
-- 切换到包含原始集合的数据库
>use admin
switched to db admin
-- 查看集合,确定处理的集合名称
> show tables
original_collection
system.users
system.version
templateInfoDo
2. 查询集合状况
> db.original_collection.stats()
{
"ns" : "admin.original_collection",
"size" : 1099377579724,
"count" : 659944,
"avgObjSize" : 1665864,
"storageSize" : NumberLong("1111568564224"),
"capped" : false,
"wiredTiger" : {
"metadata" : {
"formatVersion" : 1
......省略部分
}
stats()字段说明:
ns: 集合名字
size:集合大小,以KB计算
count:集合的记录数
avgObjSize:对象的平均大小
当然在最后还有索引相关信息,再细的就不做介绍了
-- 查询索引
> db.original_collection.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "admin.original_collection"
},
{
"v" : 2,
"key" : {
"status" : 1,
"isDeleted" : -1
},
"name" : "status_1_isDeleted_-1",
"ns" : "admin.original_collection"
}
]
PS:其中,第一个索引是默认创建的 _id
索引,第二个索引是名为 status_1_isDeleted_-1
的自定义索引,它包含 status
和 isDeleted
两个字段,并且 isDeleted
按升序排序,isDeleted
按降序排序
3. 查找_id最合适的值,并按当前值评估需要保留的数据量
首先查询最后一个集合的ID
-- mongoDB查询最后一条记录
> db.original_collection.find({},{"_id" :1,"lastModifiedTime":1}).sort({_id:-1}).limit(1)
{ "_id" : NumberLong("1640585788108308480"), "lastModifiedTime" : ISODate("2023-03-28T05:25:07.206Z") }
PS:这条语句使用了 sort()
函数按照 _id
字段进行降序排序,并使用 limit()
函数限制结果集只返回一条记录,即最后一条记录。请将 original_collection
替换为你要查询的集合名称。
通过这个ID,评估找出近一个月大概的位置
> db.original_collection.find({"_id" :{$gte: NumberLong("1630094934115943888")}},{"_id" :1,"lastModifiedTime":1}).sort({_id:1}).limit(1)
{ "_id" : NumberLong("1630094991093137408"), "lastModifiedTime" : ISODate("2023-02-27T06:38:26.286Z") }
4. 新增集合存放需要保留的数据(这边看集合的大小,需要花费一定的实际,我这边新集合40G+,用时5分钟)
> db.original_collection.aggregate([
... { $match: { "_id": { $gte: NumberLong("1630094934115943888") } } },
... { $out: "new_collection" }
... ])
5.验证数据
> db.new_collection.stats()
{
"ns" : "admin.new_collection",
"size" : 46893315614,
"count" : 31971,
"avgObjSize" : 1466745,
"storageSize" : 46799765504,
"capped" : false,
"wiredTiger" : {
"metadata" : {
"formatVersion" : 1
} ....省略部分....
"nindexes" : 1,
"totalIndexSize" : 520192,
"indexSizes" : {
"_id_" : 520192
},
"ok" : 1
}
PS:集合的数量是正确的,但是索引只有默认的_id索引,还有另外一个索引没有,需要手动创建
6.创建缺失的索引(请注意,创建索引可能需要一些时间,特别是对于大型集合)
> db.new_collection.createIndex({status: 1, isDeleted: -1}, {name: "status_1_isDeleted_-1", background: true});
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
db.new_collection.createIndex中,第一个参数是一个对象,它定义了要创建索引的字段和排序方式。在这个例子中,我们使用1表示升序排序,-1表示降序排序。第二个参数是一个对象,它定义了索引的名称。如果没有指定名称,MongoDB将为索引生成一个随机名称。
大集合耗时的索引,我们可以使用以下命令查看后台索引创建的进度:
db.currentOp({"command.createIndexes": {"$exists": true}})
PS:该命令将返回一个包含正在创建索引的进程信息的文档列表。在返回的文档中,可以查看每个进程的状态和进度。例如,可以查看“progress”字段的值,该字段表示索引创建的进度百分比。
另外,也可以使用以下命令查看当前所有的索引创建进程:
db.currentOp({"op": "command", "ns": /^.*\.\$cmd$/, "command.createIndexes": {"$exists": true}})
PS:该命令将返回一个包含所有正在创建索引的进程信息的文档列表。在返回的文档中,可以查看每个进程的状态和进度。
6.替换集合名称(新表换旧表方式)
-- 将原集合重命名为备份
db.original_collection.renameCollection("original_collection_bak20230328")
-- 将新集合重命名为原名字
db.new_collection.renameCollection("original_collection")
7.最后在验证集合的数量
db.original_collection.count({"_id" :{$gte: NumberLong(“1630094934115943888”)}})
PS:其中,original_collection
是集合的名称,count
是MongoDB的计数方法,{"_id" :{$gte: NumberLong("1630094934115943888")}}
是查询条件,表示_id大于1630094934115943888的记录。
最后操作系统层面查看下,44G的就是新集合:
8.经过几天验证,没有问题后,删除备份的索引,释放空间(别急!缓一缓)
db.original_collection_bak20230324.drop()