๐Ÿ‚ JPA, Mybatis , Dead Lock์ด์Šˆ


์ •๋ณด

ํ•ด๋‹น ๋‚ด์šฉ์€ Hello ์ฃผ๊ฐ„ ์„ธ๋ฏธ๋‚˜ ์ค‘ ์ฃผ์ œ๋กœ ์„ ์ •๋œ,
ํˆฌ์žํ•˜๊ธฐ Dead Lock์ด์Šˆ ํ•ด๊ฒฐ๊ณผ์ •์— ๋Œ€ํ•œ ์„ค๋ช…์ด๋‹ค.


๋ฌธ์ œ

  • server log
Caused by: org.apache.ibatis.exceptions.PersistenceException:
###Error querying database. Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30008ms.
###The error occurred while executing a query
###Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30008ms.
Caused by: java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30008ms.
at com.zaxxer.hikari.pool.HikariPool.createTimeoutException(HikariPool.java:696)
at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:197)
at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:162)

ํ•ด๋‹น ์„œ๋ฒ„์˜ maximum-pool-size๋Š” 40์œผ๋กœ ์„ค์ •๋˜์–ด์žˆ์œผ๋ฉฐ, 30์ดˆ์˜ ๋Œ€๊ธฐ๋ฅผ ํ–ˆ์Œ์—๋„ Connection Pool์„ ํ• ๋‹น๋ฐ›์ง€ ๋ชปํ•œ ์ƒํ™ฉ์ด๋‹ค.

  • ์œ„ ์กฐ๊ฑด ๋ฐœ์ƒ ์ฝ”๋“œ ์˜ˆ์‹œ
public void deadLockMethod(){
    mybatisSelectMethod();    
    
    jpaSelectMethod();
    
    mybatisSelectMethod();
}

์›์ธ ๋ถ„์„

๋‹จ์ˆœ ๋ถ€ํ•˜ ๋ฌธ์ œ์ธ๊ฐ€?

  • ํ•ด๋‹น ๋กœ์ง์€ ๋ช‡๊ฐœ์›” ์ „ ์ˆ˜์ • ๋œ ์ดํ›„, ๊ณ„์†ํ•ด์„œ ์ •์ƒ์ ์œผ๋กœ ์šด์˜๋˜๋˜ ์ฝ”๋“œ์ด๋‹ค.
  • ์ˆœ๊ฐ„์ ์œผ๋กœ ํŠธ๋ ˆํ”ฝ์ด ๋ชฐ๋ฆฐ ์ƒํ™ฉ์„ ๊ฐ€์ •ํ•˜๋”๋ผ๋„, 40๊ฐœ์˜ pool์ด 30์ดˆ๊ฐ„ ์ ์œ ๋ฅผ ์ง€์†ํ•œ ๊ฒƒ์€ ๋น„์ •์ƒ ์ ์ด๋‹ค.

deadlock์ด ์›์ธ์ธ๊ฐ€??

  • ์ผ๋ฐ˜์ ์œผ๋กœ deadlock์€ DB๋ ˆ๋ฒจ์—์„œ์˜ ํŠธ๋ Œ์ ์…˜์ด ์„œ๋กœ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ฒฝ์šฐ์— ๋งŽ์ด ๋ฐœ์ƒํ•œ๋‹ค.
  • ํ•˜์ง€๋งŒ, Stack trace๋ฅผ ํ™•์ธํ•˜์˜€์„ ๋•Œ, DB๋ ˆ๋ฒจ์˜ deadlock์€ ์•„๋‹ˆ์—ˆ๋‹ค.

Connection Pool ์ƒํ˜ธ ์„ ์  ํ›„ ๋Œ€๊ธฐ?

  • ์„œ๋น„์Šค ๋กœ์ง์—์„œ ํ•œ ์„œ๋น„์Šค๊ฐ€ ์ปค๋„ฅ์…˜ํ’€์„ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š๊ณ , ๋˜ ๋‹ค๋ฅธ ์„œ๋น„์Šค๊ฐ€ ์ปค๋„ฅ์…˜ ํ’€์„ ์š”์ฒญํ•˜๋ฉด ๋ฌดํ•œ ์ˆœํ™˜์ด ๋ฐœ์ƒํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋‹ค.

