<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          MongoDB和MySQL效率性能對比

          共 579字,需瀏覽 2分鐘

           ·

          2021-11-08 21:13

          點擊“藍字”,關(guān)注,置頂公眾號

          每日技術(shù)干貨,第一時間送達!

          本文主要通過批量與非批量對比操作的方式介紹MongoDB的bulkWrite()方法的使用。順帶與關(guān)系型數(shù)據(jù)庫MySQL進行對比,比較這兩種不同類型數(shù)據(jù)庫的效率。如果只是想學(xué)習(xí)bulkWrite()的使用的看第一部分就行。

          測試環(huán)境:win7旗艦版、16G內(nèi)存、i3處理器、MongoDB3.0.2、mysql5.0

          一、MongoDB批量操作

          MongoDB對數(shù)據(jù)的操作分為Read Operations和Write Operations,Read Operations包含查詢操作,Write Operations包含刪除、插入、替換、更新幾種操作。MongoDB提供客戶端用bulk方式執(zhí)行Write Operations,也就是批量寫操作。在java driver中,對應(yīng)MongoCollection的bulkWrite()方法,先來看下這個方法簽名:

          BulkWriteResult??com.mongodb.client.MongoCollection.bulkWrite(List>?requests)

          這個方法要求傳入一個List集合,集合中的元素類型為WriteModel,它表示一個可用于批量寫操作的基類模型,它有以下幾個子類DeleteManyModel、DeleteOneModel、 InsertOneModel、ReplaceOneModel、 UpdateManyModel、UpdateOneModel,從名字可以看出來它對應(yīng)了刪除、插入、替換、更新幾種操作。該方法返回一個BulkWriteResult對象,代表一個成功的批量寫操作結(jié)果,封裝了操作結(jié)果的狀態(tài)信息,如插入、更新、刪除記錄數(shù)等。

          1、插入操作

          (1)、批量插入

          代碼如下,該方法接收一個包含要進行插入的Document對象的集合參數(shù),遍歷集合,使用Document構(gòu)造InsertOneModel對象,每個InsertOneModel實例代表一個插入單個Document的操作,然后將該實例添加List集合中,調(diào)用bulkWrite()方法,傳入存儲所有插入操作的List集合完成批量插入。

          public?void?bulkWriteInsert(List?documents){
          ?List>?requests?=?new?ArrayList>();
          ?for?(Document?document?:?documents)?{
          ??//構(gòu)造插入單個文檔的操作模型
          ??InsertOneModel??iom?=?new?InsertOneModel(document);
          ??requests.add(iom);
          ?}
          ?BulkWriteResult??bulkWriteResult?=?collection.bulkWrite(requests);
          ?System.out.println(bulkWriteResult.toString());
          }

          測試:下面通過一個main函數(shù)測試下。首先構(gòu)造10萬個Product實體對象,使用一個工具類將其轉(zhuǎn)換成json字符串,然后解析成Document對象,保存到一個list集合中,然后調(diào)用上面編寫的方法測試10萬個對象插入時間。

          TestMongoDB?instance?=?TestMongoDB.getInstance();
          ArrayList?documents?=?new?ArrayList();
          for?(int?i?=?0;?i?100000;?i++)?{
          ?Product?product?=?new?Product(i,"書籍","追風(fēng)箏的人",22.5);
          ?//將java對象轉(zhuǎn)換成json字符串
          ?String?jsonProduct?=?JsonParseUtil.getJsonString4JavaPOJO(product);
          ?//將json字符串解析成Document對象
          ?Document?docProduct?=?Document.parse(jsonProduct);
          ?documents.add(docProduct);
          }

          System.out.println("開始插入數(shù)據(jù)。。。");
          long?startInsert?=?System.currentTimeMillis();
          instance.bulkWriteInsert(documents);
          System.out.println("插入數(shù)據(jù)完成,共耗時:"+(System.currentTimeMillis()?-?startInsert)+"毫秒");

          結(jié)果:1560毫秒,多次測試基本在1.5秒左右

          (2)、逐條插入

          下面再通過非批量插入10萬個數(shù)據(jù)對比下,方法如下:

          ?public?void?insertOneByOne(List?documents)?throws?ParseException{
          ??for?(Document?document?:?documents){
          ???collection.insertOne(document);
          ??}
          ?}

          測試:10萬條數(shù)據(jù)

          System.out.println("開始插入數(shù)據(jù)。。。");
          long?startInsert?=?System.currentTimeMillis();
          instance.insertOneByOne(documents);
          System.out.println("插入數(shù)據(jù)完成,共耗時:"+(System.currentTimeMillis()?-?startInsert)+"毫秒");

          結(jié)果:12068毫秒,差距非常大。由此可見,MongoDB批量插入比逐條數(shù)據(jù)插入效率提高了非常多。

          補充:

          MongoCollection的insertMany()方法和bulkWrite()方法是等價的,測試時間差不多,不再貼圖。

          ?public?void?insertMany(List?documents)?throws?ParseException{
          ??//和bulkWrite()方法等價
          ??collection.insertMany(documents);
          ?}

          2、刪除操作

          (1)、批量刪除

          掌握了批量插入,批量刪除就是依葫蘆畫瓢了。構(gòu)造DeleteOneModel需要一個Bson類型參數(shù),代表一個刪除操作,這里使用了Bson類的子類Document。重點來了,這里的刪除條件使用文檔的_id字段,該字段在文檔插入數(shù)據(jù)庫后自動生成,沒插入數(shù)據(jù)庫前document.get("_id")為null,如果使用其他條件比如productId,那么要在文檔插入到collection后在productId字段上添加索引

          collection.createIndex(new?Document("productId",?1));

          因為隨著collection數(shù)據(jù)量的增大,查找將越耗時,添加索引是為了提高查找效率,進而加快刪除效率。另外,值得一提的是DeleteOneModel表示至多刪除一條匹配條件的記錄,DeleteManyModel表示刪除匹配條件的所有記錄。為了防止一次刪除多條記錄,這里使用DeleteOneModel,保證一個操作只刪除一條記錄。當(dāng)然這里不可能匹配多條記錄,因為_id是唯一的。

          public?void?bulkWriteDelete(List?documents){
          ?List>?requests?=?new?ArrayList>();
          ?for?(Document?document?:?documents)?{
          ??//刪除條件
          ??Document?queryDocument?=?new?Document("_id",document.get("_id"));
          ??//構(gòu)造刪除單個文檔的操作模型,
          ??DeleteOneModel??dom?=?new?DeleteOneModel(queryDocument);
          ??requests.add(dom);
          ?}
          ?BulkWriteResult?bulkWriteResult?=?collection.bulkWrite(requests);
          ?System.out.println(bulkWriteResult.toString());
          }

          測試:10萬條數(shù)據(jù)

          System.out.println("開始刪除數(shù)據(jù)。。。");
          long?startDelete?=?System.currentTimeMillis();
          instance.bulkWriteDelete(documents);
          System.out.println("刪除數(shù)據(jù)完成,共耗時:"+(System.currentTimeMillis()?-?startDelete)+"毫秒");

          結(jié)果:2251毫秒

          (2)、逐條刪除

          來看看在非批量下的刪除

          ?public?void?deleteOneByOne(List?documents){
          ??for?(Document?document?:?documents)?{
          ???Document?queryDocument?=?new?Document("_id",document.get("_id"));
          ???DeleteResult?deleteResult?=?collection.deleteOne(queryDocument);
          ??}
          ?}

          測試:10萬條數(shù)據(jù)

          System.out.println("開始刪除數(shù)據(jù)。。。");
          long?startDelete?=?System.currentTimeMillis();
          instance.deleteOneByOne(documents);
          System.out.println("刪除數(shù)據(jù)完成,共耗時:"+(System.currentTimeMillis()?-?startDelete)+"毫秒");

          結(jié)果:12765毫秒,比批量刪除效率低很多

          3、更新操作

          (1)、批量更新

          再來看看批量更新,分UpdateOneModel和UpdateManyModel兩種,區(qū)別是前者更新匹配條件的一條記錄,后者更新匹配條件的所有記錄。對于ReplaceOneModel,表示替換操作,這里也歸為更新,現(xiàn)在以UpdateOneModel為例進行講解。UpdateOneModel構(gòu)造方法接收3個參數(shù),第一個是查詢條件,第二個參數(shù)是要更新的內(nèi)容,第三個參數(shù)是可選的UpdateOptions,不填也會自動幫你new一個,代表批量更新操作未匹配到查詢條件時的動作,它的upser屬性值默認(rèn)false,什么都不干,true時表示將一個新的Document插入數(shù)據(jù)庫,這個新的Document是查詢Document和更新Document的結(jié)合,但如果是替換操作,這個新的Document就是這個替換Document。

          這里會有個疑惑:這和匹配到查詢條件后執(zhí)行替換操作結(jié)果不一樣嗎?區(qū)別在于_id字段,未匹配查詢條件時插入的新的Document的_id是新的,而成功執(zhí)行替換操作,_id是原先舊的。

          ?public?void?bulkWriteUpdate(List?documents){
          ??List>?requests?=?new?ArrayList>();
          ??for?(Document?document?:?documents)?{
          ???//更新條件
          ???Document?queryDocument?=?new?Document("_id",document.get("_id"));
          ???//更新內(nèi)容,改下書的價格
          ???Document?updateDocument?=?new?Document("$set",new?Document("price","30.6"));
          ???//構(gòu)造更新單個文檔的操作模型
          ???UpdateOneModel?uom?=?new?UpdateOneModel(queryDocument,updateDocument,new?UpdateOptions().upsert(false));
          ???//UpdateOptions代表批量更新操作未匹配到查詢條件時的動作,默認(rèn)false,什么都不干,true時表示將一個新的Document插入數(shù)據(jù)庫,他是查詢部分和更新部分的結(jié)合
          ???requests.add(uom);
          ??}
          ??BulkWriteResult?bulkWriteResult?=?collection.bulkWrite(requests);
          ??System.out.println(bulkWriteResult.toString());
          ?}

          測試:10萬條數(shù)據(jù)

          System.out.println("開始更新數(shù)據(jù)。。。");
          long?startUpdate?=?System.currentTimeMillis();
          instance.bulkWriteUpdate(documents);
          System.out.println("更新數(shù)據(jù)完成,共耗時:"+(System.currentTimeMillis()?-?startUpdate)+"毫秒");

          結(jié)果:3198毫秒

          (2)、逐條更新

          對比非批量下的更新

          ?public?void?updateOneByOne(List?documents){
          ??for?(Document?document?:?documents)?{
          ???Document?queryDocument?=?new?Document("_id",document.get("_id"));
          ???Document?updateDocument?=?new?Document("$set",new?Document("price","30.6"));
          ???UpdateResult?UpdateResult?=?collection.updateOne(queryDocument,?updateDocument);
          ??}
          ?}

          測試:10萬條數(shù)據(jù)

          System.out.println("開始更新數(shù)據(jù)。。。");
          long?startUpdate?=?System.currentTimeMillis();
          instance.updateOneByOne(documents);
          System.out.println("更新數(shù)據(jù)完成,共耗時:"+(System.currentTimeMillis()?-?startUpdate)+"毫秒");

          結(jié)果:13979毫秒,比批量更新效率低很多

          4、混合批量操作

          bulkWrite()方法可以對不同類型的寫操作進行批量處理,代碼如下:

          ?public?void?bulkWriteMix(){
          ??List>?requests?=?new?ArrayList>();
          ???InsertOneModel??iom?=?new?InsertOneModel(new?Document("name","kobe"));
          ???UpdateManyModel?umm?=?new?UpdateManyModel(new?Document("name","kobe"),?
          ?????new?Document("$set",new?Document("name","James")),new?UpdateOptions().upsert(true));
          ???DeleteManyModel??dmm?=?new?DeleteManyModel(new?Document("name","James"));
          ???requests.add(iom);
          ???requests.add(umm);
          ???requests.add(dmm);
          ???BulkWriteResult?bulkWriteResult?=?collection.bulkWrite(requests);
          ???System.out.println(bulkWriteResult.toString());
          ?}

          注意:updateMany()、deleteMany()兩個方法和insertMany()不同,它倆不是批量操作,而是代表更新(刪除)匹配條件的所有數(shù)據(jù)。

          二、與MySQL性能對比

          1、插入操作

          (1)、批處理插入

          與MongoDB一樣,也是插入Product實體對象,代碼如下

          ?public?void?insertBatch(ArrayList?list)?throws?Exception{
          ??Connection?conn?=?DBUtil.getConnection();
          ??try?{
          ???PreparedStatement?pst?=?conn.prepareStatement("insert?into?t_product?value(?,?,?,?)");
          ???int?count?=?1;
          ???for?(Product?product?:?list)?{
          ????pst.setInt(1,?product.getProductId());
          ????pst.setString(2,?product.getCategory());
          ????pst.setString(3,?product.getName());
          ????pst.setDouble(4,?product.getPrice());
          ????pst.addBatch();
          ????if(count?%?1000?==?0){
          ?????pst.executeBatch();
          ?????pst.clearBatch();//每1000條sql批處理一次,然后置空PreparedStatement中的參數(shù),這樣也能提高效率,防止參數(shù)積累過多事務(wù)超時,但實際測試效果不明顯
          ????}
          ????count++;
          ???}
          ???conn.commit();
          ??}?catch?(SQLException?e)?{
          ???e.printStackTrace();
          ??}
          ??DBUtil.closeConnection(conn);
          ?}

          JDBC默認(rèn)自動提交事務(wù),切記在獲取連接后添加下面一行代碼,關(guān)閉事務(wù)自動提交。

          connection.setAutoCommit(false);

          測試:10萬條數(shù)據(jù)

          public?static?void?main(String[]?args)?throws?Exception?{
          ????????????TestMysql?test?=?new?TestMysql();
          ????????????ArrayList?list?=?new?ArrayList();
          ????????????for?(int?i?=?0;?i?1000;?i++)?{
          ????????????????Product?product?=?new?Product(i,?"書籍",?"追風(fēng)箏的人",?20.5);
          ????????????????list.add(product);
          ????????????}
          ?
          ????????????System.out.println("MYSQL開始插入數(shù)據(jù)。。。");
          ????????????long?insertStart?=?System.currentTimeMillis();
          ????????????test.insertBatch(list);
          ????????????System.out.println("MYSQL插入數(shù)據(jù)完成,共耗時:"+(System.currentTimeMillis()?-?insertStart)+"毫秒");
          }

          結(jié)果:7389毫秒,多次測試基本7秒左右

          (2)、逐條插入

          再來看看mysql逐條插入,代碼如下:

          ?public?void?insertOneByOne(ArrayList?list)?throws?Exception{
          ??Connection?conn?=?DBUtil.getConnection();
          ??try?{
          ???for?(Product?product?:?list)?{
          ????PreparedStatement?pst?=?conn.prepareStatement("insert?into?t_product?value(?,?,?,?)");
          ????pst.setInt(1,?product.getProductId());
          ????pst.setString(2,?product.getCategory());
          ????pst.setString(3,?product.getName());
          ????pst.setDouble(4,?product.getPrice());
          ????pst.executeUpdate();
          ????//conn.commit();//加上這句每次插入都提交事務(wù),結(jié)果將是非常耗時
          ???}
          ???conn.commit();
          ??}?catch?(SQLException?e)?{
          ???e.printStackTrace();
          ??}
          ??DBUtil.closeConnection(conn);
          ?}

          測試:10萬條記錄

          System.out.println("MYSQL開始插入數(shù)據(jù)。。。");
          long?insertStart?=?System.currentTimeMillis();
          test.insertOneByOne(list);
          System.out.println("MYSQL插入數(shù)據(jù)完成,共耗時:"+(System.currentTimeMillis()?-?insertStart)+"毫秒");

          結(jié)果:8921毫秒,基本比批量慢1秒多。

          2、刪除操作

          (1)、批處理刪除

          刪除的where條件是productId,這里在建表的時候沒有添加主鍵,刪除異常的慢,查了半天不知道什么原因。切記添加主鍵,主鍵默認(rèn)有索引,所有能更快匹配到記錄。

          ?public?void?deleteBatch(ArrayList?list)?throws?Exception{
          ??Connection?conn?=?DBUtil.getConnection();
          ??try?{
          ???PreparedStatement?pst?=?conn.prepareStatement("delete?from?t_product?where?id?=??");//按主鍵查,否則全表遍歷很慢
          ???int?count?=?1;
          ???for?(Product?product?:?list)?{
          ????pst.setInt(1,?product.getProductId());
          ????pst.addBatch();
          ????if(count?%?1000?==?0){
          ?????pst.executeBatch();
          ?????pst.clearBatch();
          ????}
          ????count++;
          ???}
          ???conn.commit();
          ??}?catch?(SQLException?e)?{
          ???e.printStackTrace();
          ??}
          ??DBUtil.closeConnection(conn);
          ?}

          測試:10萬條數(shù)據(jù)

          System.out.println("MYSQL開始刪除數(shù)據(jù)。。。");
          long?deleteStart?=?System.currentTimeMillis();
          test.deleteBatch(list);
          System.out.println("MYSQL刪除數(shù)據(jù)完成,共耗時:"+(System.currentTimeMillis()?-?deleteStart)+"毫秒");

          結(jié)果:7936毫秒

          (2)、逐條刪除

          代碼如下

          ?public?void?deleteOneByOne(ArrayList?list)?throws?Exception{
          ??Connection?conn?=?DBUtil.getConnection();
          ??PreparedStatement?pst?=?null;
          ??try?{
          ???for?(Product?product?:?list)?{
          ????pst?=?conn.prepareStatement("delete?from?t_product?where?id?=??");
          ????pst.setInt(1,?product.getProductId());
          ????pst.executeUpdate();
          ????//conn.commit();//加上這句每次插入都提交事務(wù),結(jié)果將是非常耗時
          ???}
          ???
          ???conn.commit();
          ??}?catch?(SQLException?e)?{
          ???e.printStackTrace();
          ??}
          ??DBUtil.closeConnection(conn);
          ?}

          測試:10萬條數(shù)據(jù)

          System.out.println("MYSQL開始刪除數(shù)據(jù)。。。");
          long?deleteStart?=?System.currentTimeMillis();
          test.deleteOneByOne(list);
          System.out.println("MYSQL刪除數(shù)據(jù)完成,共耗時:"+(System.currentTimeMillis()?-?deleteStart)+"毫秒");

          結(jié)果:8752毫秒,比批處理刪除慢一秒左右

          3、更新操作

          (1)、批處理更新

          代碼如下

          ?public?void?updateBatch(ArrayList?list)?throws?Exception{
          ??Connection?conn?=?DBUtil.getConnection();
          ??try?{
          ???PreparedStatement?pst?=?conn.prepareStatement("update?t_product?set?price=31.5?where?id=?");
          ???int?count?=?1;
          ???for?(Product?product?:?list)?{
          ????pst.setInt(1,?product.getProductId());
          ????pst.addBatch();
          ????if(count?%?1000?==?0){
          ?????pst.executeBatch();
          ?????pst.clearBatch();//每1000條sql批處理一次,然后置空PreparedStatement中的參數(shù),這樣也能提高效率,防止參數(shù)積累過多事務(wù)超時,但實際測試效果不明顯
          ????}
          ????count++;
          ???}
          ???conn.commit();
          ??}?catch?(SQLException?e)?{
          ???e.printStackTrace();
          ??}
          ??DBUtil.closeConnection(conn);
          ?}

          測試:10萬條數(shù)據(jù)

          System.out.println("MYSQL開始更新數(shù)據(jù)。。。");
          long?updateStart?=?System.currentTimeMillis();
          test.updateBatch(list);
          System.out.println("MYSQL更新數(shù)據(jù)完成,共耗時:"+(System.currentTimeMillis()?-?updateStart)+"毫秒");

          結(jié)果:8611毫秒

          (2)、逐條更新

          代碼如下

          public?void?updateOneByOne(ArrayList?list)?throws?Exception{
          ??Connection?conn?=?DBUtil.getConnection();
          ??try?{
          ???for?(Product?product?:?list)?{
          ????PreparedStatement?pst?=?conn.prepareStatement("update?t_product?set?price=30.5?where?id=?");
          ????pst.setInt(1,?product.getProductId());
          ????pst.executeUpdate();
          ????//conn.commit();//加上這句每次插入都提交事務(wù),結(jié)果將是非常耗時
          ???}
          ???conn.commit();
          ??}?catch?(SQLException?e)?{
          ???e.printStackTrace();
          ??}
          ??DBUtil.closeConnection(conn);
          ?}

          測試:10萬條數(shù)據(jù)

          System.out.println("MYSQL開始更新數(shù)據(jù)。。。");
          long?updateStart?=?System.currentTimeMillis();
          test.updateOneByOne(list);
          System.out.println("MYSQL更新數(shù)據(jù)完成,共耗時:"+(System.currentTimeMillis()?-?updateStart)+"毫秒");

          結(jié)果:9430毫秒,比批處理更新慢了1秒左右

          三、總結(jié)

          本文主要是為了介紹bulkWrite()方法的使用,也就是MongoDB的批量寫操作,通過實驗可以看出MongoDB使用bulkWrite()方法進行大量數(shù)據(jù)的寫操作比使用常規(guī)的方法進行寫操作效率要高很多。文章也介紹了mysql幾種寫操作下批量和非批量的對比,可以看出他們批處理方式比非批處理快點,但沒有MongoDB那么明顯。

          對于MongoDB與mysql的比較,批量操作下,MongoDB插入、刪除、更新都比mysql快,非批量操作下,MongoDB插入、刪除、更新都比mysql慢。當(dāng)然只是一個粗略的結(jié)論,文中并沒有進行100條、1000條、10000條或更大的這樣不同的數(shù)據(jù)對比,以及CPU內(nèi)存使用情況進行監(jiān)測,有興趣的可以嘗試下。

          來源:https://blog.csdn.net/u014513883/article/details/49365987



          往期推薦



          扔掉 Postman ,來試試神器ApiPost!

          ElasticSearch 讓人嘆為觀止的分布式系統(tǒng)架構(gòu)設(shè)計

          代碼生成利器:IDEA 強大的 Live Templates

          裝上這個插件,讓自己的代碼更規(guī)范!

          MySQL 架構(gòu)總覽、從查詢執(zhí)行流程到SQL 解析順序

          新版 IntelliJ IDEA2021.3 即將來襲,這次又出了哪些神仙功能!



          瀏覽 84
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久婷婷丁香五月 | 色婷婷丁香五月天男人天堂 | 青青草视频分类在线 | 特级黄色电影免费看 | 国产欧美精品久久 |