mongodb每天上亿数据量定期清理

背景:mongodb(应用运营数据分析与自动自助化支持平台)每分钟有30w~40w的insert,20w~30w的update。数据保留一天,一天之前的数据可以清理。一天的数据量大概1亿左右。由于数据量较大,清理数据对系统造成了较大影响,入库会出现堵塞。经过和开发多次讨论,尝试了不同的方案。最终选择了合适的清理方式,系统也开始比较稳定地运行。

除了这个mongodb库外,也遇到其他库询问关于mongodb如何进行数据清理问题。这里将常见的清理方法列出来供大家参考。

清理方法:

  1. 采用定期一分钟删除一次的方法。方法如下:

                        "op" : "remove",
    
                        "ns" : "fla.logGset",
    
                        "query" : {
    
                                "time" : {
    
                                        "$lt" : NumberLong(1495693140),
    
                                        "$gte" : NumberLong(1495693080)
    
                                }
    
                        },
    

对于数据量较小的表,这种方法比较合适。但是对于数据量较较大的库,删除速度太慢,跟不上入库的速度。比如mrcache这个库,单次删除执行时间需要大概10分钟~20分钟不等。这个远远追不上入库的速度。

  1. 采用晚上定期执行删除任务,删除小于某个时间的所有数据。

                        "op" : "remove",
    
                        "ns" : "fla.logGset",
    
                        "query" : {
    
                                "time" : {
    
                                        "$lt" : 1495752371
    
                                }
    
                        },
    

对于一个晚上能够清理完某个时间点之前的数据,这种方法还是比较合适的。如果表太大,该语句晚上没有执行完成,一直持续到白天,就会严重影响白天的业务,特别是高并发的情况下,会导致mongodb变得非常缓慢。比如mrcache这个库,有个22:00开始执行删除小于一天前的数据,数据量大概12个亿,执行到白天10:00也没有完成。mongodb变得非常缓慢,当停掉客户端程序后,发现已执行的删除语句其实还在继续执行。将mongodb切换到从库后,仍然非常缓慢。只有从后台将删除会话kill后,mongodb性能才会恢复。

db.currentOp().inprog.forEach(function(item){if(item.ns == "fla.logGset"&&item.op == "remove" )db.killOp(item.opid)})

  1. 采用mongodb的TTL索引功能。

db.person.createIndex( { "lastModifiedDate": 1 }, { expireAfterSeconds: 60 } )

查看了ttl索引实际进行的操作,mongodb在后台使用的类似于delete tab where time <某个时间的方式。

ns: ycsb.personkey:{ lastModifiedDate: 1.0 } query:

{ lastModifiedDate: { $lt: new Date(1495696792487) } }

TTL deleted: 58397

使用TTL index不需要自己写代码,mongodb自动清理数据,而且不用同步删除的数据到从库,ttl的从库操作是在从库独立运行的。

但是TTL index的清理操作对数据库有一定的影响。而且是单线程操作。

例如fla库单表的一分钟的数据30多万的数据,根据负载不同删除一分钟需要耗时从10分钟到20分钟不等,不能满足删除需求。

  1. 根据单位时间的数据量,分批删除,并启用多个线程。

例如fla库,每分钟删除一次,需要大概10分钟。大概20个线程同时运行,能够追的上入库的速度。另外一种方法是根据object id分批删除。效率和根据时间删除差不多。程序放在晚间执行,不影响白天的入库操作。到早上8:00停止。

                    "op" : "remove",

                    "ns" : "fla.logGset",

                    "query" : {

                            "time" : {

                                    "$lt" : NumberLong(1495693140),

                                    "$gte" : NumberLong(1495693080)

                            }

                    },

或者

                    "op" : "remove",

                    "ns" : "fla.logGset",

                    "query" : {

                            "_id" : {

                                    "$lt" : ObjectId("5928c66cf1516ce0f261b444"),

                                    "$gte" : ObjectId("5928c630f1516ce0f261b445")

                            }

                    },

 

mrcache库采用这种方式,目前30W/min insert操作、20w/min的update操作,无删除时mongodb还是比较稳定的; 在夜间业务低峰执行删除时, 观察过几次, 都会有一些抖动; 不过可以接受。

  1. 采用分表的方式,类似于oracle的分区操作。不过程序代码需要处理oracle的分区功能,比如创建新collection、查询新collection和删除旧collection操作。

为每天创建一个collection,比如logGset170605,logGset170606,logGset170607。

这种方式清理时很简单,只要drop相应日期的collection即可。但是程序代码需要进行额外处理,需要能够识别分表的情况,每天需要动态的创建新collection,DML操作也要切换到新的collection。在进行查询统计时,需要跨多个collection访问。

  1. 采用分库分表的方式,和第5步类似,只是collection的db也是新建的。

为每天创建一个db+collection,比如db170605.logGset170605,db170606.logGset170606,db170607.logGset170607。

清理时直接drop db,这样可以从物理磁盘释放空间。

上一篇:.Net分布式架构(二):基于Redis的Session共享


下一篇:excel文件内容导入数据库的问题及解决