์ฆ‰, ์œ„ ์˜ˆ์‹œ ์ฝ”๋“œ์—์„œ Connection Pool์„ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š๊ณ  ๋ฌดํ•œ์ • ๋Œ€๊ธฐํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๊ฐ€์žฅ ํฌ๋‹ค๊ณ  ํŒ๋‹จํ•˜์˜€๋‹ค.


Connection Pool์„ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š๋Š” ์ด์œ ?

public void deadLockMethod(){
    mybatisSelectMethod();    
    
    jpaSelectMethod();
    
    mybatisSelectMethod();
}

์ปค๋„ฅ์…˜ ํ’€ ์ƒํ˜ธ ์ ์œ ๊ฐ€ ์ผ์–ด๋‚˜๊ณ  ์žˆ๋Š” ์„œ๋น„์Šค ๋กœ์ง์„ ๋ณด์•˜์„ ๋•Œ, ํŠน๋ณ„ํ•œ ์ ์€ ๋ณด์ด์ง€ ์•Š๋Š”๋‹ค. ๊ทธ๋Ÿฌ๋‚˜, ํ•œ๊ฐ€์ง€ mybatis์™€ jpa๋ฅผ ํ˜ผ์šฉํ•ด์„œ ์‚ฌ์šฉ์ค‘์ธ ๋กœ์ง์ธ ์ ์ด ๋ˆˆ์— ๋›ด๋‹ค.

์ผ๋ฐ˜์ ์ธ ์ƒ๊ฐ์œผ๋กœ๋Š”?

@Transactional ์ด ๊ฑธ๋ ค์žˆ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, ์ˆœ์ฐจ์ ์œผ๋กœ

  1. mybatisSelectMethod(); : ์ปค๋„ฅ์…˜ ํ’€ ์ ์œ  ํ›„ ๋ฐ˜ํ™˜
  2. jpaSelectMethod(); : ์•คํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €์—์„œ ์ปค๋„ฅ์…˜ ํ’€ ์ ์œ  ํ›„ close(๋ฐ˜ํ™˜)
  3. mybatisSelectMethod(); : ์ปค๋„ฅ์…˜ ํ’€ ์ ์œ  ํ›„ ๋ฐ˜ํ™˜ (์ด๋•Œ ์ปค๋„ฅ์…˜ํ’€์ด ์ „๋ถ€ ์ ์œ ์ค‘์ด๋ผ๋ฉด ๋Œ€๊ธฐ 30s )

์˜ ์ˆœ์„œ๋กœ ์ง„ํ–‰๋  ๊ฑฐ๋ผ๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค.


MyBatis์™€ JPA ๋™์ž‘ ๋กœ์ง ๋‹จ์ˆœ ๋น„๊ต

MyBatis

MyBatis๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ JDBC ์ปค๋„ฅ์…˜์„ ๊ด€๋ฆฌํ•˜์ง€ ์•Š๊ณ , DataSource๋ฅผ ํ†ตํ•ด ์ปค๋„ฅ์…˜์„ ๊ฐ€์ ธ์˜ด. ์ฆ‰, Spring์—์„œ ์„ค์ •ํ•œ ์ปค๋„ฅ์…˜ ํ’€(HikariCP, DBCP ๋“ฑ)์„ ํ†ตํ•ด ์ปค๋„ฅ์…˜์„ ๊ด€๋ฆฌํ•œ๋‹ค.**

๊ธฐ๋ณธ ํ๋ฆ„

  1. MyBatis๊ฐ€ DataSource(ex: HikariCP)์—์„œ ์ปค๋„ฅ์…˜์„ ์š”์ฒญ
  2. SQL ์‹คํ–‰ (SELECT ๋ฌธ ์ˆ˜ํ–‰)
  3. ์ปค๋„ฅ์…˜์ด ์ž๋™ ๋ฐ˜ํ™˜๋จ (์ปค๋ฐ‹/๋กค๋ฐฑ ํ•„์š” ์—†์Œ)

JPA

jpa๋˜ํ•œ ์ปค๋„ฅ์…˜ ํ’€์„ ํ†ตํ•ด ์ปค๋„ฅ์…˜์„ ๊ด€๋ฆฌํ•˜์ง€๋งŒ, ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ(์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €)์— ๊ถŒํ•œ์„ ์œ„์ž„ํ•œ๋‹ค. ์•คํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ž : ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ํ•ต์‹ฌ ๊ฐ์ฒด

