๐ ๋๋ DB INSERT ์ต์ ํ
- CSVํ์ผ์ ์ฝ์ด ์ฌ์ฉ์ ์ผ๊ด ๋ฑ๋กํ๋ ๊ธฐ๋ฅ ๊ตฌํํจ.
- ์ฑ๋ฅ/๋ฉ๋ชจ๋ฆฌ ๊ณ ๋ ค ํ์ โก ์ํฅ์ฃผ๋ ์์ ์์๋ณด๊ณ ํ ์คํธํด๋ณด๊ธฐ๋ก ํจ!
๐ 1. ๋๋ Insert ์ ๋ฐ๋์ ๊ณ ๋ คํด์ผ ํ๋ ํต์ฌ ์์(๋ฉ๋ชจ๋ฆฌ + ์ฑ๋ฅ ๊ฐ์)
๐น ํธ๋์ญ์
ํฌ๊ธฐ(์ฑ๋ฅ)
→ ๋๋ฌด ํฐ ํธ๋์ญ์
= DB ๋ก๊ทธ ํญ์ฆ
→ 500~1000๊ฑด๋ง๋ค ์ปค๋ฐ ๊ถ์ฅ
๐น ๋คํธ์ํฌ/DB ๋ถํ(์ฑ๋ฅ)
→ DB ์ฑ๋ฅ ๋ฎ์ผ๋ฉด ๋ฐฐ์น ์ฌ์ด์ฆ ๋ ์ค์ด๊ธฐ
๐น ํ์ผ ํ์ฑ(๋ฉ๋ชจ๋ฆฌ)
→ DTO → Entity ๋ณํ ๊ณผ์ ์์ ๋ฉ๋ชจ๋ฆฌ ์ฆ๊ฐ
→ ์คํธ๋ฆฌ๋ฐ ๋ฐฉ์ ํ์ฉ
๐น ์ค๋ฅ ์ฒ๋ฆฌ ์ ๋ต(ํธ๋์ญ์
์ ์ฑ
)
→ ์คํจ ๋ฐ์ดํฐ๋ง ์ฌ์ฒ๋ฆฌํ ์ง?
→ ์ ์ฒด ๋กค๋ฐฑ์ธ์ง?→ ์๊ตฌ์ฌํญ ๋ฐ๋ผ ์ฒ๋ฆฌ ๋ฐฉ์ ๋ฌ๋ผ์ง
๐2. ๋ฉ๋ชจ๋ฆฌ ์ด์: CSV ํ์ฑ·์ ์ฅ ๋ฐฉ์์ด ๋ฉ๋ชจ๋ฆฌ์ ๋ฏธ์น๋ ์ํฅ
โ 2-1. ํ์ผ ํฌ๊ธฐ
- ์์ ํ์ผ์ ๋ฌธ์ ๊ฐ ์์ง๋ง, ์๋ง ์ค CSV๋ฅผ readAll()๋ก ์ฝ์ผ๋ฉด ๋ฉ๋ชจ๋ฆฌ๊ฐ ์๋ฐฑ MB ์ด์ ์ฆ๊ฐ
โ 2-2. ์ธ์ฝ๋ฉ ๋ฐฉ์(EUC-KR ๋ฑ)
- ๋ฉํฐ๋ฐ์ดํธ ๋ฌธ์(EUC-KR, CP949) ๋ณํ ์ ๋ด๋ถ์ ์ผ๋ก ๋ ๋ง์ ๋ฒํผ ์ฌ์ฉ
โ 2-3. ํ์ฑ/์ ์ฅ ๋ฐฉ์
ํ ์ค์ฉ ์ฝ์ ๋ → ๋ฉ๋ชจ๋ฆฌ ์ฆ์ ํด์ ๊ฐ๋ฅ
List์ ๋ด์๋๋ฉด → List ํฌ๊ธฐ × ๋ฌธ์์ด ๊ธธ์ด ๋งํผ ๋ฉ๋ชจ๋ฆฌ ์ฆ๊ฐ
์์(์๋ชป๋ ๋ฐฉ์):
List<String[]> allLines = new ArrayList<>();
while ((line = csvReader.readNext()) != null) {
allLines.add(line);
}
โก CSV ์ ์ฒด ๋ฉ๋ชจ๋ฆฌ ๋ก๋ → ๋๋์ผ ๋ OutOfMemory ์ํ
๐ 3. ์ฑ๋ฅ ์ด์: Insert ๋ฐฉ์๋ณ ์ฒ๋ฆฌ ์๋ ๋น๊ต
โ 3-1. ํ ์ค์ฉ Insert
โ ๋ฉ๋ชจ๋ฆฌ ๊ฑฐ์ ์ฌ์ฉ ์ ํจ (์๋น๋๊ณ ๊ณง๋ฐ๋ก GC ๋์์ด ๋จ. )
โ DB ์ปค๋ฅ์
/ํธ๋์ญ์
/๋คํธ์ํฌ ๋น์ฉ ๋งค๋ฒ ๋ฐ์ → ๋๋ ๋ฐ์ดํฐ ์ ๋งค์ฐ ๋๋ฆผ
๐น ์ ํฉํ ๊ฒฝ์ฐ:
- ์ฌ์ฉ์ ๊ฒ์ฆ·API ํธ์ถ ๋ฑ “ํ ๊ฑด ์ฒ๋ฆฌ ๋ก์ง”์ด ํ์ํ ์์
- ๋ฐ์ดํฐ๋ 200๊ฑด ์ดํ
โ 3-2. ๋ฐฐ์น(Batch) Insert
โ ๋คํธ์ํฌ/ํธ๋์ญ์
ํ์ ์ค์ด์ ์ฑ๋ฅ ์์น
โ ๋ฐฐ์น ํฌ๊ธฐ ๋๋ฌด ํฌ๋ฉด ๋ฉ๋ชจ๋ฆฌ ์ฆ๊ฐ
* ๋ฐฐ์น ํฌ๊ธฐ ๊ฒฐ์ ๊ธฐ์ค
- ๋ฉ๋ชจ๋ฆฌ ์ฌ์ : ํฌ๋ฉด ๋ฐฐ์น ํฌ๊ธฐ ↑
- ๋คํธ์ํฌ·DB ์ฑ๋ฅ: DB ๋ถํ๊ฐ ํฌ๋ฉด ๋ฐฐ์น ํฌ๊ธฐ ์ค์ด๊ธฐ
- DB ์ ์ฝ: MySQL max_allowed_packet ๋ฑ ํ์ธ ํ์
- ์ถ์ฒ ๋ฐฐ์น ํฌ๊ธฐ: 500~1000๊ฑด
๐น ์ ํฉํ ๊ฒฝ์ฐ:
- ๋จ์ ์ฝ์ ์์
- ๋๋ ๋ฐ์ดํฐ(์์ฒ~์๋ง ๊ฑด)
๐ 4. JDBC vs ORM ๋ฐฐ์น ์ ๋ต — ์ฑ๋ฅ·๋ฉ๋ชจ๋ฆฌ
โ 4-1. JDBC ๋ฐฐ์น Insert (๊ถ์ฅ)
- DB์ ์ง์ ์ฐ๊ฒฐํด SQL ์คํํ๋ ํ์ค API
- PreparedStatement + addBatch() + executeBatch()
- ๊ฐ์ฅ ๋น ๋ฆ
- ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ์ ์
- GC ์ํฅ ์ ์
String sql = "INSERT INTO USERS (name, email) VALUES (?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
int batchCount = 0;
while ((line = csv.readNext()) != null) {
pstmt.setString(1, line[0]);
pstmt.setString(2, line[1]);
pstmt.addBatch();
if (++batchCount % 1000 == 0) {
pstmt.executeBatch();
pstmt.clearBatch();
}
}
pstmt.executeBatch();
conn.commit();
}
โ 4-2. ORM(Hibernate) ๋ฐฐ์น Insert
- ๊ฐ์ฒด <-> ํ ์ด๋ธ ์๋ ๋งคํ
- Hibernate๋ 1์ฐจ ์บ์์ ์ํฐํฐ๋ฅผ ์ ์ฌํ๋ฏ๋ก ์ํฐํฐ๊ฐ ์์ผ์๋ก ๋ฉ๋ชจ๋ฆฌ ์ฆ๊ฐ
- batch_size ์ค์ + ์ฃผ๊ธฐ์ flush() / clear() ํ์
for (int i = 0; i < list.size(); i++) {
session.save(list.get(i));
if (i % 1000 == 0) {
session.flush();
session.clear();
}
}
๐ 5. CSV ์ฒ๋ฆฌ ๋ฐฉ์๋ณ๋ฉ๋ชจ๋ฆฌ ์๋น ์ธก์
โ 5-1. ํ ์คํธ ์๋๋ฆฌ์ค
- ์ค ๋จ์ Insert
- readAll() ์ ์ฒด ๋ฉ๋ชจ๋ฆฌ ๋ก๋ฉ
- List ์ ์ฌ ํ Bulk Insert
โ 5-2. ์ธก์ ๋ฐฉ์
- ๋ฉ๋ชจ๋ฆฌ ์ธก์ :
JVM์ด ์ฌ์ฉํ๋ ๋ฉ๋ชจ๋ฆฌ๋ Runtime.getRuntime() API๋ก ํ์ธ ๊ฐ๋ฅ
์ฌ์ฉ๋ = totalMemory() - freeMemory() - ์๊ฐ(์ฑ๋ฅ) ์ธก์ :
System.currentTimeMillis() ๋๋ Instant.now()๋ฅผ ์ฌ์ฉํด ์ฒ๋ฆฌ ์์/์ข ๋ฃ ์์ ์ ๊ธฐ๋กํ๊ณ , ๊ฒฝ๊ณผ ์๊ฐ์ ๊ณ์ฐ
โ 5-3. ์์ ์ฝ๋ (๋ฉ๋ชจ๋ฆฌ + ์ฒ๋ฆฌ ์๊ฐ ๋์ ์ธก์ )
untime runtime = Runtime.getRuntime();
long memBefore, memAfter;
long timeBefore, timeAfter;
// 1. One-by-one Insert
memBefore = runtime.totalMemory() - runtime.freeMemory();
timeBefore = System.currentTimeMillis();
while ((line = csvReader.readNext()) != null) {
insertUser(line); // ํ ์ค์ฉ DB Insert
}
timeAfter = System.currentTimeMillis();
memAfter = runtime.totalMemory() - runtime.freeMemory();
System.out.println("[Test 1] Memory: " + (memAfter - memBefore) + " bytes");
System.out.println("[Test 1] Time: " + (timeAfter - timeBefore) + " ms");
// 2. readAll() ์ ์ฒด ๋ก๋ฉ
memBefore = runtime.totalMemory() - runtime.freeMemory();
timeBefore = System.currentTimeMillis();
List<String[]> allData = csvReader.readAll(); // ๋ฉ๋ชจ๋ฆฌ ์ ์ฒด ์ ์ฌ
timeAfter = System.currentTimeMillis();
memAfter = runtime.totalMemory() - runtime.freeMemory();
System.out.println("[Test 2] Memory: " + (memAfter - memBefore) + " bytes");
System.out.println("[Test 2] Time: " + (timeAfter - timeBefore) + " ms");
// 3. Bulk Insert
memBefore = runtime.totalMemory() - runtime.freeMemory();
timeBefore = System.currentTimeMillis();
List<User> bulkList = convert(allData);
insertBulk(bulkList); // ๋ฐฐ์น Insert
timeAfter = System.currentTimeMillis();
memAfter = runtime.totalMemory() - runtime.freeMemory();
System.out.println("[Test 3] Memory: " + (memAfter - memBefore) + " bytes");
System.out.println("[Test 3] Time: " + (timeAfter - timeBefore) + " ms");
| ํ ์คํธ | ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ | ์ฒ๋ฆฌ ์๊ฐ |
| One-by-one Insert | 120,000 bytes | 1,200 ms |
| Full File Read | 4,520,000 bytes | 50 ms |
| Bulk Insert | 3,600,000 bytes | 150 ms |
โ ๏ธ ์ธก์ ์ ์ฃผ์์ฌํญ
- JVM GC๊ฐ ์คํ๋๋ฉด ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ด 0์ฒ๋ผ ๋ณด์ผ ์ ์์
- ์ธก์ ์ ํ ์์ ์ ๋ฐ๋ผ ๊ฐ์ด ๋ฌ๋ผ์ง ์ ์์
- Eclipse/IntelliJ์์ ํ ๋ฉ๋ชจ๋ฆฌ ์ํ ๋ณด๊ธฐ ์ผ๋ฉด GUI๋ก ํ์ธ ๊ฐ๋ฅ
์ดํด๋ฆฝ์ค ์ธํ : Window → Preferences → General → Show heap status ์ฒดํฌ => eclipse ์ฐฝ ํ๋จ์ ํ ๋ฉ๋ชจ๋ฆฌ ์ํ๊ฐ ์ค์ ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
โ 5-4. ๊ฒฐ๋ก
- ํ ์ค์ฉ Insert
- ํ์ผ ์ ์ฒด ๋ฉ๋ชจ๋ฆฌ ๋ก๋ฉ
- Bulk Insert (๋ฐฐ์น)
๐6. ๊ฒฐ๋ก
- ์๊ท๋ชจ ๋ฐ์ดํฐ → ํ ์ค์ฉ Insert ํด๋ ์ฑ๋ฅ·๋ฉ๋ชจ๋ฆฌ ๋ฌธ์ ์์
- ์์ฒ~์๋ง ๊ฑด ์ด์์ ๋์ฉ๋ CSV → ๋ฐ๋์ ๋ฐฐ์น Insert
- ๋ฐฐ์น ํฌ๊ธฐ๋ DB ์ฑ๋ฅ + ๋คํธ์ํฌ + JVM ๋ฉ๋ชจ๋ฆฌ์ ๋ฐ๋ผ ์กฐ์ (์ผ๋ฐ์ ์ผ๋ก 500~1000๊ฑด ๋จ์)
- ๋ชจ๋ํฐ๋ง ๋๊ตฌ ํ์ฉํด ์ต์ ๊ฐ ์ฐพ๊ธฐ → ํธ๋์ญ์ ·๋ฉ๋ชจ๋ฆฌ ์์ ์ ์ผ๋ก ๊ด๋ฆฌ
- ํธ๋์ญ์ ๊ด๋ฆฌ: ๋ฐฐ์น ์คํ๋ง๋ค ํธ๋์ญ์ ์ ์ปค๋ฐ.
'Backend > JAVA' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| jsonํ์ผ ์ฝ๊ธฐ (0) | 2025.12.18 |
|---|---|
| [JAVA] Client IP, Device Type ์ถ์ถ (IP ๊ธฐ๋ฐ ๋ก๊ทธ์ธ) (0) | 2025.12.18 |
| ํ์ผ ์ ๋ก๋, ๋ค์ด๋ก๋ (MultipartFile) (0) | 2025.11.08 |
| JUnit ๋จ์ ํ ์คํธ ์ ์ฉ, JaCoCo ์ปค๋ฒ๋ฆฌ์ง ์ธก์ (0) | 2025.08.27 |
| Stream API๋? (for-loop์ ๋น๊ต) (0) | 2025.08.09 |