MySQL로 대용량 결과 세트 스트리밍
저는 큰 MySQL 테이블을 사용하는 스프링 애플리케이션을 개발하고 있습니다.큰 테이블을 로드할 때, 나는 다음과 같은 메시지를 받습니다.OutOfMemoryException
드라이버가 전체 테이블을 응용 프로그램 메모리에 로드하려고 하기 때문입니다.
사용해 보았습니다.
statement.setFetchSize(Integer.MIN_VALUE);
제가 여는 가▁on▁but다에 걸려 있습니다.close()
온라인에서 결과 집합을 닫기 전에 읽지 않은 행을 로드하려고 하기 때문에 이러한 현상이 발생한다는 것을 발견했지만, 다음과 같이 하기 때문에 그렇지 않습니다.
ResultSet existingRecords = getTableData(tablename);
try {
while (existingRecords.next()) {
// ...
}
} finally {
existingRecords.close(); // this line is hanging, and there was no exception in the try clause
}
중단은 작은 테이블(3줄)에서도 발생하며 레코드 집합(한 가지 방법으로 발생)을 닫지 않으면connection.close()
걸려 있습니다.
행의 스택 추적:
파일 이름.int, line: available [method]socketRead0(FileDescriptor, byte[], int, int, int) 파일 이름: [int]
line: 129SocketInputStream.read(byte[], int, int) line: 129
행: 113ReadAheadInputStream.fill(int) 행: 113
: 160ReadAheadInputStream.ReadFromBundingStreamIfRequired(필요한 경우) 행: 160
) 행: 188ReadAheadInputStream.read(byte[], int, int) 행: 188
: MysqlIO(InputStream, byte[], int, int) 행: 2428 MysqlIO.reuse 및 ReadPacket int) 행: 2882
2871MysqlIO.reuseAndReadPacket(Buffer) 파일: 2871
행: 3414MysqlIO.checkErrorPacket(int) 파일: 3414
: 910MysqlIO.checkErrorPacket() 파일: 910
boolean, booleanboolean, boolean, : 14005MysqlIO.nextRow(Field[], int, boolean, int, boolean, boolean, 버퍼) 행: 1405
: 413RowDataDynamic.nextRecord() 파일: 413
: close line: 170RowDataDynamic.next() 파일: 392 RowDataDynamic.close() 파일: 170
: 152JDBC4ResultSet(ResultSetImple) 라인.realClose(Boolean) 파일: 7473JDBC4 ResultSet(ResultSetImple).close() 파일: 881DelegatingResultSet.close() 파일: 152
ResultSet 임위닫집결.기() 행: 152
준비된 진술서 위임(진술서 위임).close() 선 : 163
은 내 Database (으) database.close() line: 84
가져오기 크기만 설정하는 것은 올바른 접근 방식이 아닙니다.의 javadoc은 이미 다음을 명시하고 있습니다.
데이터베이스에서 가져올 행 수에 대한 힌트를 JDBC 드라이버에 제공합니다.
운전자는 실제로 자유롭게 힌트를 적용하거나 무시할 수 있습니다.일부 드라이버는 이를 무시하고, 일부 드라이버는 이를 직접 적용하며, 일부 드라이버는 더 많은 매개 변수가 필요합니다.MySQL JDBC 드라이버는 마지막 범주에 속합니다.MySQL JDBC 드라이버 설명서를 확인하면 다음과 같은 정보가 나타납니다(헤더 ResultSet까지 약 2/3 아래로 스크롤).
이 기능을 사용하려면 다음과 같은 방법으로 문 인스턴스를 만들어야 합니다.
stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY); stmt.setFetchSize(Integer.MIN_VALUE);
문서의 전체 섹션을 읽어 보십시오. 이 접근 방식의 주의 사항도 설명되어 있습니다.다음은 관련 인용문입니다.
이 접근 방식에는 몇 가지 주의 사항이 있습니다.연결에서 다른 쿼리를 실행하려면 먼저 결과 집합의 모든 행을 읽거나 닫아야 합니다. 그렇지 않으면 예외가 느려집니다.
(...)
문이 트랜잭션 범위 내에 있으면 트랜잭션이 완료되면 잠금이 해제됩니다(문을 먼저 완료해야 함을 의미).대부분의 다른 데이터베이스와 마찬가지로, 문에 보류 중인 모든 결과를 읽거나 문에 대한 활성 결과 집합을 닫을 때까지 문이 완료되지 않습니다.
그게 해결되지 않는다면,OutOfMemoryError
(아닙니다.Exception
), 그러면 데이터가 들어오는 즉시 처리하는 대신 모든 데이터를 Java의 메모리에 저장하는 것이 문제일 수 있습니다.이를 위해서는 코드를 더 변경하거나 완전히 다시 작성해야 합니다.저는 여기서 비슷한 질문에 대답한 적이 있습니다.
당신의 지닫을 ResultSet
배
을 닫을 때, 때닫을보.Statement
해당 항목을 닫으려고 시도합니다.ResultSet
스택 추적에서 다음 두 줄을 볼 수 있습니다.
ResultSet 임위닫집결.기() 행: 152
준비된 진술서 위임(진술서 위임).close() 선 : 163
나는 그 교수형이 있다고 생각했습니다.ResultSet.close()
는 하만실제는로지는에 있었습니다.Statement.close()
어느 쪽인가 하면ResultSet.close()
ResultSet
이미 닫혀 있었고, 그냥 걸려 있었습니다.
우리는 모두 교체했습니다.ResultSet.close()
와 함께results.getStatement().close()
모제습두거했니다다▁all▁and니.Statement.close()
그 해결되었습니다.s, 그고그문이해습니다었결되제제는리.
누군가가 같은 문제를 가지고 있을 경우, 나는 내 쿼리에서 LIMIT 조항을 사용하여 해결했습니다.
이 문제는 MySql에 버그로 보고되었습니다(여기 http://bugs.mysql.com/bug.php?id=42929) 에서 확인하십시오). 현재 "버그가 아닙니다" 상태입니다.가장 적절한 부분은 다음과 같습니다.
현재 결과 집합 "midstream"을 닫을 수 있는 방법이 없습니다.
모든 행을 읽어야 하므로 WHERE 또는 LIMIT와 같은 절을 사용하여 쿼리 결과를 제한해야 합니다.또는 다음을 시도합니다.
ResultSet rs = ...
while(rs.next()) {
...
if(bailOut == true) { break; }
}
while(rs.next()); // This will deplete the remaining rows on the stream
rs.close();
그것은 이상적이지 않을 수도 있지만, 적어도 그것은 당신을 가까이서 매달리게 합니다.
스프링 jdbc를 사용하는 경우 준비된 문 작성기를 SimpleJdbcTemplate와 함께 사용하여 fetchSize를 정수로 설정해야 합니다.MIN_VALUE.여기 http://neopatel.blogspot.com/2012/02/mysql-jdbc-driver-and-streaming-large.html 에서 설명합니다. ▁its
듣기를 중지해도 요청이 계속 진행되기 때문에 중단됩니다.ResultSet 및 문을 올바른 순서로 닫으려면 먼저 statement.cancel()을 호출합니다.
public void close() {
try {
statement.cancel();
if (resultSet != null)
resultSet.close();
} catch (SQLException e) {
// ignore errors on closing
} finally {
try {
statement.close();
} catch (SQLException e) {
// ignore errors on closing
} finally {
resultSet = null;
statement = null;
}
}
}
스크롤 가능한 결과 집합은 fetchSize를 무시하고 메모리 부족 오류를 발생시키는 모든 행을 한 번에 가져옵니다.
저의 경우 Cursors=true를 설정할 때 올바르게 작동했습니다. 그렇지 않으면 Scrollable Resultset은 fetch size의 모든 구현을 무시합니다. 제 경우 5000이었지만 Scrollable Resultset은 한 번에 수백만 개의 레코드를 가져와 과도한 메모리 사용량을 유발합니다.기본 DB는 MSSQL Server입니다.
jdbc:jtds:sqlserver://localhost:1433/ACS;TDS=8.0;사용Cursors=true
언급URL : https://stackoverflow.com/questions/2447324/streaming-large-result-sets-with-mysql
'programing' 카테고리의 다른 글
내 사이트에서 실제로 history.js를 어떻게 사용합니까? (0) | 2023.07.30 |
---|---|
#1148 - 사용된 명령은 이 MariaDB 버전에서 허용되지 않습니다. (0) | 2023.07.30 |
코틀린 및 @유효한 스프링 주석 (0) | 2023.07.30 |
jQuery 게시 요청이 중단되었습니다.사후 매개 변수의 절반만 도착 (0) | 2023.07.30 |
파일로 리디렉션될 때 stdout에 명시적 플러시가 필요한 이유는 무엇입니까? (0) | 2023.07.30 |