๊ธฐ๋ณธํ๋ฆ„

  1. jpa๊ฐ€ ์•คํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €๋ฅผ ํ†ตํ•ด DataSource(ex: HikariCP)์—์„œ ์ปค๋„ฅ์…˜์„ ์š”์ฒญ
  2. SQL ์‹คํ–‰ (find(SELECT ๋ฌธ) ์ˆ˜ํ–‰)
  3. ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ € Close
  4. ์ปค๋„ฅ์…˜ ๋ฐ˜ํ™˜

๋น„์Šทํ•œ Connection Pool time out ์ด์Šˆ ๋ณด๋Ÿฌ๊ฐ€๊ธฐ

์–ด๋А ๋ถ€๋ถ„์—์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‚˜?

  • ์•คํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €๊ฐ€ Connection Pool์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ์‹œ์ ์€ ์–ธ์ œ์ธ๊ฐ€

์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ ์ง€์† ๋ฒ”์œ„ -> ์„œ์น˜ ์ค‘ ์œ„ ๋‚ด์šฉ์„ ์ฐธ๊ณ ํ•˜์—ฌ, OSIV ๋ผ๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ๋˜์—ˆ๋‹ค.

OSIV(Open Session In View)๋ž€?

์ž์„ธํ•œ ์„ค๋ช…๋ณด๋Ÿฌ๊ฐ€๊ธฐ โ–ถ

๐ŸŒ‹ OSIV๋ž€ ๋ฌด์—‡์ธ๊ฐ€

Pasted image 20250314122906.png OSIV๋ž€ View์— ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•  ๋•Œ ์ง€์—ฐ ๋กœ๋”ฉ ๋“ฑ์˜ ์ด์œ ๋กœ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์ง€์†ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์— ์‚ฌ์šฉ๋˜๋Š” ๊ฒƒ์ด๋‹ค. ์ฆ‰, ์˜์†์„ฑ ์ปจํƒ์ŠคํŠธ(์•คํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €)์˜ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ์›น ์š”์ฒญ์ด ๋๋‚  ๋•Œ ๊นŒ์ง€ ์—ฐ์žฅํ•˜๋Š” ์˜ต์…˜์ด๋‹ค. ์ด ์„ค์ •์€ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋ณ„๋‹ค๋ฅธ ์„ค์ •์„ ํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด default ON ์ƒํƒœ์ด๋‹ค.

public void deadLockMethod(){
    mybatisSelectMethod();  -- 1
    
    jpaSelectMethod(); -- 2
    
    mybatisSelectMethod(); -- 3
}

๋‹ค์‹œ ํ•œ๋ฒˆ ์œ„ ์ฝ”๋“œ๋ฅผ ๋ณด์ž.

ํŠธ๋ž˜ํ”ฝ์ด ๋ชฐ๋ ค ์ปค๋„ฅ์…˜ ํ’€ 40๊ฐœ๊ฐ€ ์ „๋ถ€ ์ ์œ ๋˜์—ˆ์„ ๋•Œ๋ฅผ ๊ฐ€์ •ํ•˜์ž.


@Service
public class TestServiceImpl implements TestService {
    private final MybatisRepo mybatisRepo;
    private final JpaRepo jpaRepo;

    public TestServiceImpl(MybatisRepo mybatisRepo,
                             JpaRepo jpaRepo) {
        this.mybatisRepo = mybatisRepo;
        this.jpaRepo = jpaRepo;
    }

    public void test() {
        // JPA
        jpaRepo.somethingRun();
        // MyBatis
        mybatisRepo.somethingRun();
    }
}

์กฐ๊ฑด

  • ์—ฌ๋Ÿฌ๊ฐœ์˜ ํด๋ผ์–ธํŠธ์˜ ์š”์ฒญ์ด ๋™์‹œ์— ๋ฐœ์ƒ
  • 2๊ฐœ ์ด์ƒ์˜ ํด๋ผ์ด์–ธํŠธ๊ฐ€ 2๋ฒˆํ•จ์ˆ˜๋ฅผ ์ˆ˜ํ–‰ ํ›„, ์˜์†์„ฑ ์ปจํƒ์ŠคํŠธ๋ฅผ ์œ ์ง€ ์ค‘
  • 3๋ฒˆํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๋ ค๊ณ  ํ•˜๋‚˜, ์ปค๋„ฅ์…˜ ํ’€์ด ๊ฐ€๋“์ฐจ ๋Œ€๊ธฐ์ƒํƒœ์— ๋Œ์ž…

