一般在業務系統需要從 MySQL 數據庫里讀取 100w 數據行進行處理時,這種大數據量的查詢,就不能使用普通的查詢方式,直接將數據全部加載出來到內存中,很可能會發生OOM(內存溢出),并且對應大數據量的查詢,查詢會很耗時,從而不能滿足業務需求,該查詢方式會耗費大量的時間和內存去把數據庫查詢的結果封裝成需要的對象,所有這種方式是遠遠不能滿足業務需求的,對于百萬級以上的數據需要對查詢方式做特殊處理,下面將介紹三種處理大批量數據查詢的方式:
1、分頁查詢
對于大數據量的查詢,我們可以使用分頁來進行查詢,在使用分頁前,我們可以將要查詢的表上添加合適的索引,根據條件查詢的時候,沒有索引,便需要全表掃描,一旦數據量超過百萬甚至千萬,一條查詢SQL執行往往需要幾十秒甚至更多,在添加合適的索引后,使用分頁方式來查詢我們所需的數據,一般我們查詢的數據并不是全部都需要的,使用分頁來限制輸出的數據條數,大程度縮短查詢時間,一般使用LIMIT 子句可以被用于強制 SELECT 語句返回指定的記錄數。LIMIT 接受一個或兩個數字參數,參數必須是一個整數常量。如果給定兩個參數,第一個參數指定第一個返回記錄行的偏移量,第二個參數指定返回記錄行的最大數目,但注意初始記錄行的偏移量是 0(而不是 1),LIMIT # OFFSET #。在使用中需要注意:1、limit語句的查詢時間與起始記錄的位置成正比;2、mysql的limit語句是很方便,但是對記錄很多的表并不適合直接使用。所有我們可以根據業務需求按需進行使用。
2、流式查詢
流式查詢是一種在查詢成功后返回迭代器而不是整個結果集合的方式,允許逐條處理查詢結果。這種方法的主要優勢在于減少內存使用。當需要從數據庫中提取大量數據(如100萬條記錄)而內存不足時,傳統的分頁查詢可能會因為表設計不佳而效率低下。因此,流式查詢成為數據庫訪問框架的一個重要特性。在 MyBatis 中,流式查詢能夠防止因數據量過大而導致的內存溢出(OOM)。但需注意的是,執行流式查詢后,數據庫連接會保持開啟,用戶需要在獲取完所有數據后手動關閉該連接。此外,在數據未完全讀取(或關閉)之前,不能對連接進行其他查詢,否則會引發異常。
MyBatis 提供了接口用于流式查詢,使用流式查詢時,需要確保對所查詢表的并發訪問,因為它會獨占連接,因此應盡快處理結果。當查詢數據達到上萬條的時候相應過慢。當需要遍歷處理一個非常大的查詢結果時,如果不希望一次性將所有數據加載到客戶端內存中,可以考慮使用流式查詢。在分庫分表的場景中,即使單個表的查詢結果不大,如果某個查詢涉及多個庫和表,并且需要對結果進行合并、排序等操作,仍然可能導致內存溢出。深入研究 sharding-sphere 的代碼可以發現,除了在 GROUP BY 和 ORDER BY 字段不一致的情況下,其他大多數場景都非常適合使用流式查詢。這種方式能夠最大限度地降低客戶端內存的消耗。流式查詢內存會保持穩定,不會隨著記錄的增長而增長。其內存大小取決于批處理大小BATCH_SIZE的設置,該尺寸越大,內存會越大,所以BATCH_SIZE應該根據業務情況設置合適的大小。
3、游標查詢
在處理大量數據時,為了避免內存泄漏,可以考慮使用游標方式進行數據查詢。這種方法通常比傳統查詢更高效。當需要查詢百萬級的數據時,游標方式不僅能節省內存消耗,還能避免一次性加載所有數據。通過這種方式,可以逐條處理數據或按批次取出部分數據。可以在一次查詢中指定 fetchSize,然后逐步處理所有數據,直到處理完成。
總結
流式查詢、游標查詢可以避免 OOM,數據量大可以考慮此方案。但是這兩種方式會占用數據庫連接,使用中不會釋放,所以線上針對大數據量業務用到游標和流式操作,一定要進行并發控制 另外針對 JDBC 原生流式查詢,Mybatis 中也進行了封裝,雖然會慢一些,但是功能以及代碼的整潔程度會好上不少。在實際應用中,我們可以根據業務數據量情況,選擇合適的方式進行查詢,來高效的獲取目標數據。