国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

Mysql|Sql Server|Oracle|Redis|MongoDB|PostgreSQL|Sqlite|DB2|mariadb|Access|數據庫技術|

服務器之家 - 數據庫 - MongoDB - MongoDB中優雅刪除大量數據的三種方式

MongoDB中優雅刪除大量數據的三種方式

2021-11-17 17:36iVictor MongoDB

最近接到一個任務,線上的mongodb積累了大量的無用數據,導致宕機,現在對里面的數據進行批量刪除,所以這篇文章主要給大家介紹了關于MongoDB中優雅刪除大量數據的三種方式,需要的朋友可以參考下

刪除大量數據,無論是在哪種數據庫中,都是一個普遍性的需求。除了正常的業務需求,我們需要通過這種方式來為數據庫“瘦身”。

為什么要“瘦身”呢?

1、表的數據量到達一定量級后,數據量越大,表的查詢性能會越差。

畢竟數據量越大,b+樹的層級會越高,需要的io也會越多。

2、表的數據有冷熱之分,將很多無用或很少用到的數據存儲在數據庫中會消耗數據庫的資源。

譬如會占用緩存;會增加備份集的大小,進而影響備份的恢復時間等。

所以,對于那些無用的數據,我們會定期刪除。

對于那些很少用到的數據,則會定期歸檔。歸檔,一般是將數據寫入到歸檔實例或抽取到大數據組件中。歸檔完畢后,會將對應的數據從原實例中刪除。

一般來說,這種刪除操作涉及的數據量都比較大。

對于這類刪除操作,很多開發童鞋的實現就是一個簡單的delete操作。看上去,簡單明了,干凈利落。

但是,這種方式,危害性卻極大。

以 mysql 為例:

  • 會造成大事務
    大事務會導致主從延遲,而主從延遲又會影響數據庫的高可用切換。
  • 回滾表空間會不斷膨脹
    在mysql 8.0之前,回滾表空間默認是放到系統表空間中,而系統表空間一旦”膨脹“,就不會收縮。
  • 鎖定的記錄多
    相對而言,更容易導致鎖等待。

即使是分布式數據庫,如tidb,如果一次刪除了大量數據,這批數據在進行compaction時有可能會觸發流控。

所以,對于線上的大規模刪除操作,建議分而治之。具體來說,就是批量刪除,每次只刪除一部分數據,分多次執行。

就如何刪除大量數據,接下來我們看看mongodb中的落地方案。

本文主要包括以下四部分內容。

  1. mongodb中刪除數據的三種方式。
  2. 三種方式的執行效率對比。
  3. 通過write concern規避主從延遲。
  4. 刪除過程中碰到的bug。

mongodb中刪除數據的三種方式

在mongodb中刪除數據,可通過以下三種方式:

db.collection.remove()

刪除單個文檔或滿足條件的所有文檔。

db.collection.deletemany()

刪除滿足條件的所有文檔。

db.collection.bulkwrite()

批量操作接口,可執行批量插入、更新、刪除操作。

接下來,對比下這三種方式的執行效率。

三種方式的執行效率對比

環境:mongodb 3.4.4,副本集。

測試思路:分別使用 remove、deletemany、bulkwrite 刪除 10w 條記錄(每批刪除 5000 條),交叉執行 5 次。

1. remove

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// delete_date是刪除條件
var delete_date = new date("2021-01-01t00:00:00.000z");
// 獲取程序開始時間
var start_time = new date();
// 獲取滿足刪除條件的記錄數
rows = db.test_collection.find({"createtime": {$lt: delete_date}}).count()
print("total rows:", rows);
// 定義每批需要刪除的記錄數
var batch_num = 5000;
while (rows > 0) {
    // rows也可理解為剩余記錄數
    // 如果剩余記錄數小于batch_num,則將剩余記錄數賦值給batch_num
    // 為什么要怎么做,后面會提到。
    if (rows < batch_num) {
        batch_num = rows;
    }
    // 獲取滿足刪除條件的最小的5000個_id(objectid)
    var cursor = db.test_collection.find({"createtime": {$lt: delete_date}}, {"_id": 1}).sort({"_id": 1}).limit(batch_num);
    rows = rows - batch_num;
    cursor.foreach(function (each_row) {
        // 通過remove刪除記錄,這里指定了"justone": true,每次只能刪除一條記錄。
        // 為了避免誤刪除,這里同時指定了主鍵和刪除條件。
        db.test_collection.remove({'_id': each_row["_id"], "createtime": {'$lt': delete_date}}, {
            "justone": true,
            w: "majority"
        })
    });
}
// 獲取程序結束時間
var end_time = new date();
// 兩者的差值,即為程序執行時長
print((end_time - start_time) / 1000);

