¿Qué hace realmente el método Statement.setFetchSize (nSize) en el controlador JDBC de SQL Server?

Tengo esta gran mesa con millones de registros cada día y al final de cada día estoy extrayendo todos los registros del día anterior. Estoy haciendo esto como:

String SQL = "select col1, col2, coln from mytable where timecol = yesterday"; Statement.executeQuery(SQL); 

El problema es que este progtwig toma como 2GB de memoria porque toma todos los resultados en memoria y luego lo procesa.

Intenté configurar el Statement.setFetchSize(10) pero toma exactamente la misma memoria del sistema operativo, no hace ninguna diferencia. Estoy usando el controlador JDBC de Microsoft SQL Server 2005 para esto.

¿Hay alguna manera de leer los resultados en pequeños fragmentos como el controlador de base de datos de Oracle cuando la consulta se ejecuta para mostrar solo unas pocas filas y cuando se desplaza hacia abajo se muestran más resultados?

En JDBC, el setFetchSize(int) es muy importante para el rendimiento y la gestión de la memoria dentro de la JVM, ya que controla el número de llamadas de red desde la JVM a la base de datos y, por consiguiente, la cantidad de RAM utilizada para el procesamiento ResultSet.

Inherentemente si se llama a setFetchSize (10) y el controlador lo ignora, probablemente solo haya dos opciones:

  1. Pruebe con un controlador JDBC diferente que respete la sugerencia del tamaño de búsqueda.
  2. Observe las propiedades específicas del controlador en la conexión (URL y / o mapa de propiedades al crear la instancia de conexión).

RESULT-SET es el número de filas ordenadas en la base de datos en respuesta a la consulta. El ROW-SET es el fragmento de filas que se extraen de RESULT-SET por llamada de la JVM a la base de datos. El número de estas llamadas y la RAM resultante requerida para el procesamiento dependen de la configuración del tamaño de búsqueda.

Entonces, si RESULT-SET tiene 100 filas y el tamaño de búsqueda es 10, habrá 10 llamadas de red para recuperar todos los datos, usando aproximadamente 10 * {row-content-size} RAM en cualquier momento dado.

El tamaño de búsqueda predeterminado es 10, que es bastante pequeño. En el caso publicado, parecería que el controlador está ignorando la configuración del tamaño de búsqueda, recuperando todos los datos en una llamada (gran requerimiento de RAM, llamadas de red mínimas óptimas).

Lo que sucede debajo de ResultSet.next() es que en realidad no busca una fila a la vez desde RESULT-SET. Lo obtiene del SET DE FILA (local) y obtiene el siguiente JUEGO DE FILA (de forma invisible) del servidor a medida que se agota en el cliente local.

Todo esto depende del controlador, ya que la configuración es solo una ‘pista’, pero en la práctica he encontrado que así es como funciona para muchos controladores y bases de datos (verificado en muchas versiones de Oracle, DB2 y MySQL).

El parámetro fetchSize es una sugerencia para el controlador JDBC en cuanto a muchas filas para obtener de una vez desde la base de datos. Pero el conductor es libre de ignorar esto y hacer lo que considere oportuno. Algunos controladores, como Oracle, recogen filas en fragmentos, por lo que puede leer conjuntos de resultados muy grandes sin necesitar mucha memoria. Otros controladores acaban de leer todo el conjunto de resultados de una vez, y supongo que eso es lo que está haciendo su conductor.

Puede intentar actualizar su controlador a la versión de SQL Server 2008 (que podría ser mejor) o al controlador jTDS de fuente abierta.

Debe asegurarse de que la confirmación automática de la conexión esté desactivada , o establecerFetchSize no tendrá ningún efecto.

 dbConnection.setAutoCommit(false); 

Editar: Recuerdo que cuando usé esta solución, era específica de Postgres, pero espero que funcione para SQL Server.

Interfaz de statement Doc.

RESUMEN: void setFetchSize(int rows) Proporciona al controlador JDBC una pista sobre el número de filas que se deben recuperar de la base de datos cuando se necesitan más filas.

Lea este ebook J2EE y más allá Por Art Taylor

Parece que mssql jdbc está almacenando el conjunto completo de resultados para usted. Puede agregar un parámetro de cadena de conexión que indique selectMode = cursor o responseBuffering = adaptive. Si se encuentra en la versión 2.0+ del controlador mssql jdbc de 2005, el almacenamiento en memoria intermedia de respuesta debe ser adaptable por defecto.

http://msdn.microsoft.com/en-us/library/bb879937.aspx

Me parece que realmente quiere limitar las filas que se devuelven en su consulta y la página a través de los resultados. Si es así, puedes hacer algo como:

 select * from (select rownum myrow, a.* from TEST1 a ) where myrow between 5 and 10 ; 

Solo tienes que determinar tus límites.

Prueba esto:

 String SQL = "select col1, col2, coln from mytable where timecol = yesterday"; connection.setAutoCommit(false); PreparedStatement stmt = connection.prepareStatement(SQL, SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY, SQLServerResultSet.CONCUR_READ_ONLY); stmt.setFetchSize(2000); stmt.set.... stmt.execute(); ResultSet rset = stmt.getResultSet(); while (rset.next()) { // ...... 

Tenía exactamente el mismo problema en un proyecto. El problema es que, aunque el tamaño de búsqueda puede ser lo suficientemente pequeño, JDBCTemplate lee todo el resultado de su consulta y lo mapea en una lista enorme que puede volar su memoria. Terminé extendiendo NamedParameterJdbcTemplate para crear una función que devuelve un flujo de objetos. Ese flujo se basa en el ResultSet normalmente devuelto por JDBC, pero extraerá datos del ResultSet solo cuando el Stream lo requiera. Esto funcionará si no mantienes una referencia de todos los objetos que este Stream escupe. Me inspiré mucho en la implementación de org.springframework.jdbc.core.JdbcTemplate # execute (org.springframework.jdbc.core.ConnectionCallback). La única diferencia real tiene que ver con qué hacer con ResultSet. Terminé escribiendo esta función para concluir el ResultSet:

 private  Stream wrapIntoStream(ResultSet rs, RowMapper mapper) { CustomSpliterator spliterator = new CustomSpliterator(rs, mapper, Long.MAX_VALUE, NON-NULL | IMMUTABLE | ORDERED); Stream stream = StreamSupport.stream(spliterator, false); return stream; } private static class CustomSpliterator extends Spliterators.AbstractSpliterator { // won't put code for constructor or properties here // the idea is to pull for the ResultSet and set into the Stream @Override public boolean tryAdvance(Consumer action) { try { // you can add some logic to close the stream/Resultset automatically if(rs.next()) { T mapped = mapper.mapRow(rs, rowNumber++); action.accept(mapped); return true; } else { return false; } } catch (SQLException) { // do something with this Exception } } } 

puede agregar algo de lógica para hacer que Stream sea “auto cerrable”; de lo contrario, no olvide cerrarla cuando haya terminado.