摘要:引言前段時間遇到一個類似導(dǎo)出數(shù)據(jù)場景,觀察下來發(fā)現(xiàn)速度會越來越慢,導(dǎo)出萬數(shù)據(jù)需要耗費分鐘,從日志觀察發(fā)現(xiàn),耗時也是越來越高。而優(yōu)化后的方式,平均耗時在之間,總耗時中間包括業(yè)務(wù)邏輯的耗時。
引言
原因前段時間遇到一個類似導(dǎo)出數(shù)據(jù)場景,觀察下來發(fā)現(xiàn)速度會越來越慢,導(dǎo)出100萬數(shù)據(jù)需要耗費40-60分鐘,從日志觀察發(fā)現(xiàn),耗時也是越來越高。
從代碼邏輯上看,這里采取了分批次導(dǎo)出的方式,類似前端的分頁,具體是通過skip+limit的方式實現(xiàn)的,那么采用這種方式會有什么問題呢?我們google一下這兩個接口的文檔:
The?cursor.skip()?method is often expensive because it requires the server to walk from the beginning of the collection or index to get the offset or skip position before beginning to return results. As the offset (e.g.?pageNumber?above) increases,?cursor.skip()?will become slower and more CPU intensive. With larger collections,?cursor.skip()?may become IO bound.
簡單來說,隨著頁數(shù)的增長,skip()會變得越來越慢,但是具體就我們這里導(dǎo)出的場景來說,按理說應(yīng)該沒必要每次都去重復(fù)計算,做一些無用功,我的理解應(yīng)該可以拿到一個指針,慢慢遍歷,簡單google之后,我們發(fā)現(xiàn)果然是可以這樣做的。
我們可以在持久層新增一個方法,返回一個cursor專門供上層去遍歷數(shù)據(jù),這樣就不用再去遍歷已經(jīng)導(dǎo)出過的結(jié)果集,從O(N2)優(yōu)化到了O(N),這里還可以指定一個batchSize,設(shè)置一次從MongoDB中抓取的數(shù)據(jù)量(元素個數(shù)),注意這里最大是4M.
/** *Limits the number of elements returned in one batch. A cursor * typically fetches a batch of result objects and store them * locally.
* *If {@code batchSize} is positive, it represents the size of each batch of objects retrieved. It can be adjusted to optimize * performance and limit data transfer.
* *If {@code batchSize} is negative, it will limit of number objects returned, that fit within the max batch size limit (usually * 4MB), and cursor will be closed. For example if {@code batchSize} is -10, then the server will return a maximum of 10 documents and * as many as can fit in 4MB, then close the cursor. Note that this feature is different from limit() in that documents must fit within * a maximum size, and it removes the need to send a request to close the cursor server-side.
*/
比如說我這里配置的8000,那么mongo客戶端就會去默認(rèn)抓取這么多的數(shù)據(jù)量:
經(jīng)過本地簡單的測試,我們發(fā)現(xiàn)性能已經(jīng)有了飛躍的提升,導(dǎo)出30萬數(shù)據(jù),采用之前的方式,翻頁到后面平均要500ms,總耗時60039ms。而優(yōu)化后的方式,平均耗時在100ms-200ms之間,總耗時16667ms(中間包括業(yè)務(wù)邏輯的耗時)。
使用DBCursor cursor = collection.find(query).batchSize(8000); while (dbCursor.hasNext()) { DBObject nextItem = dbCursor.next(); //業(yè)務(wù)代碼 ... // }
那么我們再看看hasNext內(nèi)部的邏輯好嗎?好的.
@Override public boolean hasNext() { if (closed) { throw new IllegalStateException("Cursor has been closed"); } if (nextBatch != null) { return true; } if (limitReached()) { return false; } while (serverCursor != null) { //這里會向mongo發(fā)送一條指令去抓取數(shù)據(jù) getMore(); if (nextBatch != null) { return true; } } return false; } private void getMore() { Connection connection = connectionSource.getConnection(); try { if(serverIsAtLeastVersionThreeDotTwo(connection.getDescription()){ try { //可以看到這里其實是調(diào)用了`nextBatch`指令 initFromCommandResult(connection.command(namespace.getDatabaseName(), asGetMoreCommandDocument(), false, new NoOpFieldNameValidator(), CommandResultDocumentCodec.create(decoder, "nextBatch"))); } catch (MongoCommandException e) { throw translateCommandException(e, serverCursor); } } else { initFromQueryResult(connection.getMore(namespace, serverCursor.getId(), getNumberToReturn(limit, batchSize, count), decoder)); } if (limitReached()) { killCursor(connection); } } finally { connection.release(); } }
最后initFromCommandResult 拿到結(jié)果并解析成Bson對象
總結(jié)我們平常寫代碼的時候,最好都能夠針對每個方法、接口甚至是更細(xì)的粒度加上埋點,也可以設(shè)置成debug級別,這樣利用log4j/logback等日志框架動態(tài)更新級別,可以隨時查看耗時,從而更能夠針對性的優(yōu)化,比如Spring有個有個工具類StopWatch就可以做這件事.
對于本文說的這個場景,我們首先看看是不是代碼的邏輯有問題,然后看看是不是數(shù)據(jù)庫的問題,比如說沒建索引、數(shù)據(jù)量過大等,再去想辦法針對性的優(yōu)化,而不要上來就擼代碼。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/19036.html
摘要:最近閱讀了很多優(yōu)秀的網(wǎng)站性能優(yōu)化的文章,所以自己也想總結(jié)一些最近優(yōu)化的手段和方法。個人感覺性能優(yōu)化的核心是減少延遲,加速展現(xiàn)。初步以為是這個功能導(dǎo)致的服務(wù)掛起,詢問相關(guān)操作人員,得到當(dāng)時的操作過程?! ∽罱喿x了很多優(yōu)秀的網(wǎng)站性能優(yōu)化的文章,所以自己也想總結(jié)一些最近優(yōu)化的手段和方法。 個人感覺性能優(yōu)化的核心是:減少延遲,加速展現(xiàn)。 本文主要從產(chǎn)品設(shè)計、前端、后端和網(wǎng)絡(luò)四個...
摘要:年月日本文是關(guān)于記錄某次游戲服務(wù)端的性能優(yōu)化此處涉及的技術(shù)包括引擎隨著游戲?qū)肴藬?shù)逐漸增加單個集合的文檔數(shù)已經(jīng)超過經(jīng)常有玩家反饋說卡特別是在服務(wù)器遷移后從核降到核卡頓更嚴(yán)重了遂開始排查問題確認(rèn)服務(wù)器壓力首先使用命令查看總體情況此時占用不高 Last-Modified: 2019年6月13日11:08:19 本文是關(guān)于記錄某次游戲服務(wù)端的性能優(yōu)化, 此處涉及的技術(shù)包括: MongoDB...
摘要:年月日本文是關(guān)于記錄某次游戲服務(wù)端的性能優(yōu)化此處涉及的技術(shù)包括引擎隨著游戲?qū)肴藬?shù)逐漸增加單個集合的文檔數(shù)已經(jīng)超過經(jīng)常有玩家反饋說卡特別是在服務(wù)器遷移后從核降到核卡頓更嚴(yán)重了遂開始排查問題確認(rèn)服務(wù)器壓力首先使用命令查看總體情況此時占用不高 Last-Modified: 2019年6月13日11:08:19 本文是關(guān)于記錄某次游戲服務(wù)端的性能優(yōu)化, 此處涉及的技術(shù)包括: MongoDB...
閱讀 2960·2021-11-23 09:51
閱讀 3786·2021-11-22 15:29
閱讀 3244·2021-10-08 10:05
閱讀 1568·2021-09-22 15:20
閱讀 983·2019-08-30 15:56
閱讀 1081·2019-08-30 15:54
閱讀 741·2019-08-26 11:54
閱讀 2643·2019-08-26 11:32