欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

MongoDB中優(yōu)雅刪除大量數(shù)據(jù)的三種方式

 更新時(shí)間:2021年10月26日 09:28:14   作者:iVictor  
最近接到一個(gè)任務(wù),線上的mongodb積累了大量的無(wú)用數(shù)據(jù),導(dǎo)致宕機(jī),現(xiàn)在對(duì)里面的數(shù)據(jù)進(jìn)行批量刪除,所以這篇文章主要給大家介紹了關(guān)于MongoDB中優(yōu)雅刪除大量數(shù)據(jù)的三種方式,需要的朋友可以參考下

刪除大量數(shù)據(jù),無(wú)論是在哪種數(shù)據(jù)庫(kù)中,都是一個(gè)普遍性的需求。除了正常的業(yè)務(wù)需求,我們需要通過(guò)這種方式來(lái)為數(shù)據(jù)庫(kù)“瘦身”。

為什么要“瘦身”呢?

1、表的數(shù)據(jù)量到達(dá)一定量級(jí)后,數(shù)據(jù)量越大,表的查詢性能會(huì)越差。

畢竟數(shù)據(jù)量越大,B+樹的層級(jí)會(huì)越高,需要的IO也會(huì)越多。

2、表的數(shù)據(jù)有冷熱之分,將很多無(wú)用或很少用到的數(shù)據(jù)存儲(chǔ)在數(shù)據(jù)庫(kù)中會(huì)消耗數(shù)據(jù)庫(kù)的資源。

譬如會(huì)占用緩存;會(huì)增加備份集的大小,進(jìn)而影響備份的恢復(fù)時(shí)間等。

所以,對(duì)于那些無(wú)用的數(shù)據(jù),我們會(huì)定期刪除。

對(duì)于那些很少用到的數(shù)據(jù),則會(huì)定期歸檔。歸檔,一般是將數(shù)據(jù)寫入到歸檔實(shí)例或抽取到大數(shù)據(jù)組件中。歸檔完畢后,會(huì)將對(duì)應(yīng)的數(shù)據(jù)從原實(shí)例中刪除。

一般來(lái)說(shuō),這種刪除操作涉及的數(shù)據(jù)量都比較大。

對(duì)于這類刪除操作,很多開發(fā)童鞋的實(shí)現(xiàn)就是一個(gè)簡(jiǎn)單的DELETE操作。看上去,簡(jiǎn)單明了,干凈利落。

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

以 MySQL 為例:

  • 會(huì)造成大事務(wù)
    大事務(wù)會(huì)導(dǎo)致主從延遲,而主從延遲又會(huì)影響數(shù)據(jù)庫(kù)的高可用切換。
  • 回滾表空間會(huì)不斷膨脹
    在MySQL 8.0之前,回滾表空間默認(rèn)是放到系統(tǒng)表空間中,而系統(tǒng)表空間一旦”膨脹“,就不會(huì)收縮。
  • 鎖定的記錄多
    相對(duì)而言,更容易導(dǎo)致鎖等待。

即使是分布式數(shù)據(jù)庫(kù),如TiDB,如果一次刪除了大量數(shù)據(jù),這批數(shù)據(jù)在進(jìn)行Compaction時(shí)有可能會(huì)觸發(fā)流控。

所以,對(duì)于線上的大規(guī)模刪除操作,建議分而治之。具體來(lái)說(shuō),就是批量刪除,每次只刪除一部分?jǐn)?shù)據(jù),分多次執(zhí)行。

就如何刪除大量數(shù)據(jù),接下來(lái)我們看看MongoDB中的落地方案。

本文主要包括以下四部分內(nèi)容。

  1. MongoDB中刪除數(shù)據(jù)的三種方式。
  2. 三種方式的執(zhí)行效率對(duì)比。
  3. 通過(guò)Write Concern規(guī)避主從延遲。
  4. 刪除過(guò)程中碰到的Bug。

MongoDB中刪除數(shù)據(jù)的三種方式

在MongoDB中刪除數(shù)據(jù),可通過(guò)以下三種方式:

db.collection.remove()

刪除單個(gè)文檔或滿足條件的所有文檔。

db.collection.deleteMany()

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

db.collection.bulkWrite()

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

接下來(lái),對(duì)比下這三種方式的執(zhí)行效率。

三種方式的執(zhí)行效率對(duì)比

環(huán)境:MongoDB 3.4.4,副本集。

測(cè)試思路:分別使用 remove、deleteMany、bulkWrite 刪除 10w 條記錄(每批刪除 5000 條),交叉執(zhí)行 5 次。

1. remove

