最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

面試官:從 MySQL 讀取 100w 數(shù)據(jù)進行處理,應(yīng)該怎么做?問倒一大遍!

2023-03-16 14:21 作者:龍哥帶你學編程  | 我要投稿

大數(shù)據(jù)量操作的場景大致如下:

1、?數(shù)據(jù)遷移;
2、?數(shù)據(jù)導出;
3、?批量處理數(shù)據(jù);

在實際工作中當指定查詢數(shù)據(jù)過大時,我們一般使用分頁查詢的方式一頁一頁的將數(shù)據(jù)放到內(nèi)存處理。但有些情況不需要分頁的方式查詢數(shù)據(jù)或分很大一頁查詢數(shù)據(jù)時,如果一下子將數(shù)據(jù)全部加載出來到內(nèi)存中,很可能會發(fā)生OOM(內(nèi)存溢出);而且查詢會很慢,因為框架耗費大量的時間和內(nèi)存去把數(shù)據(jù)庫查詢的結(jié)果封裝成我們想要的對象(實體類)。

舉例:在業(yè)務(wù)系統(tǒng)需要從 MySQL 數(shù)據(jù)庫里讀取 100w 數(shù)據(jù)行進行處理,應(yīng)該怎么做?

做法通常如下:

1、?常規(guī)查詢:一次性讀取100w數(shù)據(jù)到JVM內(nèi)存中,或者分頁讀??;
2、?流式查詢:建立長連接,利用服務(wù)端游標,每次讀取一條加載到JVM內(nèi)存(多次獲取,一次一行);
3、?游標查詢:和流式一樣,通過fetchSize參數(shù),控制一次讀取多少條數(shù)據(jù)(多次獲取,一次多行);

常規(guī)查詢

默認情況下,完整的檢索結(jié)果集會將其存儲在內(nèi)存中。在大多數(shù)情況下,這是最有效的操作方式,并且由于 MySQL 網(wǎng)絡(luò)協(xié)議的設(shè)計,因此更易于實現(xiàn)。

舉例:

假設(shè)單表 100w 數(shù)據(jù)量,一般會采用分頁的方式查詢:

注:該示例使用的 MybatisPlus

該方式比較簡單,如果在不考慮 LIMIT 深分頁優(yōu)化情況下,估計你的數(shù)據(jù)庫服務(wù)器就噶皮了,或者你能等上幾十分鐘或幾小時,甚至幾天時間檢索數(shù)據(jù)

流式查詢

流式查詢指的是查詢成功后不是返回一個集合而是返回一個迭代器,應(yīng)用每次從迭代器取一條查詢結(jié)果。流式查詢的好處是能夠降低內(nèi)存使用。如果沒有流式查詢,我們想要從數(shù)據(jù)庫取 100w 條記錄而又沒有足夠的內(nèi)存時,就不得不分頁查詢,而分頁查詢效率取決于表設(shè)計,如果設(shè)計的不好,就無法執(zhí)行高效的分頁查詢。因此流式查詢是一個數(shù)據(jù)庫訪問框架必須具備的功能。

MyBatis 中使用流式查詢避免數(shù)據(jù)量過大導致 OOM ,但在流式查詢的過程當中,數(shù)據(jù)庫連接是保持打開狀態(tài)的,因此要注意的是:

1、?執(zhí)行一個流式查詢后,數(shù)據(jù)庫訪問框架就不負責關(guān)閉數(shù)據(jù)庫連接了,需要應(yīng)用在取完數(shù)據(jù)后自己關(guān)閉;
2、?必須先讀?。ɑ蜿P(guān)閉)結(jié)果集中的所有行,然后才能對連接發(fā)出任何其他查詢,否則將引發(fā)異常;

MyBatis 流式查詢接口

MyBatis 提供了一個叫 org.apache.ibatis.cursor.Cursor 的接口類用于流式查詢,這個接口繼承了 java.io.Closeable 和 java.lang.Iterable 接口,由此可知:

1、?Cursor是可關(guān)閉的;
2、?Cursor是可遍歷的;

除此之外,Cursor 還提供了三個方法:

1、?isOpen():用于在取數(shù)據(jù)之前判斷Cursor對象是否是打開狀態(tài)只有當打開時Cursor才能取數(shù)據(jù);
2、?isConsumed():用于判斷查詢結(jié)果是否全部取完;
3、?getCurrentIndex():返回已經(jīng)獲取了多少條數(shù)據(jù);

使用流式查詢,則要保持對產(chǎn)生結(jié)果集的語句所引用的表的并發(fā)訪問,因為其 查詢會獨占連接,所以必須盡快處理

為什么要用流式查詢?