2. deletemany

實例思路同remove類似,只不過會將待刪除的_id放到一個數組中,最后再通過deletemany一次性刪除。

具體代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var delete_date = new date("2021-01-01t00:00:00.000z");
var start_time = new date();
rows = db.test_collection.find({"createtime": {$lt: delete_date}}).count()
print("total rows:", rows);
var batch_num = 5000;
while (rows > 0) {
    if (rows < batch_num) {
        batch_num = rows;
    }
    var cursor = db.test_collection.find({"createtime": {$lt: delete_date}}, {"_id": 1}).sort({"_id": 1}).limit(batch_num);
    rows = rows - batch_num;
    var delete_ids = [];
    // 將滿足條件的主鍵值放入到數組中。
    cursor.foreach(function (each_row) {
        delete_ids.push(each_row["_id"]);
    });
    // 通過deletemany一次刪除5000條記錄。
    db.test_collection.deletemany({
        '_id': {"$in": delete_ids},
        "createtime": {'$lt': delete_date}
    },{w: "majority"})
}
var end_time = new date();
print((end_time - start_time) / 1000);

3. bulkwrite

實現思路同deletemany類似,也是將待刪除的_id放到一個數組中,最后再調用bulkwrite進行刪除。

具體代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var delete_date = new date("2021-01-01t00:00:00.000z");
var start_time = new date();
rows = db.test_collection.find({"createtime": {$lt: delete_date}}).count()
print("total rows:", rows);
var batch_num = 5000;
while (rows > 0) {
    if (rows < batch_num) {
        batch_num = rows;
    }
    var cursor = db.test_collection.find({"createtime": {$lt: delete_date}}, {"_id": 1}).sort({"_id": 1}).limit(batch_num);
    rows = rows - batch_num;
    var delete_ids = [];
    cursor.foreach(function (each_row) {
        delete_ids.push(each_row["_id"]);
    });
    db.test_collection.bulkwrite(
        [
            {
                deletemany: {
                    "filter": {
                        '_id': {"$in": delete_ids},
                        "createtime": {'$lt': delete_date}
                    }
                }
            }
        ],
        {ordered: false},
        {writeconcern: {w: "majority", wtimeout: 100}}
    )
}
var end_time = new date();
print((end_time - start_time) / 1000);

接下來,看看三者的執行效率。

 

刪除方式 平均執行時間(s) 第一次 第二次 第三次 第四次 第五次
remove 47.341 49.606 48.487 49.314 47.572 41.727
deletemany 16.951 16.566 18.669 17.932 18.66 12.928
bulkwrite 16.476 17.247 14.181 16.151 18.403 16.397

 

結合表中的數據,可以看出,

  1. 執行最慢的是remove,執行最快的是bulkwrite,前者差不多是后者的 2.79 倍。
  2. deletemany 和 bulkwrite 的執行效率差不多,但就語法而言,前者比后者簡潔。

所以線上如果要刪除大量數據,推薦使用 deletemany + objectid 進行批量刪除。

通過 write concern 規避主從延遲

雖然是批量刪除,但在mysql中,如果沒控制好節奏,還是很容易導致主從延遲。在mongodb中,其實也有類似的擔憂,不過我們可以通過 write concern 進行規避。

write concern,可理解為寫安全策略,簡單來說,它定義了一個寫操作,需要在幾個節點上應用(apply)完,才會給客戶端反饋。

看下面這個原理圖。

MongoDB中優雅刪除大量數據的三種方式

圖中是一個一主兩從的副本集,設置了w: "majority",代表一個寫操作,需要等待副本集中絕大多數節點(本例中是兩個)應用完,才能給客戶端反饋。

在前面的代碼中,無論是remove,deletemany還是bulkwrite方法,都設置了w: "majority"。

之所以這樣設置,一方面是為了保證數據的安全性,畢竟刪除操作能在多個節點落盤,另一方面,還能有效降低批量操作可能導致的主從延遲風險。

write concern的完整語法如下,

?
1
{ w: <value>, j: <boolean>, wtimeout: <number> }

其中,

w:指定節點數或tags。其有如下取值:

  • <number>:顯式指定節點數量。

設置為0,無需server端反饋。

設置為1,只需primary節點反饋。

設置為2,在副本集中,需要一個primary節點(primary節點必需)和一個secondary節點反饋。