// delete_date是刪除條件
var delete_date = new Date("2021-01-01T00:00:00.000Z");
// 獲取程序開始時(shí)間
var start_time = new Date();
// 獲取滿足刪除條件的記錄數(shù)
rows = db.test_collection.find({"createtime": {$lt: delete_date}}).count()
print("total rows:", rows);
// 定義每批需要?jiǎng)h除的記錄數(shù)
var batch_num = 5000;
while (rows > 0) {
    // rows也可理解為剩余記錄數(shù)
    // 如果剩余記錄數(shù)小于batch_num,則將剩余記錄數(shù)賦值給batch_num
    // 為什么要怎么做,后面會(huì)提到。
    if (rows < batch_num) {
        batch_num = rows;
    }
    // 獲取滿足刪除條件的最小的5000個(gè)_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) {
        // 通過(guò)remove刪除記錄,這里指定了"justOne": true,每次只能刪除一條記錄。
        // 為了避免誤刪除,這里同時(shí)指定了主鍵和刪除條件。
        db.test_collection.remove({'_id': each_row["_id"], "createtime": {'$lt': delete_date}}, {
            "justOne": true,
            w: "majority"
        })
    });
}
// 獲取程序結(jié)束時(shí)間
var end_time = new Date();
// 兩者的差值,即為程序執(zhí)行時(shí)長(zhǎng)
print((end_time - start_time) / 1000);

2. deleteMany

實(shí)例思路同remove類似,只不過(guò)會(huì)將待刪除的_id放到一個(gè)數(shù)組中,最后再通過(guò)deleteMany一次性刪除。

具體代碼如下:

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 = [];
    // 將滿足條件的主鍵值放入到數(shù)組中。
    cursor.forEach(function (each_row) {
        delete_ids.push(each_row["_id"]);
    });
    // 通過(guò)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

實(shí)現(xiàn)思路同deleteMany類似,也是將待刪除的_id放到一個(gè)數(shù)組中,最后再調(diào)用bulkWrite進(jìn)行刪除。

具體代碼如下:

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);

接下來(lái),看看三者的執(zhí)行效率。

刪除方式 平均執(zhí)行時(shí)間(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

結(jié)合表中的數(shù)據(jù),可以看出,

  1. 執(zhí)行最慢的是remove,執(zhí)行最快的是bulkWrite,前者差不多是后者的 2.79 倍。
  2. deleteMany 和 bulkWrite 的執(zhí)行效率差不多,但就語(yǔ)法而言,前者比后者簡(jiǎn)潔。

所以線上如果要?jiǎng)h除大量數(shù)據(jù),推薦使用 deleteMany + ObjectID 進(jìn)行批量刪除。

通過(guò) Write Concern 規(guī)避主從延遲

雖然是批量刪除,但在MySQL中,如果沒(méi)控制好節(jié)奏,還是很容易導(dǎo)致主從延遲。在MongoDB中,其實(shí)也有類似的擔(dān)憂,不過(guò)我們可以通過(guò) Write Concern 進(jìn)行規(guī)避。

Write Concern,可理解為寫安全策略,簡(jiǎn)單來(lái)說(shuō),它定義了一個(gè)寫操作,需要在幾個(gè)節(jié)點(diǎn)上應(yīng)用(Apply)完,才會(huì)給客戶端反饋。

看下面這個(gè)原理圖。

 

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

在前面的代碼中,無(wú)論是remove,deleteMany還是bulkWrite方法,都設(shè)置了w: "majority"。

之所以這樣設(shè)置,一方面是為了保證數(shù)據(jù)的安全性,畢竟刪除操作能在多個(gè)節(jié)點(diǎn)落盤,另一方面,還能有效降低批量操作可能導(dǎo)致的主從延遲風(fēng)險(xiǎn)。

Write Concern的完整語(yǔ)法如下,

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

其中,

w:指定節(jié)點(diǎn)數(shù)或tags。其有如下取值:

  • <number>:顯式指定節(jié)點(diǎn)數(shù)量。

設(shè)置為0,無(wú)需Server端反饋。

設(shè)置為1,只需Primary節(jié)點(diǎn)反饋。

設(shè)置為2,在副本集中,需要一個(gè)Primary節(jié)點(diǎn)(Primary節(jié)點(diǎn)必需)和一個(gè)Secondary節(jié)點(diǎn)反饋。

需要注意的是,這里的Secondary節(jié)點(diǎn)必須是數(shù)據(jù)節(jié)點(diǎn),可以是隱藏節(jié)點(diǎn)、延遲節(jié)點(diǎn)或Priority為 0 的節(jié)點(diǎn),但仲裁節(jié)點(diǎn)(Arbiter)絕對(duì)不行。

