λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°
Spring

Spring Thread, Transaction, Connection 관계

by 주발2 2022. 9. 3.
λ°˜μ‘ν˜•

πŸ“Ž  Spring Thread, Transaction, Connection κ΄€κ³„

μ•ˆλ…•ν•˜μ„Έμš”, 졜근 ν† λΉ„μ˜ μŠ€ν”„λ§ λ„μ„œλ‘œ μŠ€ν„°λ”” 쀑 μ•„λž˜μ™€ λΉ„μŠ·ν•œ 질문이 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

 

"νŠΈλžœμž­μ…˜μ΄ μƒˆλ‘œ 생성될 λ•Œ μŠ€λ ˆλ“œκ°€ μƒˆλ‘œ μƒμ„±λ κΉŒμš”?"

 

이와 κ΄€λ ¨ν•˜μ—¬ μ•„λž˜μ˜ 관계듀에 λŒ€ν•΄ κ°„λž΅νžˆ μ •λ¦¬ν•΄λ³΄κ³ μž ν•©λ‹ˆλ‹€.

(κ΄€λ ¨ μ½”λ“œλŠ” GitHubμ—μ„œ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€. 🐝 )

  • Thread per transaction vs One thread for serveral transaction
  • Thread per connection vs One thread for all connections

ν‹€λ¦° λ‚΄μš©μ΄ μ‘΄μž¬ν•  수 μžˆμœΌλ‹ˆ μ΄λŸ¬ν•œ λΆ€λΆ„μ—μ„œλŠ” ν”Όλ“œλ°± μ£Όμ‹œλ©΄ κ°μ‚¬ν•˜κ² μŠ΅λ‹ˆλ‹€. πŸ˜‚

