Mongodb億級(jí)數(shù)據(jù)性能測試和壓測
一,mongodb數(shù)據(jù)性能測試
之前公司將用戶的游戲數(shù)據(jù)存儲(chǔ)在mysql中,就是直接將json數(shù)據(jù)存儲(chǔ)到mysql數(shù)據(jù)庫里面,幾個(gè)月不到,數(shù)據(jù)庫里面已經(jīng)有兩億條數(shù)據(jù),而且每行中每個(gè)json數(shù)據(jù)量也比較大,導(dǎo)致占用的磁盤容量也比較大,因此為了解決mysql帶來多方面的瓶頸,最終選擇使用mongodb來代替mysql。為了測試mongodbdb的性能以及是否滿足需求,因此做了以下測試,對mongodb在高流量時(shí)驗(yàn)證其增刪改查的效率,以及對其進(jìn)行壓測
服務(wù)器配置:2核4g輕量級(jí)服務(wù)器 磁盤容量 70GB
每條數(shù)據(jù)大概在500個(gè)字節(jié),索引有一個(gè)id主鍵索引,還有一個(gè)parentId和category的聯(lián)合唯一索引,這里兩個(gè)字段能保證唯一性,因此用唯一索引效率更優(yōu)
1,mongodb數(shù)據(jù)庫創(chuàng)建和索引設(shè)置
首先在Java代碼中創(chuàng)建一個(gè)實(shí)體類,用這個(gè)類作為json對象插入到文檔中即可。
@Data public class Archive { private String id; //賬號(hào)id private String parentId; private String category; private String content; }
隨后在mongodb中創(chuàng)建一個(gè)數(shù)據(jù)庫,然后再該庫下面建立一個(gè)名為 archive 的集合,mongodb的集合就是類似于mysql的表,兩者概念是一樣的。由于后期數(shù)據(jù)量可能非常大,因此根據(jù)mongodb官方文檔所說,在數(shù)據(jù)插入前,盡量提前建立索引,為了滿足業(yè)務(wù)需求,這里選擇創(chuàng)建一個(gè)聯(lián)合索引,由于我這邊業(yè)務(wù)能保證要加索引的兩個(gè)字段的唯一性,因此選擇直接添加唯一索引
db.users.createIndex({parentId: 1,category:1}, {unique: true})
如果navicate操作不方便的話,可以安裝一個(gè) Mongodb Compass 可視化工具,如下圖,很多操作都是可以在這個(gè)可視化圖形界面上面直接操作的
2,線程池+批量方式插入數(shù)據(jù)
由于這邊主要是io操作將數(shù)據(jù)插入,不需要計(jì)算之類的,因此選擇使用io密集型線程池,接下來自定義一個(gè)線程池
@Slf4j public class ThreadPoolUtil { public static ThreadPoolExecutor pool = null; public static synchronized ThreadPoolExecutor getThreadPool() { if (pool == null) { //獲取當(dāng)前機(jī)器的cpu int cpuNum = Runtime.getRuntime().availableProcessors(); int maximumPoolSize = cpuNum * 2 ; pool = new ThreadPoolExecutor( maximumPoolSize - 2, maximumPoolSize, 5L, //5s TimeUnit.SECONDS, new LinkedBlockingQueue<>(), //數(shù)組有界隊(duì)列 Executors.defaultThreadFactory(), //默認(rèn)的線程工廠 new ThreadPoolExecutor.AbortPolicy()); //直接拋異常,默認(rèn)異常 } return pool; } }
第二步就是定義一個(gè)線程任務(wù),到時(shí)將任務(wù)丟到線程池里面,其代碼如下,該任務(wù)實(shí)現(xiàn)Callable接口,每個(gè)線程插入10萬條,每次批量插入100條數(shù)據(jù),大概就是需要1000次
@Data public class ArchiveTask implements Callable { private MongoTemplate mongoTemplate; public ArchiveTask(MongoTemplate mongoTemplate){ this.mongoTemplate = mongoTemplate; } @Override public Object call() throws Exception { List<Archive> list = new ArrayList<>(); for (int i = 1; i <= 100000; i++) { Archive archive = new Archive(); archive.setCategory("score"); archive.setId(SnowflakeUtils.nextOrderId()); archive.setParentId(SnowflakeUtils.nextOrderId()); Map<String,String> map = new HashMap<>(); StringBuilder sb = new StringBuilder(); for (int j = 0; j < 15; j++) { sb.append(UUID.randomUUID()); } map.put("key" + i, sb.toString()); archive.setContent(JSON.toJSONString(map)); list.add(archive); if (i%100 == 0){ mongoTemplate.insertAll(list); list.clear(); //手動(dòng)gc,100個(gè)對象沒被引用會(huì)被回收 list = new ArrayList<>(); } } return null; } }
最后定義一個(gè)測試類或者一個(gè)接口,我這邊使用接口,部分代碼如下,循環(huán)100次,就是會(huì)創(chuàng)建100個(gè)線程任務(wù),隨后將這個(gè)線程任務(wù)丟到線程池中,100乘以100000就是1千萬條數(shù)據(jù)
@Resource private MongoTemplate mongoTemplate; static ThreadPoolExecutor threadPool = ThreadPoolUtil.getThreadPool(); @GetMapping("/add") public void test(){ for (int i = 0; i < 100; i++) { ArchiveTask archiveTask = new ArchiveTask(mongoTemplate); threadPool.submit(archiveTask); } log.info("數(shù)據(jù)添加完成"); }
3,一千萬數(shù)據(jù)性能測試
mongodb性能測試,此時(shí)archive 集合中已有10134114條數(shù)據(jù),平均每條數(shù)據(jù)大小674字節(jié),1千多萬條,此時(shí)的存儲(chǔ)大小為5.5個(gè)g,索引的總大小為459m
接下來通過唯一索引查詢一條數(shù)據(jù),這里直接通過parentId查詢一條數(shù)據(jù),此時(shí)數(shù)據(jù)還是在不斷插入的
db.archive.find({parentId:"2405291858848274156091867143"})
是的,如下圖所示,1000多萬條數(shù)據(jù)里面查詢,只需要25ms即可將數(shù)據(jù)放回,當(dāng)然這里沒有在高流量的情況下進(jìn)行壓測。
4,兩千萬數(shù)據(jù)性能測試
此時(shí)archive集合來到了兩千萬條,每條數(shù)據(jù)和之前一樣,平均大小是674字節(jié),數(shù)據(jù)總大小來到了10.92G,內(nèi)存大小12.65g,索引總大小是913m
接下來測試查詢效率,依舊使用上面的這個(gè)parentId,由于設(shè)置的是parentId+category的聯(lián)合唯一索引,接下來兩個(gè)參數(shù)一起查
db.archive.find({parentId:"2405291858848274156091867143",category:"score"})
2000萬的數(shù)據(jù)查詢結(jié)果如下,只需要21ms,和上面的25ms慢了將近4ms,但是這4ms可以忽略
5,五千萬數(shù)據(jù)性能測試
由于70G的磁盤容量已經(jīng)只剩48G,因此在content字段將500字節(jié)的值調(diào)小,調(diào)整到150個(gè)字節(jié),以便能插入更多數(shù)據(jù)。將上面的StringBuilder拼接的15個(gè)uuid改成1個(gè)uuid
map.put("key" + i,UUID.randomUUID().toString());
此時(shí)數(shù)據(jù)來到50245694條數(shù)據(jù),每條數(shù)據(jù)平均大小372kb,總存儲(chǔ)大小12.66g,內(nèi)存中的總大小17.45g,索引大小目前只有2.8g
為了保證拿到的parentId是一次沒有查詢過的,手動(dòng)的插入一批數(shù)據(jù),手動(dòng)單條插入20條數(shù)據(jù),耗時(shí)600ms,在插入數(shù)據(jù)時(shí)會(huì)改變索引,插入數(shù)據(jù)會(huì)稍微慢些。此時(shí)的插入操作都是在多線程插入大量數(shù)據(jù)的時(shí)候測試的
db.archive.insertOne({parentId:"2024111222337",category:"score1",content:"cbasbsadhpasdbsaodgs"}) db.archive.insertOne({parentId:"2024111222337",category:"score2",content:"cbasbsadhpasdbsaodgs"}) ....
此時(shí)第一次查詢這條數(shù)據(jù),共耗時(shí)153ms,共查出20條數(shù)據(jù)
再第二次查詢之后,花費(fèi)78ms,內(nèi)部應(yīng)該也是會(huì)將查詢結(jié)果加入到緩存中,方便第二次查詢
在上面的插入操作中由于會(huì)破壞到索引結(jié)構(gòu),因此耗時(shí)久一點(diǎn)。接下來看這個(gè)更新操作,
db.archive.updateOne( { parentId: "2024111222337",category:"score1" }, { $set: { content: "cbasbsadhpasdbsaodgsscore" } } );
其結(jié)果如下,更新了一條數(shù)據(jù),只花費(fèi)了13毫秒的時(shí)間,因此更新操作速度是很快的。由于這里每一條數(shù)據(jù)都是唯一數(shù)據(jù),因此不測試批量更新
最后測試刪除數(shù)據(jù),將這20條數(shù)據(jù)全部刪除,總共花費(fèi)18毫秒
6,一億條數(shù)據(jù)性能測試
數(shù)據(jù)通過多線程+批量插入的方式來到一億條,存儲(chǔ)大小15.5g,索引長度是6g
db.archive.countDocuments() //查詢共有多少條數(shù)據(jù) 100082694
接下來往里面重新插入一部分?jǐn)?shù)據(jù),往里面插入20條數(shù)據(jù),大概花費(fèi)160多ms,插入數(shù)據(jù)會(huì)導(dǎo)致索引重構(gòu),所以耗時(shí)久一些,批量插入性能會(huì)更快。重新插入的數(shù)據(jù)可以保證這條數(shù)據(jù)沒被查過,并且知道parentId是什么
db.archive.insertOne({parentId:"20240531101059",category:"score1",content:"abcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxy"}) ....
接下來測試查詢數(shù)據(jù),只需要19ms
db.archive.find({parentId:"20240531101054"},{parentId:1,category:1}) //只返回部分字段 db.archive.find({parentId:"20240531101058"})
更新數(shù)據(jù)如下,只需要10ms
db.archive.updateOne( { parentId: "20240531101059",category:"score1" }, { $set: { content: "cbasbsadhpasdbsaodgsscore" } } );
7,壓測
以下壓測都是數(shù)據(jù)達(dá)到1億之后進(jìn)行測試的,并且都是使用的2核4g的服務(wù)器
在1s內(nèi)同時(shí)1000個(gè)線程插入數(shù)據(jù),每個(gè)線程插入20條數(shù)據(jù),中位數(shù)24,吞吐量391
在1s內(nèi)10000個(gè)線程插入數(shù)據(jù),也是每個(gè)線程批量插入20條數(shù)據(jù),可以發(fā)現(xiàn)就算是2核4g這么垃圾的輕量級(jí)服務(wù)器,10000qps也是毫無壓力的
插入數(shù)據(jù)會(huì)破壞索引,相對于修改和查詢是更慢的,接下來測試1s內(nèi)10000個(gè)線程同時(shí)執(zhí)行增改查,吞吐量可以達(dá)到2251.7
部分代碼片段如下,讓10000個(gè)線程隨機(jī)的執(zhí)行增改查的操作,在1s內(nèi)是毫無壓力的
8,總結(jié)
通過上面的數(shù)據(jù)以及mongodb的響應(yīng)來看,mongodb的性能還是非常不錯(cuò)的??纯碐PT對這種數(shù)據(jù)的評價(jià),gpt也認(rèn)為mongodb是非常合適的。當(dāng)然不管什么數(shù)據(jù)和業(yè)務(wù),只要其本質(zhì)是 json 數(shù)據(jù),不管json內(nèi)部結(jié)構(gòu)多復(fù)雜,用mongodb都是非常合適的。mongodb還適合存一些訂單數(shù)據(jù),地理數(shù)據(jù),大數(shù)據(jù)等等,其應(yīng)用范圍是非常廣泛的
以上就是Mongodb億級(jí)數(shù)據(jù)性能測試和壓測的詳細(xì)內(nèi)容,更多關(guān)于Mongodb數(shù)據(jù)性能測試的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
MongoDB模糊查詢正則regex(類似like?和?not?like)
在類關(guān)系型數(shù)據(jù)庫中,like和not?like是常用的模糊查詢操作符,它允許我們在匹配字段的時(shí)候使用通配符,在MongoDB中,也有類似的操作符,MongoDB?可以使用?$regex?操作符來設(shè)置匹配字符串的正則表達(dá)式,MongoDB?使用?PCRE(Perl?兼容的正則表達(dá)式)作為正則表達(dá)式語言2024-02-02MongoDB學(xué)習(xí)筆記—Linux下搭建MongoDB環(huán)境
本篇文章主要介紹了Linux下搭建MongoDB環(huán)境,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-11-11解決MAC上啟動(dòng)mongod報(bào)錯(cuò)exiting with code 1的問題
這篇文章主要介紹了解決MAC上啟動(dòng)mongod報(bào)錯(cuò)exiting with code 1的問題,本文給大家介紹的非常詳細(xì)對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12Mongodb3.0.5 副本集搭建及spring和java連接副本集配置詳細(xì)介紹
這篇文章主要介紹了Mongodb3.0.5 副本集搭建及spring和java連接副本集配置詳細(xì)介紹的相關(guān)資料,這里對安裝步驟進(jìn)行了詳解,需要的朋友可以參考下2016-12-12MongoDB的$sample、aggregate和$rand實(shí)現(xiàn)隨機(jī)選取數(shù)據(jù)
在MongoDB中,我們可以使用內(nèi)置的$sample聚合操作符來隨機(jī)生成數(shù)據(jù),$sample可以從集合文檔中隨機(jī)選擇指定數(shù)量的文檔,但由于其查詢整個(gè)集合的性能問題,應(yīng)該慎用,aggregate方法以及$rand函數(shù)的結(jié)合使用可以實(shí)現(xiàn)更加靈活的查詢操作,并且可以對查詢結(jié)果進(jìn)行精細(xì)篩選2024-01-01MongoDB的啟動(dòng)方法詳細(xì)總結(jié)
MongoDB是一個(gè)基于分布式文件存儲(chǔ)的數(shù)據(jù)庫,下面這篇文章主要給大家介紹了關(guān)于MongoDB啟動(dòng)方法的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-03-03centos yum 安裝 mongodb 以及php擴(kuò)展
MongoDB是一個(gè)基于分布式文件存儲(chǔ)的數(shù)據(jù)庫。由C++語言編寫。旨在為WEB應(yīng)用提供可擴(kuò)展的高性能數(shù)據(jù)存儲(chǔ)解決方案。2014-07-07mongodb設(shè)置后臺(tái)運(yùn)行的方法
這篇文章主要介紹了mongodb設(shè)置后臺(tái)運(yùn)行的方法,本文同時(shí)給出了關(guān)閉已在后臺(tái)運(yùn)行mongodb的方法,需要的朋友可以參考下2014-09-09