一般來(lái)說(shuō),設(shè)置的節(jié)點(diǎn)數(shù)越多,數(shù)據(jù)越安全,寫入的效率也會(huì)越低。

  • majority:副本集大多數(shù)節(jié)點(diǎn)。

與上面不一樣的是,這里的Secondary節(jié)點(diǎn)不僅要求是數(shù)據(jù)節(jié)點(diǎn),它的votes(members[n].votes)還必須大于0。

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

tag,顧名思義,是給節(jié)點(diǎn)打標(biāo)簽。常用于多數(shù)據(jù)中心部署場(chǎng)景。

如一個(gè)集群,有5個(gè)節(jié)點(diǎn),跨機(jī)房部署。其中3個(gè)節(jié)點(diǎn)在A機(jī)房,另外2個(gè)節(jié)點(diǎn)在B機(jī)房,因?yàn)閷?duì)數(shù)據(jù)的安全性、一致性要求很高,我們希望寫操作至少能在A機(jī)房的2個(gè)節(jié)點(diǎn)落盤,B機(jī)房的1個(gè)節(jié)點(diǎn)落盤。

對(duì)于這種個(gè)性化的需求,只有通過(guò)tags才能實(shí)現(xiàn)。

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

j:是否需要等待對(duì)應(yīng)操作的日志持久化到磁盤中。

在MongoDB中,一個(gè)寫操作會(huì)涉及到三個(gè)動(dòng)作:更新數(shù)據(jù),更新索引,寫入oplog,這三個(gè)動(dòng)作要么全部成功,要么全部失敗,這也是MongoDB單行事務(wù)的由來(lái)。

對(duì)于每個(gè)寫操作,WiredTiger都會(huì)記錄一條日志到 journal 中。

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

Journal buffer會(huì)在以下場(chǎng)景持久化到 journal 文件中:

  • 副本集中,當(dāng)有操作等待oplog時(shí)。

這類操作包括:針對(duì)oplog最新位置點(diǎn)的掃描查詢;Causally consistent session中的讀操作;對(duì)于Secondary節(jié)點(diǎn),每次批量應(yīng)用oplog后。

  • Write Concern 設(shè)置了 j: true。
  • 每100ms。

由 storage.journal.commitIntervalMs 參數(shù)指定。

  • 創(chuàng)建新的 journal 文件時(shí)。

當(dāng) journal 文件的大小達(dá)到100MB時(shí)會(huì)自動(dòng)創(chuàng)建一個(gè)新的journal 文件。

wtimeout:超時(shí)時(shí)長(zhǎng),單位ms。

不設(shè)置或設(shè)置為0,命令在執(zhí)行的過(guò)程中,如果遇到了鎖等待或節(jié)點(diǎn)數(shù)不滿足要求,會(huì)一直阻塞。

如果設(shè)置了時(shí)間,命令在這個(gè)時(shí)間內(nèi)沒(méi)有執(zhí)行成功,則會(huì)超時(shí)報(bào)錯(cuò),具體報(bào)錯(cuò)信息如下:

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"
    }
})

刪除過(guò)程中遇到的Bug

其實(shí),最開始的刪除程序是下面這個(gè)版本。

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);

相對(duì)于效率對(duì)比章節(jié)的版本,這個(gè)版本的代碼簡(jiǎn)潔不少。

  1. 不用額外獲取需要?jiǎng)h除的記錄數(shù)。
  2. batch_num在整個(gè)執(zhí)行過(guò)程中也是不變的。

但用這個(gè)版本在線上刪除數(shù)據(jù)時(shí),發(fā)現(xiàn)了一個(gè)問(wèn)題。

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

  • 最后一批的文檔數(shù)小于batch_num時(shí),會(huì)出現(xiàn)這個(gè)問(wèn)題。

刪除同實(shí)例下另外一個(gè)集合,也出現(xiàn)了類似的問(wèn)題。

但在測(cè)試環(huán)境,刪除一個(gè)簡(jiǎn)單的集合卻沒(méi)有復(fù)現(xiàn)出來(lái),懷疑這個(gè)Bug與線上集合的記錄過(guò)長(zhǎng)有關(guān)。

  • cursor只是一個(gè)迭代對(duì)象,并不是查詢結(jié)果?;赾ursor可以分批返回記錄,類似于Python中的迭代器。

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

  • 不使用sort沒(méi)有這個(gè)問(wèn)題。

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

經(jīng)過(guò)實(shí)際測(cè)試,當(dāng)要?jiǎng)h除的數(shù)據(jù)量較大時(shí),使用sort的效率確實(shí)比不使用的要高。

