[SPRING] 왜 Spring의 jdbcTemplate.batchUpdate ()가 느린가?
SPRING왜 Spring의 jdbcTemplate.batchUpdate ()가 느린가?
배치 삽입 작업을 더 빨리 수행 할 방법을 찾고 있습니다.
jdbcTemplate.update (String sql)를 사용하여 여러 배치를 삽입하려고 시도했습니다. sql은 StringBuilder에 의해 빌드되었으며 다음과 같이 보입니다.
INSERT INTO TABLE(x, y, i) VALUES(1,2,3), (1,2,3), ... , (1,2,3)
배치 크기는 정확하게 1000이었습니다. 거의 100 배치를 삽입했습니다. StopWatch를 사용하여 시간을 확인하고 삽입 시간을 찾았습니다.
min[38ms], avg[50ms], max[190ms] per batch
다행 이었지만 코드를 더 잘 만들고 싶었습니다.
그 후 jdbcTemplate.batchUpdate를 다음과 같이 사용하려고했습니다.
jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
// ...
}
@Override
public int getBatchSize() {
return 1000;
}
});
어디 SQL 같은데
INSERT INTO TABLE(x, y, i) VALUES(1,2,3);
나는 실망했다! jdbcTemplate은 분리 된 방식으로 1000 행 배치의 모든 단일 삽입을 실행했습니다. 나는 mysql_log에서 눈을 떴다. StopWatch를 사용하여 시간을 확인하고 삽입 시간을 찾았습니다.
분 [900ms], 평균 [1100ms], 최대 [2000ms] / 배치
그래서, 아무도 나에게 설명 할 수있는 이유는 jdbcTemplate이 방법으로 분리 된 삽입을하는 이유는 무엇입니까? 메소드의 이름이 batchUpdate 인 이유는 무엇입니까? 아니면이 방법을 잘못 사용하고있을 수 있습니까?
해결법
-
==============================
1.JDBC 연결 URL의 이러한 매개 변수는 일괄 처리 된 명령문의 속도에 큰 차이를 만들 수 있습니다.
JDBC 연결 URL의 이러한 매개 변수는 일괄 처리 된 명령문의 속도에 큰 차이를 만들 수 있습니다.
참고 : JDBC 배치 삽입 성능
-
==============================
2.나는 또한 JDBC JDBC 템플릿으로 같은 문제에 직면했다. 아마도 Spring Batch를 사용하면 문장이 실행되어 모든 삽입물이나 청크에 적용될 수 있습니다.
나는 또한 JDBC JDBC 템플릿으로 같은 문제에 직면했다. 아마도 Spring Batch를 사용하면 문장이 실행되어 모든 삽입물이나 청크에 적용될 수 있습니다.
jdbcTemplate.batchUpdate () 코드를 원래의 JDBC 배치 삽입 코드로 대체하고 주요 성능 향상을 발견했습니다.
DataSource ds = jdbcTemplate.getDataSource(); Connection connection = ds.getConnection(); connection.setAutoCommit(false); String sql = "insert into employee (name, city, phone) values (?, ?, ?)"; PreparedStatement ps = connection.prepareStatement(sql); final int batchSize = 1000; int count = 0; for (Employee employee: employees) { ps.setString(1, employee.getName()); ps.setString(2, employee.getCity()); ps.setString(3, employee.getPhone()); ps.addBatch(); ++count; if(count % batchSize == 0 || count == employees.size()) { ps.executeBatch(); ps.clearBatch(); } } connection.commit(); ps.close();
이 링크도 확인하십시오. JDBC 배치 삽입 성능
-
==============================
3.단순히 거래를 사용하십시오. 메소드에 @Transactional을 추가하십시오.
단순히 거래를 사용하십시오. 메소드에 @Transactional을 추가하십시오.
여러 데이터 소스 @Transactional ( "daTa Manager")을 사용하여 올바른 TX 관리자를 선언해야합니다. 60000 개의 레코드를 삽입하는 경우가 있습니다. 약 15 초가 걸립니다. 다른 방법은 없습니다.
@Transactional("myDataSourceTxManager") public void save(...) { ... jdbcTemplate.batchUpdate(query, new BatchPreparedStatementSetter() { @Override public void setValues(PreparedStatement ps, int i) throws SQLException { ... } @Override public int getBatchSize() { if(data == null){ return 0; } return data.size(); } }); }
-
==============================
4.INSERT INTO TABLE (x, y, i) VALUES (1,2,3)로 SQL 삽입을 변경하십시오. 프레임 워크는 루프를 만듭니다. 예 :
INSERT INTO TABLE (x, y, i) VALUES (1,2,3)로 SQL 삽입을 변경하십시오. 프레임 워크는 루프를 만듭니다. 예 :
public void insertBatch(final List<Customer> customers){ String sql = "INSERT INTO CUSTOMER " + "(CUST_ID, NAME, AGE) VALUES (?, ?, ?)"; getJdbcTemplate().batchUpdate(sql, new BatchPreparedStatementSetter() { @Override public void setValues(PreparedStatement ps, int i) throws SQLException { Customer customer = customers.get(i); ps.setLong(1, customer.getCustId()); ps.setString(2, customer.getName()); ps.setInt(3, customer.getAge() ); } @Override public int getBatchSize() { return customers.size(); } }); }
당신이 이런 식으로하면. Spring은 다음과 같이 처리 할 것이다.
for(int i = 0; i < getBatchSize(); i++){ execute the prepared statement with the parameters for the current iteration }
프레임 워크는 먼저 쿼리 (SQL 변수)에서 PreparedStatement를 만든 다음 setValues 메서드가 호출되고 명령문이 실행됩니다. 이는 getBatchSize () 메소드에서 지정한 횟수만큼 반복됩니다. 따라서 insert 문을 작성하는 올바른 방법은 하나의 values 절만 사용하는 것입니다. http://docs.spring.io/spring/docs/3.0.x/reference/jdbc.html에서 살펴볼 수 있습니다.
-
==============================
5.이것이 당신에게 도움이 될지 모르지만, 여기 스프링 사용하지 않는 방법으로 제가 사용을 끝내 었습니다. 그것은 내가 시도한 다양한 Spring 메소드보다 훨씬 빠릅니다. 나는 JDBC 템플릿 일괄 업데이트 방법을 사용하여 다른 대답을 설명했지만 심지어 원하는 것보다 느렸다. 나는 거래가 무엇인지, Internets은 많은 답변을하지 않았는지 잘 모르겠습니다. 커밋이 처리되는 방식과 관련이 있다고 생각했습니다.
이것이 당신에게 도움이 될지 모르지만, 여기 스프링 사용하지 않는 방법으로 제가 사용을 끝내 었습니다. 그것은 내가 시도한 다양한 Spring 메소드보다 훨씬 빠릅니다. 나는 JDBC 템플릿 일괄 업데이트 방법을 사용하여 다른 대답을 설명했지만 심지어 원하는 것보다 느렸다. 나는 거래가 무엇인지, Internets은 많은 답변을하지 않았는지 잘 모르겠습니다. 커밋이 처리되는 방식과 관련이 있다고 생각했습니다.
이 접근법은 java.sql 패키지와 PreparedStatement의 일괄 처리 인터페이스를 사용하는 단순한 JDBC이다. 이것은 MySQL 데이터베이스에 24M 레코드를 얻을 수있는 가장 빠른 방법이었습니다.
필자는 "레코드"개체의 컬렉션을 구축 한 다음 모든 레코드를 일괄 적으로 삽입하는 메서드에서 아래 코드를 호출했습니다. 모음을 만든 루프가 배치 크기를 관리했습니다.
MySQL 데이터베이스에 24M 레코드를 삽입하려고 시도하고 있었고 스프링 배치를 사용하여 초당 200 레코드를 기록했습니다. 이 방법으로 전환했을 때 초당 ~ 2500 개의 레코드가 기록되었습니다. 그래서 24M 레코드로드는 이론적으로 1.5 일에서 약 2.5 시간으로 늘어났습니다.
먼저 연결을 만듭니다 ...
Connection conn = null; try{ Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection(connectionUrl, username, password); }catch(SQLException e){}catch(ClassNotFoundException e){}
그런 다음 준비된 명령문을 작성하고 삽입을위한 값의 일괄 처리로로드 한 다음 단일 배치 삽입으로서 실행하십시오.
PreparedStatement ps = null; try{ conn.setAutoCommit(false); ps = conn.prepareStatement(sql); // INSERT INTO TABLE(x, y, i) VALUES(1,2,3) for(MyRecord record : records){ try{ ps.setString(1, record.getX()); ps.setString(2, record.getY()); ps.setString(3, record.getI()); ps.addBatch(); } catch (Exception e){ ps.clearParameters(); logger.warn("Skipping record...", e); } } ps.executeBatch(); conn.commit(); } catch (SQLException e){ } finally { if(null != ps){ try {ps.close();} catch (SQLException e){} } }
분명히 내가 오류 처리를 제거했습니다 및 쿼리 및 레코드 개체는 명목상과 그 외의 것들입니다.
편집하다: 원래의 질문은 삽입을 foobar 값 (?,?,?), (?,?,?) ... 메소드 (?,?,?)로 스프링 배치로 비교 한 것이므로 여기에 더 직접적인 반응이 있습니다.
원래의 방법은 "LOAD DATA INFILE"접근 방식을 사용하지 않고 대량 데이터로드를 MySQL로 가장 빨리 수행 할 수있는 방법입니다. MysQL 문서의 인용문 (http://dev.mysql.com/doc/refman/5.0/en/insert-speed.html) :
Spring JDBC Template batchUpdate 메소드를 수정하여 'setValues'호출마다 여러 개의 VALUES가 지정된 삽입을 수행 할 수 있지만 삽입되는 객체에 대해 반복 할 때 인덱스 값을 수동으로 추적해야합니다. 그리고 삽입되는 물건의 총 수가 준비된 진술서에있는 VALUES리스트의 배수가 아닐 때 마지막에 까다로운 케이스에 빠지게됩니다.
윤곽을 그리는 접근법을 사용한다면 똑같은 일을 할 수 있습니다 (여러 VALUES 목록이있는 준비된 문장을 사용하십시오). 그리고 마지막에 그 엣지 경우에 도달하면, 당신이 빌드하고 실행할 수 있으므로 다루기가 좀 더 쉽습니다. VALUES리스트가 정확히 맞는 마지막 문장 하나. 약간 해키지만, 가장 최적화 된 것들이 있습니다.
-
==============================
6.호출에서 argTypes 배열을 크게 개선 한 것을 발견했습니다.
호출에서 argTypes 배열을 크게 개선 한 것을 발견했습니다.
필자의 경우 Spring 4.1.4와 Oracle 12c에서는 35 개의 필드가있는 5000 개의 행을 삽입했다.
jdbcTemplate.batchUpdate(insert, parameters); // Take 7 seconds jdbcTemplate.batchUpdate(insert, parameters, argTypes); // Take 0.08 seconds!!!
argTypes param은 다음과 같이 각 필드를 설정하는 int 배열입니다.
int[] argTypes = new int[35]; argTypes[0] = Types.VARCHAR; argTypes[1] = Types.VARCHAR; argTypes[2] = Types.VARCHAR; argTypes[3] = Types.DECIMAL; argTypes[4] = Types.TIMESTAMP; .....
org \ springframework \ jdbc \ core \ JdbcTemplate.java를 디버깅하고 대부분의 시간이 각 필드의 특성을 알아 내려고 소비되었다는 것을 알게되었고 이것은 각 레코드에 대해 만들어졌습니다.
희망이 도움이!
from https://stackoverflow.com/questions/20360574/why-springs-jdbctemplate-batchupdate-so-slow by cc-by-sa and MIT license
'SPRING' 카테고리의 다른 글
[SPRING] Spring에서 무시되는 Jackson 주석 (0) | 2019.01.13 |
---|---|
[SPRING] 봄 - jsp 파일에 이미지 표시 (0) | 2019.01.13 |
[SPRING] JsonSerializer에 @ bean을 @autowire하는 방법? (0) | 2019.01.13 |
[SPRING] 후행 슬래시가있는 URL을 해당 URL없이 해당 URL로 리디렉션하는 방법은 무엇입니까? (0) | 2019.01.13 |
[SPRING] Spring 보안 - 로그인으로 리다이렉션시 URL 매개 변수 유지하기 (0) | 2019.01.13 |