目錄
1 背景
在處理天璇大查詢的過程中,遇到一些問題:
-
哪些數據庫能夠實現分批加載(MySQL、Doris、Kylin)?
-
非JDBC的如何實現
-
在不同的實現模式(原生驅動和jdbcTemplates以及zebra)下,具體如何使用(哪些必要條件,哪些方式,原理是什麼)才能實現分批加載。
帶着這些疑惑,進行了如下的一些探查。
2 JDBC
2.1 jdbc協議
何爲jdbc,JDBC,是一種用於Java編程語言和多種數據庫連接的標準Java API。
體現在代碼層面就是在java.sql包中的一些接口:
-
DriverManager:該類被用於管理數據庫驅動。當Java應用發送一個數據庫連接請求時,會伴隨一個對應數據庫的驅動連接子協議。這個協議會通過DriverManager發送到各個Driver,而第一個識別該協議的Dirver會在對應的數據庫和JDBC接口間建立連接。
-
Driver:驅動接口負責處理與數據庫服務器之間的通信。Java編程人員很少會直接使用到這個接口,這是用於DriverManager類在管理着Driver接口。
-
Connection:這個接口包含了所有連接數據庫相關的方法。一個connection對象會包含了和數據庫通信的上下文等信息。所有與數據庫相關的通信都且僅需要通過connection對象。
-
Statement:該接口負責將Java代碼中嵌入的SQL語句提交到數據庫,另外還有一些派生出的接口可以完成參數的傳遞和存儲過程的執行。
-
ResultSet:通過Statement對象執行的查詢語句的查詢結果將被通過ResultSet對象從數據庫取回。ResultSet對象可以像迭代器一樣去操作數據。
-
SQLException:這個類用來處理數據庫應用中出現的各種錯誤。
2.2 數據查詢-JDBC實現
2.2.1 Mysql
一般就是各數據庫廠商提供的驅動,接下來以MySQL爲例進行分析。
驅動包 mysql-connector-java,該驅動包就有上述的JDBC實現。
接下來我們從一個簡單的數據庫查詢過程來看該實現的過程。
String driver= "com.mysql.jdbc.Driver";useCursorFetch=true&defaultFetchSize=50
String url = "jdbc:mysql://host:port/database?useCursorFetch=true&defaultFetchSize=50";
Connection con;
String user = "xxx";
String password = "yyy";
try {
Class.forName(driver);
con = DriverManager.getConnection(url, user, password);
Statement statement = con.createStatement();
// Statement statement = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
String sql = "select * from dw_column";//我的表格叫persons
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()){
resultSet.last();
System.out.println("行數" + resultSet.getRow());
System.out.println(resultSet.getString("column_name"));
System.out.println("hah");
}
System.out.println("哈哈");
resultSet.close();
con.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
-
Class.forName(driver);
在MySQL的Driver類中有如下靜態代碼,實現了在加載類的同時將驅動注入到Manager中,也因此能夠使用DriverMangager獲取Connection。static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } }
-
Connection
MySQL驅動獲取到的Connection實現是 -
statement的實現是StatementImpl.java
3 使用遊標分批獲取數據
JDBC在提供了相關的設置,useCursorFetch、defaultFetchSize。
當然這些要生效必須在這resultSetType和resultSetConcurrency兩個配置項處在對應的模式
Connection.createStatement(int resultSetType,int resultSetConcurrency)
// todo 這兩個配置項的可選值和含義。
只有resultSetType=1003、resultSetConcurrency=1007時,fetchSize才能生效,生效時,result不能使用resultSet.last()方法,會提示“UNsupport operation”
非fetchSize模式下,數據會一次全部加載到ResultSet中,可以使用last。(利用這個可以獲取總行數)
具體的實現依賴於各數據庫廠商的驅動。
3.2 Mysql&Doris
MySQL和Doris在jdbc查詢方面一樣
對於MySQL而言,最終需要在StatementImpl中需要設置了defaultFetchSize屬性。
在StatementImpl中有如下構造函數,會從Connection中獲取這些配置參數
這些參數附在jdbcUrl上或者在代碼中設置都是可以的(設置Connection或者Statement都是可以的,最終都會體現在Statement)
int defaultFetchSize = this.connection.getDefaultFetchSize();
if (defaultFetchSize != 0) {
this.setFetchSize(defaultFetchSize);
}
在jdbc上附上以上兩個參數之後,使用2.2章節中的測試代碼測試後可以發現,
jdbc4ResultSet --> rowData 中有個fetchedRows屬性,這個就是分批取出來的數據,在沒有next的之後此屬性是null,當遊標往後移動的時候,此時fetchedRows就會有值。
3.3 Kylin
public AvaticaStatement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
return super.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
}
從Kylin jdbc jar包來看,Kylin是支持的分批獲取的。
還需要更加具體的測試
4 Spring jdbcTemplates
jdbcTemplates,底層也是調用驅動包的實現類,來創建Connection和Statement,只是再上面一層又封裝了一層。jdbcTemplates的主要目的是封裝了對查詢後數據的處理。
queryForList方法,內部是採用了fetchSize的模式,逐條轉化爲List的元素
queryForRowSet,對外輸出類似ResultSet sqlRowSet,但是sqlRowSet雖然內部封裝了個ResultSet,但是卻不是原始的ResultSet,而是逐條處理之後的ReusltSet,所以sqlRowSet是全量數據集。
在滿足必要條件和配置了fetchSize後,jdbcTemplates諸多方法的內部是分批獲取數據的,只是jdbcTemplates對外提供都是整個結果集。
調用鏈如下所示:
SqlRowSetResultSetExtractor
createSqlRowSet
ResultSetWrappingSqlRowSet
CachedRowSetImpl.populate(rs); // 該方法會逐條處理ResultSet。
while(var1.next()) 。。。
8 結論
-
MySQL數據源原生支持分批加載到JVM,必須滿足resultSetType=1003、resultSetConcurrency=1007條件,然後配置useCursorFetch=true&defaultFetchSize=50即可。
-
spring 的jdbcTemplates提供的方法返回的都是含有所有數據的結果集,雖然其內部是分批獲取,但對外是一次性的提供數據
-
【TODO】其他類型的數據源待繼續調研
9 參考資料
https://my.oschina.net/yibuliushen/blog/887509
https://www.cnblogs.com/benwu/articles/9126972.html
https://blog.csdn.net/shb_derek1/article/details/8105935