一般,當 Microsoft JDBC Driver for SQL Server 執行查詢時,驅動程序會將服務器中的全部結果檢索到應用程序內存中。儘管這種方法能夠最大程度地減小 SQL Server 上的資源佔用,但它可能會在 JDBC 應用程序中針對生成很是大的結果的查詢引起 OutOfMemory(OOM)錯誤。查詢二進制對象或者海量數據(本文桐城大數據)的結果集時,查詢的響應時間更長,更容易出現OOM的問題,SQL Server做爲成熟的數據庫,有本身的解決方式,本文主要討論java中的實現:java
1、經過數據庫遊標的方式,控制結果集的每次返回的結果大小,來下降大數據查詢的資源消耗和響應時間:web
1.使用SQL Server JDBC的URL爲selectMethod=cursor類型(默認selectMethod=direct): sql
String url="jdbc:sqlserver://127.0.0.1;databaseName=lyy;selectMethod=cursor";
這種方式會對全部使用該url的鏈接下的全部查詢起做用,可能引發其餘普通狀況的讀取性能降低。 數據庫
2.設置Fetch Size,要想該設置起效必須使用SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY類型的Statement:服務器
Statement stmt = con.createStatement(SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY,ResultSet.CONCUR_READ_ONLY); stmt.setFetchSize(100);
這種方式會形成SQL Server的API侵入,但該設置僅對使用該statement當前查詢起做用,不失爲一種更好的辦法。 app
參考官方文檔:http://technet.microsoft.com/en-us/library/aa342344%28SQL.90%29.aspx sqlserver
2、使用自適應緩衝(Adaptive Buffering),每次僅佔用必要的資源,來下降大數據查詢的資源消耗和響應時間。 性能
1.設置url爲responseBuffering=adaptive類型,不一樣版本驅動的默認responseBuffering有所不一樣,詳情參考:http://technet.microsoft.com/en-us/library/ms378988(v=sql.100).aspx 測試
String url = "jdbc:sqlserver://192.168.100.136:1433;DatabaseName = lyy;responseBuffering=adaptive";
這種方式會對全部使用該url的鏈接下的全部查詢起做用,可能引發其餘普通狀況的讀取性能降低。 fetch
2. 經過 SQLServerStatement 對象的setResponseBuffering方法來設置response buffering
code1:
stmt = conn.createStatement(); //Display the response buffering mode. SQLServerStatement SQLstmt = (SQLServerStatement) stmt; SQLstmt.setResponseBuffering("adaptive"); System.out.println("Response buffering mode is: " + SQLstmt.getResponseBuffering()); rs = stmt.executeQuery(sql);
code:2:
stmt = conn.createStatement(); if (stmt.isWrapperFor(com.microsoft.sqlserver.jdbc.SQLServerStatement.class)) { SQLServerStatement SQLstmt = stmt.unwrap(SQLServerStatement.class); SQLstmt.setResponseBuffering("adaptive"); System.out.println("Response buffering mode has been set to " + SQLstmt.getResponseBuffering()); } rs = stmt.executeQuery(sql);
開發人員應遵循如下重要準則,以儘量減小應用程序佔用的內存:
應避免使用鏈接字符串屬性 selectMethod=cursor 來容許應用程序處理很是大的結果集。自適應緩衝功能容許應用程序在不使用服務器遊標的狀況下處理很是大的只進、只讀結果集。請注意,設置 selectMethod=cursor 時,該鏈接生成的全部只進只讀結果集都會受到影響。換言之,若是應用程序例行處理只有幾行的短結果集,則與沒有將 selectMethod 設置爲 cursor 的狀況相比,針對每一個結果集建立、讀取和關閉服務器遊標在客戶端和服務器端都會使用更多的資源。
經過使用 getAsciiStream、getBinaryStream, 或 getCharacterStream 方法(而不是 getBlob 或 getClob 方法),將大文本或二進制值做爲流進行讀取。從版本 1.2 開始,SQLServerCallableStatement 類提供了新的 get<Type>Stream 方法來實現此目的。
確保在 SELECT 語句中將可能具備大值的列放在列列表的最後,而且使用 SQLServerResultSet 的 get<Type>Stream 方法按選擇列時的順序來訪問這些列。
確保在用來建立 SQLServerCallableStatement 的 SQL 語句的參數列表中,最後聲明可能具備大值的 OUT 參數。此外,確保使用 SQLServerCallableStatement 的get<Type>Stream 方法,按照聲明 OUT 參數時的順序訪問這些參數。
避免同時對同一鏈接執行一條以上的語句。若是在處理上一條語句的結果以前執行另外一條語句,可能致使將未處理的結果緩衝到應用程序內存中。
但在某些狀況下,使用 selectMethod=cursor 而不是 responseBuffering=adaptive 可能更有利,例如:
在這兩種狀況下,須要考慮建立、讀取和關閉服務器遊標的開銷。
若是應用程序對只進只讀結果集的處理速度很慢(例如,在某些用戶輸入後再讀取每一行),則使用 selectMethod=cursor 代替 responseBuffering=adaptive能夠有助於減小 SQL Server 使用的資源。
若是應用程序在同一鏈接上同時處理兩個或更多的只進只讀結果集,則處理這些結果集時,使用 selectMethod=cursor 代替 responseBuffering=adaptive 可能有助於減小驅動程序須要的內存。
此外,下面的列表針對可滾動的結果集和只進的可更新結果集提供了一些建議:
對於可滾動結果集,在提取行塊時,驅動程序始終會將 SQLServerResultSet 對象的 getFetchSize 方法所指示的行數讀入內存,即便在啓用了自適應緩衝的狀況下也是如此。若是滾動致使 OutOfMemoryError,您能夠調用 SQLServerResultSet 對象的 setFetchSize 方法將提取大小設置爲較少的行數(若是必要,甚至可減少到 1 行),從而減小提取的行數。若是這樣仍是沒法防止 OutOfMemoryError, ,則應避免在可滾動的結果集中包含很是大的列。
對於只進的可更新結果集,在提取行塊時,驅動程序一般會將 SQLServerResultSet 對象的 getFetchSize 方法所指示的行數讀入內存,即便在該鏈接上啓用了自適應緩衝的狀況下也是如此。若是調用 SQLServerResultSet 對象的 next 方法致使 OutOfMemoryError,您能夠調用 SQLServerResultSet 對象的 setFetchSize 方法將提取大小設置爲較少的行數(若是必要,甚至可減少到 1 行),從而減小提取的行數。執行該語句前,也能夠調用 SQLServerStatement 對象的 setResponseBuffering 方法並提供參數「adaptive」來強制驅動程序不緩衝任何行。由於該結果集是不可滾動的,因此若是應用程序使用 get<Type>Stream 方法之一訪問大型列值,驅動程序將會在應用程序讀取該值後當即將其丟棄,正如對只進只讀結果集所作的那樣。
3.經過調用 SQLServerDataSource 對象的 setResponseBuffering 方法,爲經過該 SQLServerDataSource 對象建立的connection來設置 response buffering模式。
該方式一般是用於java web應用。
參考官方文檔:http://technet.microsoft.com/en-us/library/bb879937(v=sql.110).aspx
3、考慮使用 setMaxRows 方法(或 SET ROWCOUNT 或 SELECT TOP N SQL 語法)來限制從可能較大的結果集中返回的行數,對於不須要的結果儘可能不去查詢。
可使用setMaxRows的方法來查詢表的指定前幾行,而不是把整個表的數據行所有都查詢出來。Statement/PreparedStatement setMaxRows(N)等價於SELECT TOP N SQL 語法,其中:
N<=表的總行數時,返回的結果集爲表的前N行。
N>表的總行數時,返回的結果集爲表的總行數,不報錯。
String sql ="select id,name,data from dbo.blobtable "; stmt = conn.prepareStatement(sql); stmt.setMaxRows(2); rs = stmt.executeQuery(); //或者 String sql ="select id,name,data from dbo.blobtable "; stmt = conn.createStatement(); stmt.setMaxRows(2); rs = stmt.executeQuery(sql); //等價於: String sql ="select top 2 id,name,data from dbo.blobtable "; stmt = conn.prepareStatement(sql); rs = stmt.executeQuery(); //或者 String sql ="select top 2 id,name,data from dbo.blobtable "; stmt = conn.createStatement(); rs = stmt.executeQuery(sql);
這種方式並不適用於全部狀況,須要酌情選擇。
參考官方文檔: http://technet.microsoft.com/en-us/library/aa342344%28SQL.90%29.aspx
4、測試並比較1、二兩種方式
數據:sqlserver2008,一個表(2600行,每行數據3.3M左右)
操做:查詢所欲哦數據,並遍歷每行結果。
測試結果:
方法 | 使用的時間 | 內存使用狀況 | 垃圾回收狀況 |
1.使用adaptive buffering | 4分28秒 | <=80m,內存居高恆定 | 垃圾回收佔用的時間約0.2% |
2.使用遊標fetch size=10 | 5分13秒 | <=80m,內存交替高低起伏 |
垃圾回收佔用的時間約2% |
以上結果可見,方法1更快速,可是垃圾回收較差;方法2速度滿了一些,可是垃圾回收好一些;對於已知表數據詳情的,能夠經過方法2設置合適的fetch size來優化查詢的資源消耗和響應時間;對於不知道表數據詳情的可使用方法1來優化。