需要注意的是,這里的secondary節點必須是數據節點,可以是隱藏節點、延遲節點或priority為 0 的節點,但仲裁節點(arbiter)絕對不行。

一般來說,設置的節點數越多,數據越安全,寫入的效率也會越低。

  • majority:副本集大多數節點。

與上面不一樣的是,這里的secondary節點不僅要求是數據節點,它的votes(members[n].votes)還必須大于0。

  • <custom write concern name>:指定tags。

tag,顧名思義,是給節點打標簽。常用于多數據中心部署場景。

如一個集群,有5個節點,跨機房部署。其中3個節點在a機房,另外2個節點在b機房,因為對數據的安全性、一致性要求很高,我們希望寫操作至少能在a機房的2個節點落盤,b機房的1個節點落盤。

對于這種個性化的需求,只有通過tags才能實現。

具體使用,可參考:https://docs.mongodb.com/manual/tutorial/configure-replica-set-tag-sets/#configure-custom-write-concern

j:是否需要等待對應操作的日志持久化到磁盤中。

在mongodb中,一個寫操作會涉及到三個動作:更新數據,更新索引,寫入oplog,這三個動作要么全部成功,要么全部失敗,這也是mongodb單行事務的由來。

對于每個寫操作,wiredtiger都會記錄一條日志到 journal 中。

日志在寫入journal之前,會首先寫入到 journal buffer(最大128kb)中。

journal buffer會在以下場景持久化到 journal 文件中:

  • 副本集中,當有操作等待oplog時。

這類操作包括:針對oplog最新位置點的掃描查詢;causally consistent session中的讀操作;對于secondary節點,每次批量應用oplog后。

  • write concern 設置了 j: true。
  • 每100ms。

由 storage.journal.commitintervalms 參數指定。

  • 創建新的 journal 文件時。

當 journal 文件的大小達到100mb時會自動創建一個新的journal 文件。

wtimeout:超時時長,單位ms。

不設置或設置為0,命令在執行的過程中,如果遇到了鎖等待或節點數不滿足要求,會一直阻塞。

如果設置了時間,命令在這個時間內沒有執行成功,則會超時報錯,具體報錯信息如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
rs:primary> db.test.insert({"a": 1}, {writeconcern: {w: "majority", wtimeout: 100}})
writeresult({
    "ninserted": 1,
    "writeconcernerror": {
        "code": 64,
        "codename": "writeconcernfailed",
        "errinfo": {
            "wtimeout": true
        },
        "errmsg": "waiting for replication timed out"
    }
})

刪除過程中遇到的bug

其實,最開始的刪除程序是下面這個版本。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var delete_date = new date("2021-01-01t00:00:00.000z");
var start_time = new date();
var batch_num = 5000;
while (1 == 1) {
    var cursor = db.test_collection.find({"createtime": {$lt: delete_date}}, {"_id": 1}).sort({"_id": 1}).limit(batch_num);
    delete_ids = []
    cursor.foreach(function (each_row) {
        delete_ids.push(each_row["_id"])
    });
 
    if (delete_ids.length == 0) {
        break;
    }
    db.test_collection.deletemany({
        '_id': {"$in": delete_ids},
        "createtime": {'$lt': delete_date}
    }, {w: "majority"})
}
var end_time = new date();
print((end_time - start_time) / 1000);

相對于效率對比章節的版本,這個版本的代碼簡潔不少。

  1. 不用額外獲取需要刪除的記錄數。
  2. batch_num在整個執行過程中也是不變的。

但用這個版本在線上刪除數據時,發現了一個問題。

在刪除到最后一批時,程序會hang在那里。重試了多次依然如此。分析如下:

  • 最后一批的文檔數小于batch_num時,會出現這個問題。

刪除同實例下另外一個集合,也出現了類似的問題。

但在測試環境,刪除一個簡單的集合卻沒有復現出來,懷疑這個bug與線上集合的記錄過長有關。

  • cursor只是一個迭代對象,并不是查詢結果。基于cursor可以分批返回記錄,類似于python中的迭代器。

最后一批也不是完全沒有返回,而是在返回100條之后才hang在那里。

  • 不使用sort沒有這個問題。

為什么要使用sort呢?這樣可保證得到的id是有序且在物理上的存儲是相鄰的。這樣,在執行批量刪除操作時,效率也會相對較高。

經過實際測試,當要刪除的數據量較大時,使用sort的效率確實比不使用的要高。

如果刪除的數據量較小,使不使用sort則沒多大區別。

總結