如果有一個很大的查詢結(jié)果需要遍歷處理,又不想一次性將結(jié)果集裝入客戶端內(nèi)存,就可以考慮使用流式查詢;

分庫分表場景下,單個表的查詢結(jié)果集雖然不大,但如果某個查詢跨了多個庫多個表,又要做結(jié)果集的合并、排序等動作,依然有可能撐爆內(nèi)存;詳細研究了sharding-sphere的代碼不難發(fā)現(xiàn),除了group by與order by字段不一樣之外,其他的場景都非常適合使用流式查詢,可以最大限度的降低對客戶端內(nèi)存的消耗。

關(guān)于流式查詢查詢小編本人了解不是很多,再此就不過多說明,如果發(fā)現(xiàn)好的資源,還請留言讓小編也學習一下。

游標查詢

對大量數(shù)據(jù)進行處理時,為防止內(nèi)存泄漏情況發(fā)生,也可以采用游標方式進行數(shù)據(jù)查詢處理。這種處理方式比常規(guī)查詢要快很多。

當查詢百萬級的數(shù)據(jù)的時候,還可以使用游標方式進行數(shù)據(jù)查詢處理,不僅可以節(jié)省內(nèi)存的消耗,而且還不需要一次性取出所有數(shù)據(jù),可以進行逐條處理或逐條取出部分批量處理。一次查詢指定 fetchSize 的數(shù)據(jù),直到把數(shù)據(jù)全部處理完。

Mybatis 的處理加了兩個注解:@Options 和 @ResultType

@Options

  • ResultSet.FORWORD_ONLY:結(jié)果集的游標只能向下滾動

  • ResultSet. SCROLL_INSENSITIVE:結(jié)果集的游標可以上下移動,當數(shù)據(jù)庫變化時,當前結(jié)果集不變

  • ResultSet.SCROLL_SENSITIVE:返回可滾動的結(jié)果集,當數(shù)據(jù)庫變化時,當前結(jié)果集同步改變

  • fetchSize:每次獲取量

@ResultType

@ResultType(BigDataSearchEntity.class):轉(zhuǎn)換成返回實體類型

注意:?返回類型必須為 void ,因為查詢的結(jié)果在 ResultHandler 里處理數(shù)據(jù),所以這個 hander 也是必須的,可以使用 lambda 實現(xiàn)一個依次處理邏輯。

注意:

雖然上面的代碼中都有 @Options 但實際操作卻有不同:

1、?方式一是多次查詢,一次返回多條;
2、?方式二是一次查詢,一次返回一條;

原因:

Oracle 是從服務(wù)器一次取出 fetch size 條記錄放在客戶端,客戶端處理完成一個批次后再向服務(wù)器取下一個批次,直到所有數(shù)據(jù)處理完成。

MySQL 是在執(zhí)行 ResultSet.next() 方法時,會通過數(shù)據(jù)庫連接一條一條的返回。flush buffer 的過程是阻塞式的,如果網(wǎng)絡(luò)中發(fā)生了擁塞,send buffer 被填滿,會導致 buffer 一直 flush 不出去,那 MySQL 的處理線程會阻塞,從而避免數(shù)據(jù)把客戶端內(nèi)存撐爆。

非流式查詢和流式查詢區(qū)別:

1、?非流式查詢:內(nèi)存會隨著查詢記錄的增長而近乎直線增長;
2、?流式查詢:內(nèi)存會保持穩(wěn)定,不會隨著記錄的增長而增長其內(nèi)存大小取決于批處理大小BATCH_SIZE的設(shè)置,該尺寸越大,內(nèi)存會越大所以BATCH_SIZE應(yīng)該根據(jù)業(yè)務(wù)情況設(shè)置合適的大??;

另外要切記每次處理完一批結(jié)果要記得釋放存儲每批數(shù)據(jù)的臨時容器,即上文中的gxids.clear();


面試官:從 MySQL 讀取 100w 數(shù)據(jù)進行處理,應(yīng)該怎么做?問倒一大遍!的評論 (共 條)

分享到微博請遵守國家法律
龙南县| 高州市| 连州市| 三原县| 佛坪县| 巴青县| 吉隆县| 中山市| 澜沧| 铜川市| 延川县| 名山县| 寿光市| 卢湾区| 彰化县| 昭平县| 昆山市| 前郭尔| 自治县| 福建省| 右玉县| 潞西市| 大余县| 十堰市| 寿阳县| 齐河县| 新营市| 湖口县| 岗巴县| 丽水市| 南雄市| 饶阳县| 漳州市| 定边县| 阿拉善右旗| 永胜县| 北宁市| 栖霞市| 崇州市| 延长县| 衡南县|