2009-08-23 30 views
32

Tengo esta gran mesa con algunos 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:¿Qué hace realmente el método Statement.setFetchSize (nSize) en el controlador JDBC de SQL Server?

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

El problema es que este programa toma como 2 GB de memoria, ya que se lleva todos los resultados en la memoria y luego lo procesa.

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

¿Hay alguna manera de leer los resultados en trozos pequeños 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?

Respuesta

2

interfaz Statement Doc

RESUMEN: void setFetchSize(int rows) da al conductor JDBC una pista sobre el número de filas que deben ser exagerado de la base de datos cuando más filas son necesario.

Leer este libro electrónico J2EE and beyond By Art Taylor

1

Me suena 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í, puede hacer algo como:

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

Solo tiene que determinar sus límites.

9

Debe asegurarse de que la confirmación automática en la conexión se ha desactivado en, o setFetchSize no tendrá ningún efecto.

dbConnection.setAutoCommit(false); 

Editar: recordar que cuando utilicé Esto soluciona era Postgres-específica, pero espero que seguirá siendo el trabajo para SQL Server.

+5

No sé Postgres, pero ... la configuración de confirmación automática no debe tener ninguna referencia en una instrucción SELECT y/o tamaño de búsqueda de búsqueda (no están realmente relacionados). –

+0

@jwaddell ¿Es esto 'Si la confirmación automática en la Conexión está activada, entonces setFetchSize no tendrá ningún efecto.' También es cierto para 'Oracle 11g'? –

+0

Commit es una función de los estados-manipulación-lenguaje de datos (DML) como INSERT, UPDATE, DELETE en la base de datos de dos fase de confirmación (2PC). Esto no tiene nada que ver con las consultas, que es lo que "fetch" está relacionado con "SELECT". Por lo tanto el valor de auto-entrega (sentencia DML es auto-cometido en la ejecución frente a tener que ejecutar una confirmación por separado a partir de entonces) no tiene nada que ver con ir a buscar en Oracle, MySQL o SQL Server y probablemente cualquier otro RDBMS. –

3

Parece que mssql jdbc está guardando en búfer todo el conjunto 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

22

El parámetro fetchSize es un indicio al controlador JDBC como a muchas filas en busca de una sola vez de 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 el controlador jTDS de fuente abierta.

+0

Absolutamente correcto. Para MSSQL, el controlador jTDS es una mejor opción. – BalusC

+0

¿Cómo sería un juego responseBuffering adaptativos en el controlador jTDS, y quiero decir, no en el nivel de controlador, pero en el nivel de la consulta? – eugenevd

46

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

Intrínsecamente si setFetchSize (10) se está llamando y el conductor hace caso omiso de ella, es probable que haya sólo dos opciones:

  1. probar un controlador JDBC diferente que honrará la indirecta de búsqueda hacia tamaño.
  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).

El RESULTADO-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 requeridas para el procesamiento dependen de la configuración del tamaño de búsqueda.

Así que 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, utilizando aproximadamente 10 * {row-content-size} RAM en cualquier momento hora.

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

Lo que sucede debajo de ResultSet.next() es que en realidad no va a buscar 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).

+0

Sé que esta es la respuesta anterior. Pero tengo una pregunta. ¿Cuál es la diferencia entre fetchsize y scroll? Según mi comprensión, el desplazamiento se usa para que no obtengamos todos los resultados a la vez. –

+0

El desplazamiento es el proceso de avanzar/retroceder en el conjunto de resultados. Para desplazarse, uno inherentemente recupera o ya puede haber sido recuperado en la memoria JVM. Los cursores en varias bases de datos pueden ser solo hacia adelante a pesar de las opciones en JDBC para desplazarse hacia atrás a través de un conjunto de datos recuperados. Entonces, fetch-size sigue siendo la forma en que se establece la cantidad de datos que extraemos del DB en la red. La configuración de desplazamiento no afectaría esto en JDBC. –

1

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()) { 
    // ...... 
0

que tenían 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é de escribir esta función para envolver el conjunto de resultados:

private <T> Stream<T> wrapIntoStream(ResultSet rs, RowMapper<T> mapper) { 
    CustomSpliterator<T> spliterator = new CustomSpliterator<T>(rs, mapper, Long.MAX_VALUE, NON-NULL | IMMUTABLE | ORDERED); 
    Stream<T> stream = StreamSupport.stream(spliterator, false); 
    return stream; 
} 
private static class CustomSpliterator<T> extends Spliterators.AbstractSpliterator<T> { 
    // 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<? super T> 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 
     } 
    } 
} 

se puede añadir un poco de lógica para hacer que Stream "se puede cerrar auto", de lo contrario no olvide cerrarlo cuando haya terminado.

Cuestiones relacionadas