0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

MyBatis Plus解決大數據量查詢慢問題

jf_ro2CN3Fa ? 來源:CSDN ? 2023-01-16 10:17 ? 次閱讀

  • 常規(guī)查詢
  • 流式查詢
  • 游標查詢

大數據量操作的場景大致如下:

  • 數據遷移
  • 數據導出
  • 批量處理數據

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

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

做法通常如下:

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

常規(guī)查詢

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

舉例:

假設單表 100w 數據量,一般會采用分頁的方式查詢:

@Mapper
publicinterfaceBigDataSearchMapperextendsBaseMapper<BigDataSearchEntity>{

@Select("SELECTbds.*FROMbig_data_searchbds${ew.customSqlSegment}")
PagepageList(@Param("page")Pagepage,@Param(Constants.WRAPPER)QueryWrapperqueryWrapper);

}

注:該示例使用的 MybatisPlus

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

基于 Spring Boot + MyBatis Plus + Vue & Element 實現的后臺管理系統(tǒng) + 用戶小程序,支持 RBAC 動態(tài)權限、多租戶、數據權限、工作流、三方登錄、支付、短信、商城等功能

  • 項目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 視頻教程:https://doc.iocoder.cn/video/

流式查詢

流式查詢指的是查詢成功后不是返回一個集合而是返回一個迭代器,應用每次從迭代器取一條查詢結果。流式查詢的好處是能夠降低內存使用。

如果沒有流式查詢,我們想要從數據庫取 100w 條記錄而又沒有足夠的內存時,就不得不分頁查詢,而分頁查詢效率取決于表設計,如果設計的不好,就無法執(zhí)行高效的分頁查詢。因此流式查詢是一個數據庫訪問框架必須具備的功能。

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

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

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

  • Cursor 是可關閉的;
  • Cursor 是可遍歷的。

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

  • isOpen(): 用于在取數據之前判斷 Cursor 對象是否是打開狀態(tài)。只有當打開時 Cursor 才能取數據;
  • isConsumed(): 用于判斷查詢結果是否全部取完。
  • getCurrentIndex(): 返回已經獲取了多少條數據

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

為什么要用流式查詢?

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

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

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實現的后臺管理系統(tǒng) + 用戶小程序,支持 RBAC 動態(tài)權限、多租戶、數據權限、工作流、三方登錄、支付、短信、商城等功能

  • 項目地址:https://github.com/YunaiV/yudao-cloud
  • 視頻教程:https://doc.iocoder.cn/video/

游標查詢

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

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

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

@Mapper
publicinterfaceBigDataSearchMapperextendsBaseMapper<BigDataSearchEntity>{

//方式一多次獲取,一次多行
@Select("SELECTbds.*FROMbig_data_searchbds${ew.customSqlSegment}")
@Options(resultSetType=ResultSetType.FORWARD_ONLY,fetchSize=1000000)
PagepageList(@Param("page")Pagepage,@Param(Constants.WRAPPER)QueryWrapperqueryWrapper);

//方式二一次獲取,一次一行
@Select("SELECTbds.*FROMbig_data_searchbds${ew.customSqlSegment}")
@Options(resultSetType=ResultSetType.FORWARD_ONLY,fetchSize=100000)
@ResultType(BigDataSearchEntity.class)
voidlistData(@Param(Constants.WRAPPER)QueryWrapper<BigDataSearchEntity>queryWrapper,ResultHandler<BigDataSearchEntity>handler);

}

@Options

  • ResultSet.FORWORD_ONLY:結果集的游標只能向下滾動
  • ResultSet.SCROLL_INSENSITIVE:結果集的游標可以上下移動,當數據庫變化時,當前結果集不變
  • ResultSet.SCROLL_SENSITIVE:返回可滾動的結果集,當數據庫變化時,當前結果集同步改變
  • fetchSize:每次獲取量

@ResultType

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

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

注意:

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

  • 方式一是多次查詢,一次返回多條;
  • 方式二是一次查詢,一次返回一條;

原因:

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

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

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

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

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

審核編輯 :李倩


聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 數據庫
    +關注

    關注

    7

    文章

    3739

    瀏覽量

    64181
  • MySQL
    +關注

    關注

    1

    文章

    794

    瀏覽量

    26359
  • 數據遷移
    +關注

    關注

    0

    文章

    66

    瀏覽量

    6931