(β€» ν•΄λ‹Ή ν¬μŠ€νŒ…μ€ https://truehong.tistory.com/140 λΈ”λ‘œκ·Έλ₯Ό μ°Έκ³ ν•˜μ—¬ μž‘μ„±ν•˜μ˜€μŠ΅λ‹ˆλ‹€.)

 

 

Thread per transaction vs One thread for serveral transaction

λ¨Όμ € ν•˜λ‚˜μ˜ μŠ€λ ˆλ“œμ—μ„œ ν•˜λ‚˜μ˜ νŠΈλžœμž­μ…˜μ„ κ΄€λ¦¬ν•˜λŠ”μ§€ ν˜Ήμ€ ν•˜λ‚˜μ˜ μŠ€λ ˆλ“œμ—μ„œ μ—¬λŸ¬κ°œμ˜ νŠΈλžœμž­μ…˜μ„ κ΄€λ¦¬ν•˜λŠ”μ§€ κ°„λ‹¨ν•œ 예제λ₯Ό 톡해 μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@Service
@RequiredArgsConstructor
public class OuterService {
 
    private final InnerService innerService;
 
    @Transactional
    public void outerMethod() {
        System.out.println("=========================================");
        System.out.println(Thread.currentThread().getId() + ", " + Thread.currentThread().getName());
        System.out.println("=========================================");
        innerService.innerMethod();
    }
 
}
 
 
@Service
public class InnerService {
 
    /* μƒˆλ‘œμš΄ νŠΈλžœμž­μ…˜ μƒμ„± */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void innerMethod() {
        System.out.println("=========================================");
        System.out.println(Thread.currentThread().getId() + ", " + Thread.currentThread().getName());
        System.out.println("=========================================");
    }
}
 
 
...
 
@Test
void transactionNewTest() {
    outerService.outerMethod();
}
 
 
cs

κ²°κ³Όλ₯Ό 확인해보면 νŠΈλžœμž­μ…˜μ„ μƒˆλ‘œ μƒμ„±ν–ˆμœΌλ‚˜ μŠ€λ ˆλ“œμ˜ id, name이 λ™μΌν•œκ²ƒμœΌλ‘œ 보아 ν•˜λ‚˜μ˜ μŠ€λ ˆλ“œμ—μ„œ μ—¬λŸ¬ 개의 νŠΈλžœμž­μ…˜μ„ κ΄€λ¦¬ν•˜λŠ” 것을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

 

ν•˜λ‚˜μ˜ μŠ€λ ˆλ“œμ—μ„œ μ—¬λŸ¬ νŠΈλžœμž­μ…˜μ„ κ΄€λ¦¬ν•˜κΈ° λ•Œλ¬Έμ— μœ„ μ½”λ“œμ—μ„œ μƒˆλ‘œμš΄ νŠΈλžœμž­μ…˜μ΄ μƒμ„±λ˜λŠ” InnerServiceμ—μ„œ μ˜ˆμ™Έκ°€ λ°œμƒν•˜μ—¬ 둀백이 λœλ‹€λ©΄, μƒμœ„ νŠΈλžœμž­μ…˜μΈ OuterServiceμ—μ„œλ„ 둀백이 λœλ‹€λŠ” 것을 μ˜ˆμΈ‘ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
@Service
@RequiredArgsConstructor
public class OuterService {
 
    private final InnerService innerService;
    private final ItemRepository itemRepository;
 
    @Transactional
    public void outerMethod() {
        System.out.println("=========================================");
        System.out.println(Thread.currentThread().getId() + ", " + Thread.currentThread().getName());
        System.out.println("=========================================");
 
        itemRepository.addCount("item"0);
 
        innerService.innerMethod();
 
        // λ‘€λ°± λ˜μ–΄μ„œ μ‹€ν–‰ μ•ˆλ¨
        itemRepository.print();
    }
 
}
 
 
@Service
@RequiredArgsConstructor
public class InnerService {
 
    private final ItemRepository itemRepository;
 
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void innerMethod() {
        System.out.println("=========================================");
        System.out.println(Thread.currentThread().getId() + ", " + Thread.currentThread().getName());
 
        itemRepository.addCount("item"5);
 
        throw new RuntimeException();
    }
 
}
 
 
@Repository
public class ItemRepository {
 
    private Map<String, Integer> item = new HashMap<>();
 
    public void addCount(String name, Integer count) {
        item.put(name, count);
    }
 
    public void print() {
        System.out.println(item);
    }
 
}
 
 
@SpringBootTest
class OuterServiceTest {
 
    @Autowired
    private OuterService outerService;
 
    @Autowired
    private InnerService innerService;
 
    @Test
    void transactionNewTest() {
        outerService.outerMethod();
    }
}
 
cs

InnerServiceμ—μ„œ κ°•μ œμ μœΌλ‘œ μ˜ˆμ™Έλ₯Ό λ°œμƒμ‹œμΌœ 확인해보면 OuterService μ—μ„œλ„ itemRepository.save() λ©”μ„œλ“œκ°€ μ‹€ν–‰λ˜μ§€ μ•Šκ³  μ’…λ£Œλ˜λŠ” 것을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

 

 

How to manage thread per transaction

κ·Έλ ‡λ‹€λ©΄ μƒˆλ‘œμš΄ νŠΈλžœμž­μ…˜μ—μ„œ μƒˆλ‘œμš΄ μŠ€λ ˆλ“œλ‘œ κ΄€λ¦¬ν•˜κ³ μž ν•˜λ €λ©΄ μ–΄λ–»κ²Œ ν•΄μ•Όν• κΉŒμš”?

κ°„λ‹¨ν•œ λ°©λ²•μœΌλ‘œλŠ” 비동기인 @Async μ–΄λ…Έν…Œμ΄μ…˜μ„ μΆ”κ°€ν•˜λ©΄ λ˜κΈ΄ν•˜λ‚˜ ꢌμž₯ν•˜λŠ” 방법은 μ•„λ‹Œ λ“― ν•©λ‹ˆλ‹€.

μ˜ˆμ œμ—μ„œλŠ” νŠΈλžœμž­μ…˜μ„ μƒˆλ‘œ μƒμ„±ν•˜λŠ”(REQUIRES_NEW)λ₯Ό μ„€μ •ν•œ InnerService 클래슀의 λ©”μ„œλ“œμ— μ„€μ •ν•˜λ©΄ λ©λ‹ˆλ‹€.

1
2
3
4
5
6
7
8
9
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void innerMethod() {
        System.out.println("=========================================");
        System.out.println(Thread.currentThread().getId() + ", " + Thread.currentThread().getName());
 
        itemRepository.addCount("item"5);
 
        throw new RuntimeException();
    }
cs

μœ„μ™€ 같이 @Async μ–΄λ…Έν…Œμ΄μ…˜μ„ μ„€μ •ν•˜κ³  ν…ŒμŠ€νŠΈν•΄λ³΄λ©΄ InnerServiceμ—μ„œ μ˜ˆμ™Έκ°€ λ°œμƒν•˜μ—¬ λ‘€λ°±ν•˜μ§€λ§Œ, OuterServiceμ—λŠ” μ „νŒŒκ°€ λ˜μ§€ μ•Šκ³ (λ‘€λ°±λ˜μ§€μ•Šκ³ ) μ‹€ν–‰λ˜λŠ” 것을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

 

 

Thread per connection vs one thread for all connections

μŠ€ν”„λ§μ΄ μ œκ³΅ν•˜λŠ” νŠΈλžœμž­μ…˜ 좔상화 κ³„μΈ΅κ΅¬μ‘°λŠ” μ•„λž˜μ™€ κ°™μ€λ°μš”, PlatformTransactionManager μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜λŠ” λ°©μ‹μœΌλ‘œ JtaTransactionManager, JpaTransactionManager, DataSourceTransactionManager(Jdbc), HibernateTransactionManager λ“±μ˜ κ΅¬ν˜„μ²΄λ“€μ΄ μ‘΄μž¬ν•©λ‹ˆλ‹€.

 

 

μŠ€ν”„λ§μ˜ 경우 λ©€ν‹°μŠ€λ ˆλ“œ 기반으둜 μŠ€ν”„λ§ νŠΈλžœμž­μ…˜μ€ ν•˜λ‚˜μ˜ μŠ€λ ˆλ“œλ‹Ή μ—¬λŸ¬ 컀λ„₯μ…˜μ„ μ‚¬μš©ν•˜λŠ”λ°μš”, Jdbc 기반의 νŠΈλžœμž­μ…˜μ„ μ²˜λ¦¬ν•  λ•Œ μ‚¬μš©ν•˜λŠ” DataSourceTransactionManager 클래슀의 λ¬Έμ„œμ—μ„œ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

Binds a JDBC Connection from the specified DataSource to the current thread, potentially allowing for one thread-bound Connection per DataSource.

DataSource둜 λΆ€ν„° ν˜„μž¬ μŠ€λ ˆλ“œμ— JDBC Connection을 λ°”μΈλ”©ν•˜μ—¬ 잠재적으둜 DataSource λ‹Ή ν•˜λ‚˜μ˜ μŠ€λ ˆλ“œ μ—°κ²°ν•©λ‹ˆλ‹€.

 

즉, ν•˜λ‚˜μ˜ μŠ€λ ˆλ“œλ‹Ή μ—¬λŸ¬ 컀λ„₯μ…˜(μ—¬κΈ°μ„  졜초 컀λ„₯μ…˜ν’€μΈ DataSource에 μ„€μ •λœ 컀λ„₯μ…˜λ§ŒνΌ, μŠ€ν”„λ§λΆ€νŠΈ 2.0 이상에선 HikariCP)을 λ°”μΈλ”©ν•˜μ—¬ μ‚¬μš©ν•˜λŠ” λ“― ν•©λ‹ˆλ‹€.

 

μ•„λž˜ 섀정을 톡해 컀λ„₯μ…˜ν’€μ˜ μƒνƒœλ₯Ό 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

 

1
2
3
4
5
6
7
8
9
10
11
-- application.yml
logging:
  level:
    com.zaxxer.hikari: TRACE
    com.zaxxer.hikari.HikariConfig: DEBUG
 
 
-- application.properties
logging.level.com.zaxxer.hikari=TRACE
logging.level.com.zaxxer.hikari.HikariConfig=DEBUG
 
cs

 

μœ„ μ‚¬μ§„μ—μ„œ 총 10개의 컀λ„₯μ…˜μ„ 풀에 μΆ”κ°€λ₯Ό ν•΄λ†“λŠ”λ°, μ΄λŠ” HikariCP의 default κ°’ μž…λ‹ˆλ‹€.

μΆ”κ°€μ μœΌλ‘œ λ‹€λ₯Έ λ””ν΄νŠΈ 값듀이 κΆκΈˆν•˜λ©΄ HikariCPλ₯Ό μ°Έκ³ ν•΄μ£Όμ„Έμš”.

 

 

why one thread for all connections?

그럼 μ™œ ν•˜λ‚˜μ˜ μŠ€λ ˆλ“œμ— μ—¬λŸ¬ 컀λ„₯μ…˜μ„ μ‚¬μš©ν• κΉŒμš”? μ–΄μ°Œλ³΄λ©΄ λ‹Ήμ—°ν•œ 질문일 수 μžˆμ§€λ§Œ, μ•„λ¬΄λž˜λ„ 컀λ„₯μ…˜ λ‹Ή μŠ€λ ˆλ“œλ₯Ό μƒμ„±ν•˜λŠ” 것은 맀우 맀우 λΉ„νš¨μœ¨μ μΌν…λ°μš”, 예λ₯Ό λ“€μ–΄ 컀λ„₯μ…˜μ΄ 10,000이라면 μŠ€λ ˆλ“œλ„ 10,000κ°œκ°€ λ˜κΈ°λ•Œλ¬Έμ— 맀우 λ§Žμ€ μ˜€λ²„ν—€λ“œκ°€ μžˆμ„ λ“― ν•©λ‹ˆλ‹€.

 

이와 κ΄€λ ¨ν•˜μ—¬ stackoverflow에 닡변이 λ‚˜μ™€μžˆλŠ”λ°μš”, μš”μ•½ν•˜μžλ©΄ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  1. Stack memory μ ˆμ•½ - Non-blocking IO saves the stack memory
  2. λΆ€ν•˜κ°€ 높을 λ•Œ Context Switching 및 컀널 μ „ν™˜ κ°μ†Œ

ν•˜λ‚˜μ˜ μŠ€λ ˆλ“œμ— μ—¬λŸ¬ 컀λ„₯μ…˜μ„ μ‚¬μš©ν•˜λŠ” μ΄μœ λŠ” 사싀상 Stack memoryλ₯Ό μ ˆμ•½ν•˜κ³ μž ν•˜λŠ” μ΄μœ κ°€ 99%정도라고 ν•©λ‹ˆλ‹€.

 

이λ₯Ό ν”„λ‘œμ„ΈμŠ€ 및 μŠ€λ ˆλ“œμ˜ κ°œλ…μ— λΉ—λŒ€μ–΄λ³΄λ©΄.. μŠ€λ ˆλ“œλŠ” ν”„λ‘œμ„ΈμŠ€ λ‚΄μ—μ„œ Stack만 λ”°λ‘œ 할당을받고 κ·Έ μ™Έμ˜ μ˜μ—­μ€ κ³΅μœ κ°€ κ°€λŠ₯ν•œ μ˜μ—­μΈλ° 이둜 인해 μŠ€λ ˆλ“œκ°€ λŠ˜μ–΄λ‚ μˆ˜λ‘ κ³΅μœ κ°€ λΆˆκ°€λŠ₯ν•œ Stack λ©”λͺ¨λ¦¬κ°€ κ³„μ†ν•΄μ„œ μ¦κ°€ν•˜κΈ° λ•Œλ¬Έμ΄ μ•„λ‹κΉŒ μƒκ°μ„ν•©λ‹ˆλ‹€.

 

 

References

 

 

 

λ°˜μ‘ν˜•

λŒ“κΈ€