這篇文章為大家帶來了關於mysql的相關知識,主要介紹了MySQL中的串流查詢及遊標查詢方式,具有很好的參考價值,希望對大家有所幫助。
推薦學習:mysql影片教學
現在業務系統需要從MySQL 資料庫裡讀取500w 資料行進行處理
@Test public void generalQuery() throws Exception { // 1核2G:查询一百条记录:47ms // 1核2G:查询一千条记录:2050 ms // 1核2G:查询一万条记录:26589 ms // 1核2G:查询五万条记录:135966 ms String sql = "select * from wh_b_inventory limit 10000"; ps = conn.prepareStatement(sql); ResultSet rs = ps.executeQuery(sql); int count = 0; while (rs.next()) { count++; } System.out.println(count); }
JVM監控
我們將對記憶體調小-Xms70m -Xmx70m整個查詢過程中,堆記憶體佔用逐步成長,並且最終導致OOM:java.lang.OutOfMemoryError: GC overhead limit exceeded
1、頻繁觸發GC2、存在OOM隱患##2.2 串流查詢
從測試結果來看,串流查詢並沒有提升查詢的速度
@Test public void streamQuery() throws Exception { // 1核2G:查询一百条记录:138ms // 1核2G:查询一千条记录:2304 ms // 1核2G:查询一万条记录:26536 ms // 1核2G:查询五万条记录:135931 ms String sql = "select * from wh_b_inventory limit 50000"; statement = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); statement.setFetchSize(Integer.MIN_VALUE); ResultSet rs = statement.executeQuery(sql); int count = 0; while (rs.next()) { count++; } System.out.println(count); }
我們將堆記憶體調小-Xms70m -Xmx70m
我們發現即使堆記憶體只有70m,卻依然沒有發生OOM
2.3 遊標查詢
1、需要在資料庫連線資訊裡拼接參數
useCursorFetch=true#2、其次設定Statement 每次讀取資料數量,例如一次讀取1000
#從測試結果來看,遊標查詢在一定程度
縮短了查詢速度@Test
public void cursorQuery() throws Exception {
Class.forName("com.mysql.jdbc.Driver");
// 注意这里需要拼接参数,否则就是普通查询
conn = DriverManager.getConnection("jdbc:mysql://101.34.50.82:3306/mysql-demo?useCursorFetch=true", "root", "123456");
start = System.currentTimeMillis();
// 1核2G:查询一百条记录:52 ms
// 1核2G:查询一千条记录:1095 ms
// 1核2G:查询一万条记录:17432 ms
// 1核2G:查询五万条记录:90244 ms
String sql = "select * from wh_b_inventory limit 50000";
((JDBC4Connection) conn).setUseCursorFetch(true);
statement = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
statement.setFetchSize(1000);
ResultSet rs = statement.executeQuery(sql);
int count = 0;
while (rs.next()) {
count++;
}
System.out.println(count);
}
我們將堆記憶體調小- Xms70m -Xmx70m
我們發現在單執行緒情況下,遊標查詢和串流查詢一樣,都能很好的規避OOM,並且遊標查詢能夠最佳化查詢速度。
#三、RowData
3.1 RowDataStatic
3.2 RowDataDynamic
3.3 RowDataCursor
預設的RowDataStatic 讀取全部資料到客戶端記憶體中,也就是我們的JVM;
RowDataDynamic 每次IO 呼叫讀取一個資料;
RowDataCursor 一次讀取fetchSize 行,消費完成再發起請求呼叫。
四、JDBC 通訊原理
JDBC 用戶端-> 客戶端Socket -> MySQL -> 擷取資料返回-> MySQL 核心Socket Buffer -> 網路-> 客戶端Socket Buffer -> JDBC 用戶端
普通查詢會將當次查詢到的所有資料載入到JVM,然後再處理。
如果查詢資料量過大,會不斷經歷GC,然後就是記憶體溢出
服務端準備好從第一個資料開始返回時,向緩衝區懟入數據,這些數據通過TCP鏈路,懟入客戶端機器的內核緩衝區,JDBC會的inputStream.read()方法會被喚醒去讀取數據,唯一的區別是開啟了stream讀取的時候,每次只是從核心讀取一個package大小的數據,只是回傳一行數據,如果1個package無法組裝1行數據,會再讀1個package。
當開啟遊標的時候,服務端回傳資料的時候,就會按照fetchSize的大小回傳資料了,而客戶端接收資料的時候每次都會把換緩衝區資料全部讀取乾淨,假如數據有1億數據,將FetchSize設定成1000的話,會進行10萬次來回通訊;
由於MySQL方不知道客戶端什麼時候將數據消費完,而自身的對應表可能會有DML寫入操作,此時MySQL需要建立一個臨時空間來存放需要拿走的資料。
因此對於當你啟用useCursorFetch讀取大表的時候會看到MySQL上的幾個現象:
並發呼叫:Jmete 1 秒10 個執行緒並發呼叫
串流查詢記憶體效能報告如下
並發呼叫對於記憶體佔用情況也很OK,不存在疊加式增加
遊標查詢記憶體效能報告如下
1、遊標查詢和串流查詢在單執行緒下都能夠規避OOM的情況;
2、在查詢速度上游標查詢比流式查詢更快,流式查詢和普通查詢相比並不能縮短查詢時間;
3、在並發場景下,串流查詢堆疊記憶體走勢更穩定,不存在疊加式增加。
推薦學習:mysql影片教學
#以上是MySQL中的串流查詢及遊標查詢方式(總結分享)的詳細內容。更多資訊請關注PHP中文網其他相關文章!