μλ νμΈμ, μ΄λ² ν¬μ€ν μμλ λμμ±(Concurrency)μ λν΄ μ΄ν΄λ³΄κ² μ΅λλ€.
(μμ μ½λλ κΉνλΈμμ νμΈνμ€ μ μμ΅λλ€.)
λμμ±(Concurrency) κ°λ
λ€μ΄λ² μ¬μ μ κ²μν΄λ³Έ λμμ±μ λ€μκ³Ό κ°μ΅λλ€.
μ΄λ€ λ μ¬κ±΄μ΄ κ°μ μκ°μ μΌμ΄λλ κ²μ μ΄λ₯΄λ λ§
λλΆλΆμ μΉ μλ²λ μ¬λ¬ κ°μ μμ²(ν΄λΌμ΄μΈνΈ)μ λμμ μνν μ μκ³ , μ΄λ μμ±ν μ½λκ° λμμ μνλ μ μλ€λ μλ―Έμ λμΌν λ§₯λ½μ λλ€.
μ΄λ¬ν λμμ± λ¬Έμ λ₯Ό μ΄ν΄λ³΄κΈ° μν΄ κ²μκΈμμ ν΄λΌμ΄μΈνΈκ° λμμ μ’μμλ₯Ό νμ λ λ°μν μ μλ λ¬Έμ μ λν΄ μ΄ν΄λ³΄κ² μ΅λλ€.
μ κ·Έλ¦Όμ λν μ€λͺ μ λ€μκ³Ό κ°μ΅λλ€.
- User Aκ° κ²μκΈ 1μ μ’μμλ₯Ό ν΄λ¦νμ λ likeCount μ‘°ν (likeCount = 0)
- User Bλ κ²μκΈ 1μ μ’μμλ₯Ό ν΄λ¦νμ λ likeCount μ‘°ν (likeCount = 0)
- Bλ Aκ° ν΄λ¦ν μμ μ΄ μ²λ¦¬λκΈ° μ μ κ°μ μ‘°ννμΌλ―λ‘ μΉ΄μ΄νΈλ 0
- User Aκ° ν΄λ¦μ λν΄ commitμ νκ³ , μ‘°ν μ + 1 μ¦κ° (likeCount = 1)
- User Bλ ν΄λ¦μ ν΅ν΄ commitμ νκ³ , μ‘°ν μ + 1 μ¦κ° (likeCount = 1)
λ λͺ μ μ¬μ©μκ° μ’μμλ₯Ό ν΄λ¦νκΈ° λλ¬Έμ μ΅μ’ countλ 2κ° λμ΄μΌ νμ§λ§, κ° μ μ κ° countλ₯Ό +1 νκΈ° μν΄ μ‘°ννμ λμ κ°μ λͺ¨λ 0μ΄κΈ° λλ¬Έμ 2κ° μλ 1μ΄ μ¦κ°λκ³ , μ΅μ’ κ²°κ³Όλ +1μ΄ μ¦κ°νμ¬ 1μ΄ λλ λμμ± λ¬Έμ κ° λ°μν©λλ€.
μμ κ°μ μν©μ κ²½μ 쑰건(race condition)μ΄λΌκ³ ν©λλ€.
κ²½μ 쑰건(race condition)
- μ¬λ¬ νλ‘μΈμ€ λ° μ€λ λκ° λμμ λμΌν λ°μ΄ν°(곡μ λ°μ΄ν°)λ₯Ό μ‘°μν λ νμ΄λ°μ΄λ μ κ·Ό μμμ λ°λΌ μμνλ κ²°κ³Όκ° λ¬λΌμ§ μ μλ μν©μ μλ―Έν©λλ€.
μ°Έκ³ : https://www.youtube.com/watch?v=vp0Gckz3z64&t=3s
μ¬μ©μκ° μ’μμλ₯Ό ν΄λ¦νλ κ³Όμ μ CPU levelμμ λ΄€μλ Read-Modify-Write(μ‘°ν-μμ -μ°κΈ°) κ³Όμ μ ν΅ν΄ μ΄λ£¨μ΄μ§κ² λ©λλ€.
- likeCountλ₯Ό Registerλ‘ Loadνκ³ (Read)
- Register = Register + 1 (Modify)
- Registerλ₯Ό likeCountμ Store ν©λλ€. (Write)
λμμ±(Concurrency) μ΄μμ λ¬Έμ μ
μμ κ°μ λμμ± μ΄μμλ μ΄λ ν λ¬Έμ μ λ€μ΄ μ‘΄μ¬ν κΉμ?
λλ΅μ μΌλ‘ μλμ κ°μ λ¬Έμ μ λ€μ΄ μ‘΄μ¬ν κ² κ°μ΅λλ€.
- 곡μ λ°μ΄ν°μ λν΄ μμ κ²°κ³Όκ° λ€λ₯΄μ§λ§ μ€λ₯κ° λ°μνμ§ μμ΅λλ€.
- μ½λλ₯Ό μμ±ν λλ λ‘컬μμ κ°λ°νκΈ° λλ¬Έμ νμ νκΈ°κ° νλλλ€.
- λΉμ νμ μΌλ‘ λ°μνκΈ° λλ¬Έμ λλ²κΉ μ΄ νλ€ μ μμ΅λλ€.
μ½λλ‘ μ΄ν΄λ³΄λ λμμ± λ¬Έμ
μ 체 μ½λλ κΉνλΈμ μ‘΄μ¬νλ©°, κ°λ¨ν μ£Όμ λ‘μ§λ§ μ΄ν΄λ³΄λ©΄ λ€μκ³Ό κ°μ΅λλ€.
Post id = 1μΈ κ²μκΈμ νμ¬ κ°μ λλ€.
curl λͺ λ Ήμ΄λ‘ λ€μκ³Ό κ°μ΄ λ κ°μ μμ²μ 보λ λλ€.
μ€νλ 쿼리λ₯Ό 보면 update 쿼리λ μ΄ λ κ°μ΄μ§λ§, μ΄μ μ μ΄ν΄λ³Έ μν©μ²λΌ μ΅μ’ κ²°κ³Όλ +1λ§ λμμ΅λλ€.
λμμ± λ¬Έμ ν΄κ²° - 1. synchronized
κΈ°μ‘΄μ λμμ± λ¬Έμ μ κ΄λ ¨ν΄μ ν΄κ²°ν μ μλ λ°©λ² μ€ νλλ‘ λ©μλμ synchronized ν€μλλ₯Ό μκ°ν΄λ΄€μ΅λλ€.
λ©μλμ synchronizedλ₯Ό μ μ©νλ©΄ λμμ± λ¬Έμ λ λ°μνμ§ μμκΉμ? ν μ€νΈλ₯Ό ν΄λ³΄κ² μ΅λλ€.
(Serviceλ Domainμ΄λ μ΄λμ synchronizedλ₯Ό μΆκ°νλ κ²°κ³Όλ λμΌνκΈ°μ μμΉλ ν¬κ² μκ΄μ μμ κ² κ°μ΅λλ€.)
μ΄μ κ³Ό λμΌνκ² λ λ²μ μμ²μ 보λμΌλ μ¬μ ν λμμ± λ¬Έμ λ λ°μνμκ³ , μ΅μ’ κ²°κ³Όλ μμ λ§μ°¬κ°μ§λ‘ 1λ§ μ¦κ°νμ΅λλ€. π§
κ·ΈλΌ synchronizedκ° λμμ± λ¬Έμ λ₯Ό ν΄κ²°ν μ μλ€λ κ²°κ³ΌμΈλ°μ, μ΄λ»κ² λ κ±ΈκΉμ?
μμ λν λ¬Έμ λ μλ Stackoverflowμμ μ°Ύμλ³Ό μ μμ΅λλ€.
- https://stackoverflow.com/questions/41767860/spring-transactional-with-synchronized-keyword-doesnt-work
- https://stackoverflow.com/questions/29427021/synchronized-transactional-method-in-spring
μ ν¬μ€ν μ λ΄μ©μ μ’ ν©ν΄λ³΄λ©΄ λ€μκ³Ό κ°μ΅λλ€.
- νΈλμμ (@Transactional)κ³Ό λκΈ°ν(synchronized)λ μλ‘ λ€λ₯Έ κ°λ
- νΈλμμ μ Begin - λ‘μ§ μ€ν(count + 1) - Commitμ μμλ‘ μ§νλλλ°, νλμ νΈλμμ μ΄ μ»€λ° λκΈ° μ λ€λ₯Έ νΈλμμ μ΄ μ€νλ μ μκ³ , μ΄λ‘ μΈν΄ λμμ± μ΄μκ° λ°μ
- synchronized ν€μλλ λ©μλκ° ν λ²μ νλμ μ€λ λμμλ§ μ€νν μ μλλ‘ Locking
synchronizedκ° μ²λ¦¬λλ κ³Όμ μ low-levelμμ μ΄ν΄λ³΄λ©΄ λͺ¨λν°(Monitor)λΌλ κ°λ μ ν΅ν΄ λκΈ°ν κ³Όμ μ΄ μ΄λ£¨μ΄μ§κ² λ©λλ€.
Monitor(λͺ¨λν°)λ 곡μ μμμ λν μ κ·Όμ μ μ΄νλ κ°μ²΄λ‘, ν λ²μ νλμ μ€λ λλ§ λ¦¬μμ€μ μ κ·Όν μ μλλ‘ κ³΅μ 리μμ€μ λν μ κ·Όμ λκΈ°ννλλ° μ¬μ©λλ κΈ°μ μ λλ€.
Monitorμ μ²λ¦¬ κ³Όμ μ μμ κ°μ΄ μ€λ λ λ¨μλ‘ λ©μλλ₯Ό μ€ννλ μμ μμ λͺ¨λν° λ½μ νλ(acquire) λ° λ°ν(release) ν©λλ€.
Monitorμ λν μμΈν κ°λ μ μλ μμμ μμΈν λμμμΌλ κΆκΈνμλ©΄ μμ & ν¬μ€ν μ μ°Έκ³ ν΄μ£ΌμΈμ π
- https://youtu.be/Dms1oBmRAlo
- https://happy-coding-day.tistory.com/8
λ°λΌμ Thread Aμ νΈλμμ λ΄μμ likePost() λ©μλκ° μνλ ν νΈλμμ μ΄ μ»€λ° λκΈ° μ Thread Bμ likePost() λ©μλκ° μ€νμ΄ λκΈ° λλ¬Έμ μμνλ λ°μ λ¬λ¦¬ synchronized ν€μλλ₯Ό ν΅ν΄ λμμ± λ¬Έμ λ₯Ό ν΄κ²°ν μ μμ΅λλ€.
(컀λ°μ΄ λκΈ° μ Thread Bκ° μ‘°νλ₯Ό νκΈ° λλ¬Έμ μΈλ(Undo) μμμ μ‘΄μ¬νλ countκ° μ¦κ°νκΈ° μ΄μ κ°μ κ°μ Έμ€κ² λ©λλ€.)
* μΈλ(Undo)
μΈλ μμμ UPDATE λ¬Έμ₯μ΄λ DELETEμ κ°μ λ¬Έμ₯μΌλ‘ λ°μ΄ν°λ₯Ό λ³κ²½νμ λ λ³κ²½λκΈ° μ μ λ°μ΄ν°(μ΄μ λ°μ΄ν°)λ₯Ό 보κ΄νλ κ³³μ λλ€. μλ₯Ό λ€μ΄ λ€μ 쿼리λ₯Ό μ΄ν΄λ³΄κ² μ΅λλ€.
MySQL > UPDATE member SET name = 'μ΄μ£Όν' WHERE member_id = '5';
μλ¬Έμ₯μ΄ μ€νλλ©΄ νΈλμμ μ μ»€λ° νμ§ μμλ μ€μ λ°μ΄ν° νμΌ λ΄μ©μ "μ΄μ£Όν"μΌλ‘ λ³κ²½μ΄ λ©λλ€. κ·Έλ¦¬κ³ λ³κ²½λκΈ° μ μ κ°μ΄ "νκΈΈλ"μ΄λΌλ©΄ μΈλ μμμλ "νκΈΈλ"μ΄λΌλ κ°μ΄ λ°±μ μ΄ λλ κ²μ λλ€. μ΄ μνμμ λ§μ½ μ¬μ©μκ° μ»€λ°μ νκ² λλ©΄ νμ¬ μν(μ΄μ£Όν)κ° κ·Έλλ‘ μ μ§λκ³ , λ‘€λ°±μ νκ² λλ€λ©΄ μΈλ μμμ λ°±μ λ λ°μ΄ν°(νκΈΈλ)λ₯Ό λ€μ λ°μ΄ν° νμΌλ‘ 볡ꡬν©λλ€.
μ΄λ¬ν μΈλμ λ°μ΄ν°λ ν¬κ² λ κ°μ§ μ©λλ‘ μ¬μ©μ΄ λ©λλ€.
1) νΈλμμ μ λ‘€λ°± λλΉμ©
2) νΈλμμ μ 격리 μμ€μ μ μ§νλ©΄μ λμ λμμ±μ μ 곡
μ λ¬Έμ λ₯Ό ν΄κ²°νλ €λ©΄ νΈλμμ μ΄ μ μ©λκΈ° μ λ¨κ³μμ synchronized ν€μλλ₯Ό μ μ©νκ±°λ, @Transactional μ κ±°λ₯Ό ν΅ν΄ λμμ± μ΄μλ₯Ό ν΄κ²°ν μ μμ κ² κ°μ΅λλ€. λ³λ‘ μ’μ λ°©λ²μ μλ κ² κ°μ§λ§, ν μ€νΈν΄λ΄ μλ€.
1. νΈλμμ μ΄ μ μ©λκΈ° μ synchronized μ μ©νκΈ°
μνλ λλ‘ λμμ± μ΄μλ ν΄κ²°μ΄ λμμ§λ§, μ±λ₯μ΄ λ§€μ° λΉν¨μ¨μ μΌ λ― ν©λλ€. (μλ 2λ² μ°Έκ³ )
2. @Transactional μ κ±°
μμ μ½λμ²λΌ @Transactional μ΄λ Έν μ΄μ μ μ κ±°νλ©΄ μμνλ κ²°κ³Όκ° λμ€κΈ΄ νμ§λ§, synchronizedλ‘ μΈν΄ λ©μλμ λͺ¨λ λμμ λν΄ Lockμ κ±Έμ΄ νλμ μ€λ λλ§ μ κ·Όμ΄ κ°λ₯νκΈ° λλ¬Έμ λ§μ μ€λ²ν€λκ° λ°μν©λλ€.
μ½ 1000λ²μ μμ²λ§ λ°μνλλΌλ λλ΅ 11μ΄κ° λλ μκ°μ΄ μμλ©λλ€.. π±
synchronized ν€μλλ λ λ€λ₯Έ λ¬Έμ κ° μ‘΄μ¬νλλ°μ μ΄λ λμΌν νλ‘μΈμ€ λ΄μ μ€λ λ λ¨μμμλ§ λμμ±μ 보μ₯ν©λλ€.
μ¦, λ¨μΌ μλ²λΌλ©΄ λμμ± μ΄μκ° λ°μνμ§ μκ² μ§λ§ μ€μ§μ μΌλ‘ μΉ νκ²½κ³Ό κ°μ΄ μ¬λ¬ λμ μλ²λ₯Ό νμ©νλ©΄ λμμ±μ 보μ₯ν μ μμ΅λλ€.
3. νΈλμμ μ 격리 μμ€μ SERIALIZABLEμΌλ‘ λ³κ²½
μ±λ₯μ λ§€μ° λ¨μ΄μ§κ² μ§λ§, 격리 μμ€μ΄ κ°μ₯ λμ SERIALIZABLEλ₯Ό μ€μ νλ©΄ ν΄κ²°λμ§ μμκΉ? λΌκ³ μκ°νμ¬ ν μ€νΈλ₯Ό ν΄λ³΄μμ΅λλ€.
(νΈλμμ μ 격리 μμ€μ λν΄μλ ν΄λΉ ν¬μ€ν μ μ°Έκ³ ν΄ μ£ΌμΈμ.)
νμ§λ§ Deadlockμ΄ λ°μνλλ°μ, μ΄λ SERIALIZABLEμ νΉμ§μ μκ°ν΄λ³΄λ©΄ μ΄ν΄ν μ μμ΅λλ€.
SERIALIZABLEμ 격리 μμ€μ SELECT μΏΌλ¦¬κ° μ¬μ©λλ λ μ½λ(RECORD)μ λν΄ μ½κΈ° λ½(Shared Lock)μ΄ κ±Έλ¦¬κ² λ©λλ€.
SELECT * FROM performance_schema.data_locks WHERE LOCK_TYPE = 'RECORD';
Shared Lockμ Lockμ μ»μ μΈμ μμ μ½κΈ°λ§ κ°λ₯νλ©° μ°κΈ°(Insert, Update, Delete) λ±μ μμ μ μνν μ μμ΅λλ€.
λ νΈλμμ μ΄ λμΌν λ°μ΄ν°(κ²μκΈ 1)μ λν΄ μ½κΈ° λ½(Shared Lock)μ κ±Έκ³ , κ·Έ ν μλ‘ μ°κΈ° λ½(Exclusive Lock)μ κ±ΈκΈ° λλ¬Έμ Deadlockμ΄ λ°μν©λλ€. (S-lock, X-lockμ κ²½μ° μλ‘ μ립ν μ μκΈ° λλ¬Έμ λλ€.)
λ νΈλμμ μ μ°κΈ° λ½μ κ±ΈκΈ° μν΄ μλ‘ Lockμ΄ ν΄μ λκΈ°λ₯Ό 무νν κΈ°λ€λ¦¬λ©° Deadlockμ΄ λ°μνκ² λ©λλ€.
μ 리
μ΄μμΌλ‘ μ’μμ κΈ°λ₯μ ν΅ν΄ λμμ± μ΄μμ μμ μ½λ, Javaμ synchronized ν€μλ λ±μ λν΄ μ΄ν΄λ³΄μμ΅λλ€.
synchronized ν€μλλ₯Ό ν΅ν΄ λμμ± μ΄μλ₯Ό ν΄κ²°νλ λ°©λ²λ μ‘΄μ¬νμ§λ§, μ¬λ¬ λμ μλ²λ₯Ό νμ©νλ μΉ νκ²½μμλ μ¬μ©νκΈ°κ° νλ€ κ² κ°μ΅λλ€.
λ€μμλ λ°μ΄ν°λ² μ΄μ€μμ μ 곡νλ λκ΄μ λ½(Optimistic Lock) λ° λΉκ΄μ λ½(Pessimistic Lock)μ ν΅ν΄ λμμ±μ μ μ΄νλ λ°©λ²μ λν΄ μ΄ν΄λ³΄κ² μ΅λλ€.
π μ°Έκ³
- https://stackoverflow.com/questions/41767860/spring-transactional-with-synchronized-keyword-doesnt-work
- https://stackoverflow.com/questions/29427021/synchronized-transactional-method-in-spring
- http://wannte.tistory.com/18
- https://hudi.blog/troubleshooting-of-category-role-concurrency-issue/
'Spring' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
Bean Validation (Hibernate Validation) (0) | 2023.09.30 |
---|---|
λ§λ€λ©΄μ λ°°μ°λ νλ‘ νΈ μ»¨νΈλ‘€λ¬(Front Controller) ν¨ν΄ (5) | 2023.08.16 |
Spring λΆμ° νκ²½μμ μΈμ κ΄λ¦¬νκΈ° (Redis Session Clustering) (2) | 2023.02.12 |
Spring DispatcherServlet(λμ€ν¨μ²μλΈλ¦Ώ) κ°λ λΆν° λμ κ³Όμ κΉμ§ (4) | 2023.02.11 |
SpringBoot Multi DataSource - JPA (0) | 2023.01.21 |
λκΈ