[자바, iBatis 배치활용]insert/update 속도 개선! :: 개발/일상_Mr.lee

[자바, iBatis 배치활용]insert/update 속도 개선!

Posted by Mr.mandu.
2017. 8. 25. 09:02 개발/java,spring

새로운 이슈가 생겨서 블로그에 적어두려고 합니다.

일반 적으로 데이터를  insert, update를 할때 시간을 신경쓰지 않습니다.

그런데 이번 프로젝트를 진행하며 

데이터 insert 할 건수가 대략 적이지만 50만건 정도는 되었습니다.


아무생각없이 일반 insert문으로 진행 할시 시간이 어마어마하게 많이 걸렸습니다.

1,000건을 insert해도 시간이 상당하더군요.


그래서 대용량 insert 등 막 검색해서 

배치! 라는 것을 알게 되었습니다.


먼저 startBatch(), excuteBatch()함수를 활용해야 하는데

SqlMapExecutor 인터페이스에서 가져온 설명 입니다.


/**

   * Starts a batch in which update statements will be cached before being sent to

   * the database all at once. This can improve overall performance of updates update

   * when dealing with numerous updates (e.g. inserting 1:M related data).

   *

   * @throws java.sql.SQLException If the batch could not be started.

   */


  void startBatch() throws SQLException;


  /**

   * Executes (flushes) all statements currently batched.

   *

   * @return the number of rows updated in the batch

   * @throws java.sql.SQLException If the batch could not be executed or if any of the statements

   *                               fails.

   */

  int executeBatch() throws SQLException;


startBatch() 를 보시면....제가 정확히 해석은 못하나 이해한 뜻은 이렇습니다.

insert, update 구문을 데이타 베이스로 한번에 모아서 보낸다...뭐 이런뜻...?

그래서 속도향상에 도움을 줄수있다!


그리고

excuteBatch() 이것은...

모아뒀던 구문을 한번에 실행한다!

뭐 이런 뜻을 파악하고 있습니다.



그럼 이제 실제 사용했던 소스를 첨부하겠습니다.


public Object batchInsertData(final ArrayList<Map> list, final String data_gb, final String new_doc_no, final String del_yn, final String query) throws Exception {

		// ibatis의 SqlExecutor을 사용
		return getSqlMapClientTemplate().execute(
			
			new SqlMapClientCallback<Object> () {
				
				int totalCount = list.size();	// 전체 데이터의 건수
				@Override
				public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
			
					try {
						// 배치처리 시작
						executor.startBatch ();
						Map eachData;

						for (int idx = 0; idx < totalCount; idx++) {
							eachData = list.get(idx);
							
							eachData.put("data_gb", data_gb);
							eachData.put("data", data1);
							eachData.put("data2", data2);
							
							// 데이터 저장
							executor.insert(query, eachData);
							dataCount++;

							// 저장건수 1000건 마다 배치 처리
							if ((dataCount % 1000) == 0) {
								executor.executeBatch();
								batchCount++;
							}					
						}
						// 저장하지 않은 데이터 건수가 남아있다면 남은거 처리
						if ((batchCount * 1000) < totalCount) {
							executor.executeBatch();
						}
					} catch (Exception e) {
						throw new SQLException(e.getMessage());
					}finally{
						executor.executeBatch();
						
					}
					return dataCount;
				}
			}
		);
	}

간략하게 소스 설명을 드리자면!

저는 insnert 문을 적용시켰습니다.

insert문 전에 startBatch() 를 사용합니다.


그리고 excuteBatch()의 실행 시기는 

1000건마다 실행되게 해두었습니다.


실행 결과 이전보다 거의 10배???

코드 실행 당시 실행시간도 체크했었는데 정확한 시간차이는 생각이 안나네요


결론적으로! 대용량 insert/update는 배치를 활용하시는 것을 추천합니다.