(一)場景與方案
場景:java端從數據庫讀取100W數據進行后台業務處理。
常規實現1:分頁讀取出來。缺點:需要排序后分頁讀取,性能低下。
常規實現2:一次性讀取出來。缺點:需要很大內存,一般計算機不行。
非常規實現:建立長連接,利用服務端游標,一條一條流式返回給java端。
非常規實現優化:jdbc中有個重要的參數fetchSize(它對業務實現無影響,即不會限制讀取條數等),優化后可顯著提升性能。
(二)代碼與執行結果
public static void main(String[] args) throws SQLException { getAll(1); getAll(10); getAll(100); getAll(1000); } public static void getAll(int fetchSize) { try { long beginTime=System.currentTimeMillis(); Connection connection = DriverManager.getConnection(MYSQL_URL); connection.setAutoCommit(false); //為了設置fetchSize,必須設置為false String sql = "select * from test"; PreparedStatement psst = connection.prepareStatement(sql,ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); psst.setFetchSize(fetchSize); ResultSet rs = psst.executeQuery(); int totalCount=0; while (rs.next()) { totalCount++; } rs.close(); psst.close(); connection.close(); long endTime=System.currentTimeMillis(); System.out.println("totalCount:"+totalCount+";fetchSize:"+fetchSize+";耗時:"+(endTime-beginTime)+"ms"); } catch (SQLException e) { e.printStackTrace(); } }
執行結果如下——
totalCount:3185194;fetchSize:1;耗時:23770ms totalCount:3185194;fetchSize:10;耗時:23253ms totalCount:3185194;fetchSize:100;耗時:21890ms totalCount:3185194;fetchSize:1000;耗時:20985ms
可以看到,當fetchSize為1000時,性能有提升。(看一些網友的數據,性能提升更多)
(三)原理分析
1、先在服務端執行查詢后將數據緩存在服務端。(耗時相對較長)
2、java端獲取數據時,利用服務端游標進行指針跳動,如果fetchSize為1000,則一次性跳動1000條,返回給java端緩存起來。(耗時較短,跳動次數為N/1000)
3、在調用next函數時,優先從緩存中取數,其次執行2過程。(內存讀取,耗時可忽略)
題外話:spring的JdbcCursorItemReader是對fetchSize的良好應用。