์‹คํ–‰

  1. ๊ฐœ๋ฐœ์ž๋Š” 2๋ฒˆํ•จ์ˆ˜๋Š” ๋™์ž‘์„ ์™„๋ฃŒํ•œ ํ›„ ์ปค๋„ฅ์…˜ํ’€์ด ํ•ด์ œ๋˜๊ธธ ๊ธฐ๋Œ€ํ•จ.
  2. OSIV ์˜ต์…˜์ด ์ผœ์ ธ์žˆ์„๋•Œ, Lazy Loading์ด view๋ ˆ์ด์–ด ๊นŒ์ง€ ์ด์–ด์ง.
    • ์ฆ‰, ๋™์ž‘์ด ์™„๋ฃŒ๋˜์–ด๋„ ์ปค๋„ฅ์…˜ํ’€์„ ํ•ด์ œํ•˜์ง€ ์•Š์Œ.
  3. 3๋ฒˆํ•จ์ˆ˜๋Š” ์ปค๋„ฅ์…˜ํ’€์ด ํ•ด์ œ๋˜๊ธธ ๋ฌดํ•œ์ • ๊ธฐ๋‹ค๋ฆผ.

ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

1. OSIV์˜ต์…˜ OFF

  • ํ•ด๋‹น ์„œ๋ฒ„์˜ ์˜ต์…˜์„ ๋„๋ฉด, ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ ๋ฌธ์ œ ๋ฐ ์ปค๋„ฅ์…˜ ์ ์œ  ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐ ๊ฐ€๋Šฅ ํ•œ๊ณ„ : ํ•ด๋‹น ์„œ๋ฒ„์˜ ๋‹ค๋ฅธ ์„œ๋น„์Šค ๊นŒ์ง€ ์ง์ ‘์ ์ธ ์˜ํ–ฅ์„ ๋ผ์นจ

2. @Transactional

  • @Transactional์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋”๋ผ๋„ ๊ฒฐ๊ณผ๋Š” ๋งˆ์ฐฌ๊ฐ€์ง€.
  • ์˜คํžˆ๋ ค OSIV์˜ต์…˜ OFF์ด๋”๋ผ๋„ ๊ฐ™์€ ํŠธ๋ Œ์ ์…˜ ์•ˆ์— ์žˆ๋‹ค๋ฉด ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ๋†”์ฃผ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๊ฒฐ๊ณผ๋Š” ๋™์ผ.

์ฆ‰, DB์ปค๋„ฅ์…˜ ์ ์œ  ์‹œ์ ์˜ ์ฐจ์ด๋งŒ ์ƒ๊ธธ ๋ฟ, ๋ฐ˜ํ™˜์‹œ์ ์˜ ์ฐจ์ด๋Š” ์—†๋‹ค.

๋‹จ, ํŠธ๋ Œ์ ์…˜ ๋งค๋‹ˆ์ €๋ฅผ ํ†ตํ•ฉํ•˜์—ฌ ์ฝ”๋“œ๋ฅผ ๋ฆฌํŽ™ํ† ๋งํ•œ๋‹ค๋ฉด ๊ฐ€๋Šฅ

3. JPA ๋˜๋Š” mybatis ๋‹จ์ผํ™”

  • JAP๋กœ ํ•จ์ˆ˜๋ฅผ ๋‹จ์ผํ™” ํ•œ๋‹ค๋ฉด, ๋™์ผํ•œ ์ปค๋„ฅ์…˜(HikariCP) ์•ˆ์—์„œ ์‹คํ–‰๋˜์–ด, ์ปค๋„ฅ์…˜ ํ’€ ๋ฌดํ•œ ๋Œ€๊ธฐ/์„ ์  ๋ฌธ์ œ๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋˜๋Š” mybatis๋กœ ๋‹จ์ผํ™” ํ•˜๋”๋ผ๋„, ์ฒ˜๋ฆฌ ์ฆ‰์‹œ ์ปค๋„ฅ์…˜ํ’€์„ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฌธ์ œํ•ด๊ฒฐ ๊ฐ€๋Šฅ