原文標題:MyBatis Plus 解決大數據量查詢慢問題

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    大數據從業(yè)者必知必會的Hive SQL調優(yōu)技巧

    大數據從業(yè)者必知必會的Hive SQL調優(yōu)技巧 摘要 :在大數據領域中,Hive SQL被廣泛應用于數據倉庫的數據查詢和分析。然而,由于數據量
    的頭像 發(fā)表于 09-24 13:30 ?118次閱讀

    使用mybatis切片實現數據權限控制

    一、使用方式 數據權限控制需要對查詢出的數據進行篩選,對業(yè)務入侵最少的方式就是利用mybatis或者數據庫連接池的切片對已有業(yè)務的sql進行
    的頭像 發(fā)表于 07-09 17:26 ?296次閱讀
    使用<b class='flag-5'>mybatis</b>切片實現<b class='flag-5'>數據</b>權限控制

    CC2640R2F BLE如何實現一次連接事件傳輸的數據量為500字節(jié),或者更大?

    您好,我想實現一次連接事件傳輸的數據量為500字節(jié),或者更大。是如何實現的? MTU設置成255,應該是可以傳輸251字節(jié)數據。MAX_NUM_PDU設置成5,應該可以傳送251*5=1255字節(jié)吧?目前傳輸超過251字節(jié)的數據
    發(fā)表于 05-30 06:12

    藍牙Mesh模塊多跳大數據量高帶寬傳輸數據方法

    通過多個跳數進行通信,從而實現大范圍的覆蓋。然而,隨著數據量的增加和帶寬需求的提高,如何在藍牙Mesh網絡中實現高效、穩(wěn)定的多跳大數據量高帶寬傳輸數據成為了一個亟待解決的問題。本文將介紹一種基于藍牙Mesh模塊的多跳
    的頭像 發(fā)表于 05-28 11:23 ?573次閱讀
    藍牙Mesh模塊多跳<b class='flag-5'>大數據量</b>高帶寬傳輸<b class='flag-5'>數據</b>方法

    大數據技術是干嘛的 大數據核心技術有哪些

    的核心技術,包括數據采集、存儲與管理、處理與分析等方面。 一、大數據技術背景和概念 1.1 背景 隨著互聯(lián)網技術的迅猛發(fā)展,人們可以通過各種途徑產生、獲取和傳輸數據,使數據量呈現爆炸式
    的頭像 發(fā)表于 01-31 11:07 ?2812次閱讀

    串口中斷函數中,接收的數據量已經超過了FIFO的長度,會不會造成數據丟失呀?

    如果在串口中斷函數中,正在讀緩沖區(qū)的中的數據(還沒有讀完),這個時候,串口又接收到新的數據,接收的數據量已經超過了FIFO的長度,會不會造成數據丟失呀
    發(fā)表于 01-17 08:14

    mybatis邏輯分頁和物理分頁的區(qū)別

    這兩種分頁方式的區(qū)別。 邏輯分頁是在數據庫中執(zhí)行查詢時使用的一種分頁方式。這種方式是通過在查詢語句中添加LIMIT或OFFSET關鍵字來限制結果集的大小和偏移來實現的。常見的邏輯分頁
    的頭像 發(fā)表于 12-03 14:54 ?784次閱讀

    mybatis框架的主要作用

    。MyBatis框架的主要作用包括以下幾個方面。 數據庫操作的簡化和標準化: MyBatis框架提供了一種簡單的方式來執(zhí)行數據庫操作,包括插入、更新、刪除和
    的頭像 發(fā)表于 12-03 14:49 ?1893次閱讀

    mybatis和mybatisplus的區(qū)別

    個輕量級的持久層框架,它提供了一個靈活的SQL映射機制,使得開發(fā)者可以編寫原生SQL語句來操作數據庫。MyBatis的設計目標是將原生SQL和對象關系映射(ORM)相結合,以便開發(fā)者可以靈活地操作數據庫。 而
    的頭像 發(fā)表于 12-03 11:53 ?2391次閱讀

    mybatis接口動態(tài)代理原理

    MyBatis是一款輕量級的Java持久化框架,它通過XML或注解配置的方式,將數據庫操作與SQL語句解耦,提供了一種簡單、靈活的數據訪問方式。在MyBatis中,使用動態(tài)代理技術來實
    的頭像 發(fā)表于 12-03 11:52 ?839次閱讀

    mybatis的dao能重載嗎

    MyBatis的DAO能否重載? 在MyBatis中,DAO是數據訪問對象的縮寫,用于執(zhí)行與數據庫交互的操作。MyBatis的DAO可以重載
    的頭像 發(fā)表于 12-03 11:51 ?1184次閱讀

    java數據量大了怎么處理

    當Java應用程序處理大數據量時,需要采取一些技術和策略來優(yōu)化性能和提高可擴展性。在本文中,我將詳細介紹一些常見的處理大數據量的方法和建議。 一、數據結構和算法優(yōu)化 1.使用合適的數據
    的頭像 發(fā)表于 11-23 14:43 ?3223次閱讀

    mysql中的數據大于千萬怎么辦

    等方面。 一、硬件方面的優(yōu)化 增加服務器的內存容量:大量數據的讀寫操作需要較大的內存空間進行緩存,以提高性能。建議將服務器的內存升級到足夠的容量,以適應大數據量的操作。 使用SSD硬盤:傳統(tǒng)的機械硬盤在大數據量下的讀寫
    的頭像 發(fā)表于 11-23 14:41 ?1445次閱讀

    多線程并發(fā)查詢oracle數據

    數據庫的原理、使用場景、實現方法以及可能遇到的問題和解決方案。 一、多線程并發(fā)查詢的原理 在傳統(tǒng)的單線程查詢方式中,當一個查詢請求發(fā)起時,數據
    的頭像 發(fā)表于 11-17 14:22 ?3435次閱讀

    深入分析SQL的排查、解決思路

    出于一些歷史原因有的SQL查詢可能非常復雜,需要同時關聯(lián)非常多的表,使用一些復雜的函數、子查詢,這樣的SQL在項目初期由于數據量比較少,不會對數據庫造成較大的壓力,但是隨著時間的積累以
    的頭像 發(fā)表于 10-31 10:29 ?1601次閱讀
    深入分析<b class='flag-5'>慢</b>SQL的排查、解決思路