如果刪除的數(shù)據(jù)量較小,使不使用sort則沒(méi)多大區(qū)別。

總結(jié)

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

在MongoDB中,如果要?jiǎng)h除大量數(shù)據(jù),推薦使用deleteMany + ObjectID進(jìn)行批量刪除。

為了保證操作的安全性及規(guī)避批量操作帶來(lái)的主從延遲風(fēng)險(xiǎn),建議在執(zhí)行刪除操作時(shí),將Write Concern設(shè)置為w: "majority"。

到此這篇關(guān)于MongoDB中優(yōu)雅刪除大量數(shù)據(jù)的文章就介紹到這了,更多相關(guān)MongoDB刪除大量數(shù)據(jù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

參考

[1] Journaling

[2] Write Concern

相關(guān)文章

  • Mongodb的oplog詳解

    Mongodb的oplog詳解

    這篇文章主要介紹了Mongodb的oplog詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • MongoDB教程之索引介紹

    MongoDB教程之索引介紹

    這篇文章主要介紹了MongoDB教程之索引介紹,本文講解了索引基礎(chǔ)、唯一索引、使用explain、索引管理等內(nèi)容,需要的朋友可以參考下
    2015-05-05
  • MongoDB入門教程之分片技術(shù)詳解

    MongoDB入門教程之分片技術(shù)詳解

    這篇文章主要介紹了MongoDB入門教程之分片技術(shù)詳解,分片是mongodb中的另一種集群技術(shù),需要的朋友可以參考下
    2014-08-08
  • window平臺(tái)安裝MongoDB數(shù)據(jù)庫(kù)圖文詳解

    window平臺(tái)安裝MongoDB數(shù)據(jù)庫(kù)圖文詳解

    本篇文章主要介紹了window平臺(tái)安裝MongoDB數(shù)據(jù)庫(kù)圖文詳解,主要介紹window下面安裝mogod的步驟和使用細(xì)節(jié)。感興趣的小伙伴們可以參考一下。
    2016-11-11
  • MongoDB最大連接數(shù)設(shè)置失效的異常分析過(guò)程與解決方法

    MongoDB最大連接數(shù)設(shè)置失效的異常分析過(guò)程與解決方法

    mongodb最大連接數(shù)是20000。所以業(yè)界流傳一段話,千萬(wàn)級(jí)以下的用mysql、千萬(wàn)級(jí)以上的用mongodb,億級(jí)以上的用hadoop。下面這篇文章主要給大家介紹了關(guān)于MongoDB最大連接數(shù)設(shè)置失效的異常分析過(guò)程,需要的朋友可以參考下
    2018-09-09
  • mongodb+php實(shí)現(xiàn)簡(jiǎn)單的增刪改查

    mongodb+php實(shí)現(xiàn)簡(jiǎn)單的增刪改查

    這篇文章主要介紹了mongodb+php實(shí)現(xiàn)簡(jiǎn)單的增刪改查的相關(guān)資料,需要的朋友可以參考下
    2016-07-07
  • CentOS7.2 安裝 MongoDB 3.4的教程

    CentOS7.2 安裝 MongoDB 3.4的教程

    這篇文章主要介紹了CentOS7.2 安裝 MongoDB 3.4的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-05-05
  • SpringBoot?集成MongoDB實(shí)現(xiàn)文件上傳功能

    SpringBoot?集成MongoDB實(shí)現(xiàn)文件上傳功能

    這篇文章主要介紹了SpringBoot?集成MongoDB實(shí)現(xiàn)文件上傳,主要通過(guò)示例代碼記錄文件上傳的步驟,代碼簡(jiǎn)單易懂,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-04-04
  • mongodb數(shù)據(jù)庫(kù)入門之CURD簡(jiǎn)單操作示例

    mongodb數(shù)據(jù)庫(kù)入門之CURD簡(jiǎn)單操作示例

    這篇文章主要介紹了mongodb數(shù)據(jù)庫(kù)入門之CURD簡(jiǎn)單操作,結(jié)合簡(jiǎn)單示例形式分析了MongoDB數(shù)據(jù)庫(kù)基本的CURD增刪改查相關(guān)操作技巧與注意事項(xiàng),需要的朋友可以參考下
    2019-10-10
  • MongoDB查詢之高級(jí)操作詳解(多條件查詢、正則匹配查詢等)

    MongoDB查詢之高級(jí)操作詳解(多條件查詢、正則匹配查詢等)

    這篇文章主要給大家介紹了關(guān)于MongoDB查詢之高級(jí)操作(多條件查詢、正則匹配查詢等)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10

最新評(píng)論