從最佳實踐的角度出發,無論是在哪種數據庫中,如果都刪除(更新)大量數據,都建議分而治之,分批執行。

在mongodb中,如果要刪除大量數據,推薦使用deletemany + objectid進行批量刪除。

為了保證操作的安全性及規避批量操作帶來的主從延遲風險,建議在執行刪除操作時,將write concern設置為w: "majority"。

到此這篇關于mongodb中優雅刪除大量數據的文章就介紹到這了,更多相關mongodb刪除大量數據內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

參考

[1] journaling

[2] write concern

原文鏈接:https://www.cnblogs.com/ivictor/p/15457454.html

延伸 · 閱讀

精彩推薦
  • MongoDBMongoDB 內存使用情況分析

    MongoDB 內存使用情況分析

    都說 MongoDB 是個內存大戶,但是怎么知道它到底用了多少內存呢...

    MongoDB教程網10002020-09-29
  • MongoDBmongodb基本命令實例小結

    mongodb基本命令實例小結

    這篇文章主要介紹了mongodb基本命令,結合實例形式總結分析了MongoDB數據庫切換、查看、刪除、查詢等基本命令用法與操作注意事項,需要的朋友可以參考下...

    dawn-liu3652020-05-26
  • MongoDB分布式文檔存儲數據庫之MongoDB分片集群的問題

    分布式文檔存儲數據庫之MongoDB分片集群的問題

    這篇文章主要介紹了分布式文檔存儲數據庫之MongoDB分片集群的問題,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋...

    Linux-18743072020-12-20
  • MongoDBMongoDB憑什么躋身數據庫排行前五

    MongoDB憑什么躋身數據庫排行前五

    MongoDB以比去年同期超出65.96分的成績繼續雄踞榜單前五,這個增幅在全榜僅次于PostgreSQL的77.99,而其相對于4月份的6.10分的增長也是僅次于微軟SQL Server排名...

    孫浩峰3892020-05-22
  • MongoDBMongodb實現定時備份與恢復的方法教程

    Mongodb實現定時備份與恢復的方法教程

    這篇文章主要給大家介紹了Mongodb實現定時備份與恢復的方法教程,文中通過示例代碼介紹的非常詳細,對大家具有一定的參考學習價值,需要的朋友們下面...

    chenjsh364522020-05-13
  • MongoDB遷移sqlserver數據到MongoDb的方法

    遷移sqlserver數據到MongoDb的方法

    這篇文章主要介紹了遷移sqlserver數據到MongoDb的方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下...

    聽楓xl9682021-01-03
  • MongoDBMongoDB安裝圖文教程

    MongoDB安裝圖文教程

    這篇文章主要為大家詳細介紹了MongoDB安裝圖文教程,分為兩大部分為大家介紹下載MongoDB和安裝MongoDB的方法,感興趣的小伙伴們可以參考一下 ...

    Yangyi.He6132020-05-07
  • MongoDBMongoDB中javascript腳本編程簡介和入門實例

    MongoDB中javascript腳本編程簡介和入門實例

    作為一個數據庫,MongoDB有一個很大的優勢——它使用js管理數據庫,所以也能夠使用js腳本進行復雜的管理——這種方法非常靈活 ...

    MongoDB教程網6982020-04-24
主站蜘蛛池模板: 欧洲亚洲精品久久久久 | 午夜欧美 | 日韩黄网| 激情国产视频 | 国产午夜精品一区二区三区 | 国产欧美久久久久久 | 久久精品国产亚洲一区二区三区 | 久久国际影院 | 精品欧美一区二区三区久久久 | 九九九在线 | 欧美日韩在线一区二区三区 | 香蕉久久久久久 | 亚洲精品一区二区三区四区高清 | 日韩中文字幕在线播放 | 99福利视频 | 99精品久久 | 欧美在线不卡 | jizzxxx日本 | 亚洲视频欧美视频 | 91精品国产综合久久婷婷香蕉 | 午夜爽爽影院 | 中文字幕在线免费视频 | 精品久久久久久国产 | 毛片黄视频| 亚洲欧美日韩国产综合精品二区 | 最新国产精品 | 久久九九国产精品 | 欧美国产日韩在线 | 国产日韩一区 | 国产精品亚洲综合 | 日操| 国产精品毛片久久久久久久 | 日本全黄裸体片 | 三级在线网 | 久久久国产视频 | 日韩精品一区二区三区 | 国产精品美女久久久久aⅴ国产馆 | 亚洲成熟少妇视频在线观看 | www.91福利| 国产精品久久久久久久久 | 黄色高清网站 |