天天看點

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,這樣可以從實體磁盤釋放空間。