Backend/JAVA

๋Œ€๋Ÿ‰ DB INSERT ์ตœ์ ํ™”

dddzr 2025. 11. 29. 17:12

๐Ÿ“Œ ๋Œ€๋Ÿ‰ 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. ํ…Œ์ŠคํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค

  1. ์ค„ ๋‹จ์œ„ Insert
  2. readAll() ์ „์ฒด ๋ฉ”๋ชจ๋ฆฌ ๋กœ๋”ฉ
  3. 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๊ฑด ๋‹จ์œ„)
  • ๋ชจ๋‹ˆํ„ฐ๋ง ๋„๊ตฌ ํ™œ์šฉํ•ด ์ตœ์ ๊ฐ’ ์ฐพ๊ธฐ → ํŠธ๋žœ์žญ์…˜·๋ฉ”๋ชจ๋ฆฌ ์•ˆ์ •์ ์œผ๋กœ ๊ด€๋ฆฌ
  • ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ: ๋ฐฐ์น˜ ์‹คํ–‰๋งˆ๋‹ค ํŠธ๋žœ์žญ์…˜์„ ์ปค๋ฐ‹.