대량의 데이터를 데이터베이스에 저장할 일이 생겼는데, statement를 작성하고 execute하는걸 반복문으로 돌리니 완료될때 까지 하루종일 걸렸다.
기존 코드
package com.dokuny.find_public_wifi.repository;
import com.dokuny.find_public_wifi.model.WifiApiDto;
import com.dokuny.find_public_wifi.service.ApiService;
import java.sql.*;
import java.util.ArrayList;
public class WifiRepositoryImpl implements WifiRepository {
public static void main(String[] args) {
WifiRepositoryImpl wifiRepository = new WifiRepositoryImpl();
wifiRepository.saveAll();
}
@Override
public void saveAll() {
ArrayList<WifiApiDto> wifiDataAll = ApiService.getWifiDataAll();
try {
Class.forName("org.sqlite.JDBC");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = DriverManager.getConnection("jdbc:sqlite:D:\\study\\플젝\\Find_Public_Wifi\\find_public_wifi");
String sql = "DELETE FROM wifi;";
pstmt = conn.prepareStatement(sql);
pstmt.execute();
for (WifiApiDto wifiApiDto : wifiDataAll) {
sql = "INSERT INTO wifi VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1,wifiApiDto.getX_SWIFI_MGR_NO());
pstmt.setString(2,wifiApiDto.getX_SWIFI_WRDOFC());
pstmt.setString(3,wifiApiDto.getX_SWIFI_MAIN_NM());
pstmt.setString(4,wifiApiDto.getX_SWIFI_ADRES1());
pstmt.setString(5,wifiApiDto.getX_SWIFI_ADRES2());
pstmt.setString(6,wifiApiDto.getX_SWIFI_INSTL_FLOOR());
pstmt.setString(7,wifiApiDto.getX_SWIFI_INSTL_TY());
pstmt.setString(8,wifiApiDto.getX_SWIFI_INSTL_MBY());
pstmt.setString(9,wifiApiDto.getX_SWIFI_SVC_SE());
pstmt.setString(10,wifiApiDto.getX_SWIFI_CMCWR());
pstmt.setString(11,wifiApiDto.getX_SWIFI_CNSTC_YEAR());
pstmt.setString(12,wifiApiDto.getX_SWIFI_INOUT_DOOR());
pstmt.setString(13,wifiApiDto.getX_SWIFI_REMARS3());
pstmt.setFloat(14,wifiApiDto.getLAT());
pstmt.setFloat(15,wifiApiDto.getLNT());
pstmt.setString(16,wifiApiDto.getWORK_DTTM());
pstmt.execute();
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
try {
if (rs != null && !rs.isClosed()) {
rs.close();
}
if (pstmt != null && !pstmt.isClosed()) {
pstmt.close();
}
if (conn != null && !conn.isClosed()) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
진짜 너무 오래 걸린다. 끝까지 기다려보려했는데 오래걸려서 포기했다...
그래서 찾아보니 addBatch()와 executeBatch()를 사용해서 한번에 쿼리를 날려보내면 빨라질 수도 있다고 하길래
- Statement.addBatch()
- 쿼리 구문을 대량으로 처리하기 위해 메모리에 올려두는 메소드
- Statement.executeBatch()
- 메모리에 올려져있는 쿼리들을 한번에 DB로 날리는 메소드
개선 코드 1 - addBatch(), executeBatch() 사용
package com.dokuny.find_public_wifi.repository;
import com.dokuny.find_public_wifi.model.WifiApiDto;
import com.dokuny.find_public_wifi.service.ApiService;
import java.sql.*;
import java.util.ArrayList;
public class WifiRepositoryImpl implements WifiRepository {
public static void main(String[] args) {
WifiRepositoryImpl wifiRepository = new WifiRepositoryImpl();
wifiRepository.saveAll();
}
@Override
public void saveAll() {
ArrayList<WifiApiDto> wifiDataAll = ApiService.getWifiDataAll();
try {
Class.forName("org.sqlite.JDBC");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = DriverManager.getConnection("jdbc:sqlite:D:\\study\\플젝\\Find_Public_Wifi\\find_public_wifi");
String sql = "DELETE FROM wifi;";
pstmt = conn.prepareStatement(sql);
pstmt.execute();
pstmt = conn.prepareStatement("BEGIN TRANSACTION;");
pstmt.execute();
sql = "INSERT INTO wifi VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);";
pstmt = conn.prepareStatement(sql);
for (WifiApiDto wifiApiDto : wifiDataAll) {
pstmt.setString(1,wifiApiDto.getX_SWIFI_MGR_NO());
pstmt.setString(2,wifiApiDto.getX_SWIFI_WRDOFC());
pstmt.setString(3,wifiApiDto.getX_SWIFI_MAIN_NM());
pstmt.setString(4,wifiApiDto.getX_SWIFI_ADRES1());
pstmt.setString(5,wifiApiDto.getX_SWIFI_ADRES2());
pstmt.setString(6,wifiApiDto.getX_SWIFI_INSTL_FLOOR());
pstmt.setString(7,wifiApiDto.getX_SWIFI_INSTL_TY());
pstmt.setString(8,wifiApiDto.getX_SWIFI_INSTL_MBY());
pstmt.setString(9,wifiApiDto.getX_SWIFI_SVC_SE());
pstmt.setString(10,wifiApiDto.getX_SWIFI_CMCWR());
pstmt.setString(11,wifiApiDto.getX_SWIFI_CNSTC_YEAR());
pstmt.setString(12,wifiApiDto.getX_SWIFI_INOUT_DOOR());
pstmt.setString(13,wifiApiDto.getX_SWIFI_REMARS3());
pstmt.setFloat(14,wifiApiDto.getLAT());
pstmt.setFloat(15,wifiApiDto.getLNT());
pstmt.setString(16,wifiApiDto.getWORK_DTTM());
pstmt.addBatch();
pstmt.clearParameters();
}
pstmt.executeBatch();
pstmt.clearBatch();
pstmt = conn.prepareStatement("END TRANSACTION;");
pstmt.execute();
} catch (SQLException e) {
e.printStackTrace();
}finally {
try {
if (rs != null && !rs.isClosed()) {
rs.close();
}
if (pstmt != null && !pstmt.isClosed()) {
pstmt.close();
}
if (conn != null && !conn.isClosed()) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
위 코드처럼 개선해보았지만 똑같이 느렸다..
그래서 더 깊게 찾아보고 생각해보니 어차피 저렇게 한번에 날린다 하더라도
SQLite는 모든 INSERT, UPDATE 구문을 각각 하나의 트랜잭션으로 처리하기 때문에 오래걸리는 것 같다는 결론이 났다.
그렇다면 해결방법을 찾아보니 하나의 트랜잭션으로 묶어보는 방법이 있었다.
개선 코드 2 - 트랜잭션 하나로 묶기(Begin Transaction, End Transaction)
@Override
public void saveAll() {
ArrayList<WifiApiDto> wifiDataAll = ApiService.getWifiDataAll();
try {
Class.forName("org.sqlite.JDBC");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = DriverManager.getConnection("jdbc:sqlite:D:\\study\\플젝\\Find_Public_Wifi\\find_public_wifi");
String sql = "DELETE FROM wifi;";
pstmt = conn.prepareStatement(sql);
pstmt.execute();
pstmt = conn.prepareStatement("BEGIN TRANSACTION;");
pstmt.execute();
sql = "INSERT INTO wifi VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);";
pstmt = conn.prepareStatement(sql);
for (WifiApiDto wifiApiDto : wifiDataAll) {
pstmt.setString(1, wifiApiDto.getX_SWIFI_MGR_NO());
pstmt.setString(2, wifiApiDto.getX_SWIFI_WRDOFC());
pstmt.setString(3, wifiApiDto.getX_SWIFI_MAIN_NM());
pstmt.setString(4, wifiApiDto.getX_SWIFI_ADRES1());
pstmt.setString(5, wifiApiDto.getX_SWIFI_ADRES2());
pstmt.setString(6, wifiApiDto.getX_SWIFI_INSTL_FLOOR());
pstmt.setString(7, wifiApiDto.getX_SWIFI_INSTL_TY());
pstmt.setString(8, wifiApiDto.getX_SWIFI_INSTL_MBY());
pstmt.setString(9, wifiApiDto.getX_SWIFI_SVC_SE());
pstmt.setString(10, wifiApiDto.getX_SWIFI_CMCWR());
pstmt.setString(11, wifiApiDto.getX_SWIFI_CNSTC_YEAR());
pstmt.setString(12, wifiApiDto.getX_SWIFI_INOUT_DOOR());
pstmt.setString(13, wifiApiDto.getX_SWIFI_REMARS3());
pstmt.setFloat(14, wifiApiDto.getLAT());
pstmt.setFloat(15, wifiApiDto.getLNT());
pstmt.setString(16, wifiApiDto.getWORK_DTTM());
pstmt.addBatch();
pstmt.clearParameters();
}
pstmt.executeBatch();
pstmt.clearBatch();
pstmt = conn.prepareStatement("END TRANSACTION;");
pstmt.execute();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (pstmt != null && !pstmt.isClosed()) {
pstmt.close();
}
if (conn != null && !conn.isClosed()) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
그 오래 걸리던게 그래도 속도가 540ms ~ 600ms 사이로 걸리게 되었다!
그 외에 캐시 사이즈를 늘리는 방법도 있는데
개선 코드 3 - 캐시사이즈 변경 (PRAGMA cache_size)
public class WifiRepositoryImpl implements WifiRepository {
public static void main(String[] args) {
WifiRepositoryImpl wifiRepository = new WifiRepositoryImpl();
wifiRepository.saveAll();
}
@Override
public void saveAll() {
ArrayList<WifiApiDto> wifiDataAll = ApiService.getWifiDataAll();
try {
Class.forName("org.sqlite.JDBC");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = DriverManager.getConnection("jdbc:sqlite:D:\\study\\플젝\\Find_Public_Wifi\\find_public_wifi");
pstmt = conn.prepareStatement("PRAGMA cache_size=10000");
pstmt.execute();
String sql = "DELETE FROM wifi;";
pstmt = conn.prepareStatement(sql);
pstmt.execute();
long startTime = System.currentTimeMillis();
pstmt = conn.prepareStatement("BEGIN TRANSACTION;");
pstmt.execute();
sql = "INSERT INTO wifi VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);";
pstmt = conn.prepareStatement(sql);
for (WifiApiDto wifiApiDto : wifiDataAll) {
pstmt.setString(1, wifiApiDto.getX_SWIFI_MGR_NO());
pstmt.setString(2, wifiApiDto.getX_SWIFI_WRDOFC());
pstmt.setString(3, wifiApiDto.getX_SWIFI_MAIN_NM());
pstmt.setString(4, wifiApiDto.getX_SWIFI_ADRES1());
pstmt.setString(5, wifiApiDto.getX_SWIFI_ADRES2());
pstmt.setString(6, wifiApiDto.getX_SWIFI_INSTL_FLOOR());
pstmt.setString(7, wifiApiDto.getX_SWIFI_INSTL_TY());
pstmt.setString(8, wifiApiDto.getX_SWIFI_INSTL_MBY());
pstmt.setString(9, wifiApiDto.getX_SWIFI_SVC_SE());
pstmt.setString(10, wifiApiDto.getX_SWIFI_CMCWR());
pstmt.setString(11, wifiApiDto.getX_SWIFI_CNSTC_YEAR());
pstmt.setString(12, wifiApiDto.getX_SWIFI_INOUT_DOOR());
pstmt.setString(13, wifiApiDto.getX_SWIFI_REMARS3());
pstmt.setFloat(14, wifiApiDto.getLAT());
pstmt.setFloat(15, wifiApiDto.getLNT());
pstmt.setString(16, wifiApiDto.getWORK_DTTM());
pstmt.addBatch();
pstmt.clearParameters();
}
pstmt.executeBatch();
pstmt.clearBatch();
pstmt = conn.prepareStatement("END TRANSACTION;");
pstmt.execute();
long endTime = System.currentTimeMillis();
System.out.println(String.format("코드 실행 시간: %20dms", endTime - startTime));
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (pstmt != null && !pstmt.isClosed()) {
pstmt.close();
}
if (conn != null && !conn.isClosed()) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
살짝 더 빨라졌다. 500ms초반대로 나온다.
그 외에 더 빨라질 수 있는 방법이 있는지 찾아봐야겠지만 지금도 만족스럽다.
'JAVA > JAVA 기초' 카테고리의 다른 글
JDBC (0) | 2022.05.17 |
---|---|
자바 8 람다를 이용한 다중 조건 정렬 (0) | 2022.04.07 |
스트림 (Stream) (0) | 2022.03.29 |
람다식 (0) | 2022.03.29 |
컬렉션 프레임워크 (Collection Framework) (0) | 2022.03.29 |
댓글