์์ ๋ฑ ํน ์๋น์ค๋ฅผ ์ฃผ์ ๋ก ํ ํ๋ก์ ํธ๋ฅผ ์งํํ์ง๋ง, ๋์์ฑ์ ๋ํ ์ฒ๋ฆฌ๋ฅผ ํด์ฃผ์ง ๋ชป ํ ์ ์ ๋ณด์ํ๊ธฐ ์ํด ๊ณต๋ถํ ๋ด์ฉ์ ์ ๋ฆฌํ ๊ธ์ ๋๋ค.
๋์์ฑ ์ด์ ํด๊ฒฐ๋ฐฉ๋ฒ
synchronized
- ํ๋์ ํ๋ก์ธ์ค์์๋ง ๋์. ์ฆ ์๋ฒ๊ฐ ์ฌ๋ฌ๋์ด๊ณ ์๋ก ๋ค๋ฅธ ์๋ฒ์์ ๋์์ ์ ๊ทผํ๋ค๋ฉด synchronized ๋ ๊ธฐ๋ฅ์ ๋ฐํํ ์ ์๋ค.
- @Transactional ์ ์ฌ์ฉํ๋ฉด, ํธ๋์ญ์ ์ด ์ปค๋ฐํ ๋ ์ ๊น์ ํ ์ ๋ค๋ฅธ ์ค๋ ๋๊ฐ ์ ๊ทผํ ์ ์์ด์ ๋์์ฑ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ค.
Lock (DB์์ ์ฌ๊ณตํ๋ ๋ฝ๊ธฐ๋ฅ์ ์ฌ์ฉ)
Pessimistic Lock (๋น๊ด์ ๋ฝ)
- row๋ table ๋จ์๋ก ๋ฝ์ ๊ฒ
- ์์ ์์ฒญ์ ๋ฐ๋ฅธ ๋์์ฑ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ๊ฒ์ด๋ผ๊ณ ์์ํ๊ณ ๋ฝ์ ๊ฑธ์ด๋ฒ๋ฆฌ๋ ๋น๊ด์ ๋ฝ
- ๊ณต์ ๋ฝ(Shared Lock)๊ณผ ๋ฒ ํ๋ฝ(Exclusive Lock) ๋ ์ค ํ๋์ ๋ฝ์ ๊ฑธ์ด์ฃผ๋ ๋ฐฉ๋ฒ
- ๊ณต์ ๋ฝ(Shared Lock) : ๋ค๋ฅธ ํธ๋์ญ์ ์์ ์ฝ๊ธฐ๋ง ๊ฐ๋ฅ
- ๋ฒ ํ๋ฝ(Exclusive Lock) : ๋ค๋ฅธ ํธ๋์ญ์ ์์ ์ฝ๊ธฐ, ์ฐ๊ธฐ ๋ ๋ค ๋ถ๊ฐ๋ฅ.
- ๋ฐ๋๋ฝ์ด ๋ฐ์ํ ์ ์๊ธฐ ๋๋ฌธ์ ์ฃผ์ํด์ผ ํจ.
- ๋ฐ๋๋ฝ : ํ์ฌ ์๋ก ์ํ๋ ์์์ด ์๋๋ฐฉ์ ํ ๋น๋์ด ์์ด์ ๋ ํ๋ก์ธ์ค๋ ๋ฌดํ์ wait ์ํ์ ๋น ์ง
- ๋ฐ์ดํฐ ์ ํฉ์ฑ์ ๋ณด์ฅํ๋ ๋์ ์ ์กฐํํ ๋ ๋งค๋ฒ ๋ฝ์ ์ฌ์ฉํ๋ฏ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ถ๋ด์ ์ค ์ ์๋ค.
- SpringDatJpa์์ respository์ @Lock ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํด ๊ตฌํํ ์ ์์
@Lock(LockModeType.PESSIMISTIC_READ) // ๊ณต์ ๋ฝ : ๋ค๋ฅธ ํธ๋์ญ์
์์ ์ฝ๊ธฐ๋ง ๊ฐ๋ฅ
@Lock(LockModeType.PESSIMISTIC_WRITE) // ๋ฒ ํ๋ฝ : ๋ค๋ฅธ ํธ๋์ญ์
์์ ์ฝ๊ธฐ๋ ์ฐ๊ธฐ๋ ๋ชปํจ
@Query("select s from Stock s where s.id = :id")
Stock findByIdWithPessimisticLock(Long id);
์ฟผ๋ฆฌ๋ฅผ ๋ณด๋ฉด SELECT ~ FOR UPDATE ์ฟผ๋ฆฌ๊ฐ ๋๊ฐ๋ ๊ฒ์ ํ์ธํ ์ ์์.
์ฆ “๋ฐ์ดํฐ๋ฅผ ์์ ํ๋ ค๊ณ ์ฐพ์ ๊ฒ์ด๋, ๋ค๋ฅธ ๋ถ๋ค์ ๊ฑด๋๋ฆฌ์ง ๋ง์ธ์!” ๋ผ๋ ๋ป
Optimistic Lock (๋๊ด์ ๋ฝ)
- ์ค์ ๋ก Lock์ ์ด์ฉํ์ง ์๊ณ ๋ฒ์ ์ ์ด์ฉํจ์ผ๋ก์ ์ ํฉ์ฑ์ ๋ง์ถ๋ ๋ฐฉ๋ฒ
- ๋ฐ์ดํฐ๋ฅผ ์ฝ์ ํ์ update๋ฅผ ์ํํ ๋ ํ์ฌ ๋ด๊ฐ ์ฝ์ ๋ฒ์ ์ด ๋ง๋์ง ํ์ธํ๋ฉฐ update๋ฅผ ์งํ
- ์ฆ, ์์์ Lock์ ๊ฑธ์ด์ ์ ์ ํ์ง ์๊ณ ๋์์ฑ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ฉด ๊ทธ๋๊ฐ์ ์ฒ๋ฆฌํ๋ ๋๊ด์ ๋ฝ ๋ฐฉ์.
- version์ ๋ณ๊ฒฝ์ด ๊ฐ์ง๋์ ๋ ์ฌ์์ฒญ์ ํ๋ ๋ก์ง์ ์ง์ ์์ฑํด์ค์ผ ํจ
- SpringDatJpa์์๋ respository์ @Lock ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํด ๊ตฌํํ ์ ์์
@Lock(LockModeType.OPTIMISTIC)
@Query("select s from Stock s where s.id = :id")
Stock findByIdWithOptimisticLock(Long id);
- ๋ํ @Version ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํด version ํ๋๋ฅผ Stock ๊ฐ์ฒด ํ๋์ ์ถ๊ฐํด์ค์ผ ํจ
@Version
private Long version;
Named Lock
- ๋ณ๋์ ๊ณต๊ฐ์ Lock์ ์์ฑํ๊ณ ๋ฐํํ์ฌ ๋์์ฑ ์ด์๋ฅผ ํด๊ฒฐํจ.
- Named Lock์ ํธ๋์ น์ ์ด ๋๋ ๋ ์๋์ผ๋ก ๋ฐํ๋์ง ์๊ธฐ ๋๋ฌธ์ ๋ฐํ ๋ก์ง์ ์ง์ ๊ตฌํํด์ค์ผ ํจ.
public interface LockRepository extends JpaRepository<Stock, Long>{
// 3000ms ์ด ๋์ lock ํ๋์ ์๋ํ๋ค.
@Query(value = "select get_lock(:key, 3000)", nativeQuery = true)
void getLock(String key);
@Query(value = "select release_lock(:key)", nativeQuery = true)
void releaseLock(String key);
}
mysql์์ get_lock(), release_lock()์ ํตํด lock์ ์ ์ดํ ์ ์๋ค.
- Pessimistic , Optimistic Lock์ item์ ๋ํด์ lock์ ๊ฑธ์๋ค๋ฉด named lock์ ๋ณ๋ MySQL ์๋ฒ ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ์ lock์ ๊ฑด๋ค.
Redis
๋ ๊ฐ์ง ๋ฐฉ์ (Lettuce, Redisson) ์ด ์์
Lettuce(Spring Data Redis๋ ๊ธฐ๋ณธ ํด๋ผ์ด์ธํธ๋ก Lettuce๋ฅผ ์ฌ์ฉ)
- Setnx ๋ช ๋ น์ ํ์ฉํ์ฌ ๋ถ์ฐ๋ฝ์ ๊ตฌํ (Set if not Exist - key:value๋ฅผ setํ ๋, ๊ธฐ์กด์ ๊ฐ์ด ์์ ๋๋ง setํ๋ ๋ช ๋ น์ด)
- Setnx ๋ Spin Lock ๋ฐฉ์์ด๋ฏ๋ก retry ๋ก์ง์ ๊ฐ๋ฐ์๊ฐ ์์ฑํด์ค์ผ ํจ.
- Spin Lock ์ด๋, Lock์ ํ๋ํ๋ ค๋ ์ค๋ ๋๊ฐ Lock์ ํ๋ํ ์ ์๋์ง ํ์ธํ๋ฉด์ ๋ฐ๋ณต์ ์ผ๋ก SETNX๋ฅผ ์๋ํ๋ ๋ฐฉ๋ฒ → redis์ ๋ง์ ๋ถํ ๋ฐ์ → ๋ฝ ํ๋ ์์ฒญ ์ฌ์ด ์ฌ์ด๋ง๋ค Thread.sleep์ ํตํด ๋ถํ๋ฅผ ์ค์ฌ์ค ์ ์์
- ์์ฒด์ ์ธ ํ์์์ ๊ตฌํ์ด ์กด์ฌํ์ง ์์ ์ดํ๋ฆฌ์ผ์ด์ ์ฝ๋ ์์์ ํ์์์์ ์ง์ ๊ตฌํํด์ผ ํจ
Redisson
- Publish-Subscribe ๊ธฐ๋ฐ์ผ๋ก Lock ๊ตฌํ ์ ๊ณต
- Pub-Sub ๋ฐฉ์์ด๋, ์ฑ๋์ ํ๋ ๋ง๋ค๊ณ ๋ฝ์ ์ ์ ์ค์ธ ์ค๋ฐ๋ฅด๊ฐ ๋ฝ์ ํด์ ํ์์ ๋๊ธฐ์ค์ธ ์ค๋ ๋์๊ฒ ์๋ ค์ฃผ๋ฉด ๋๊ธฐ์ค์ธ ์ค๋ ๋๊ฐ ๋ฝ ์ ์ ๋ฅผ ์๋ํ๋ ๋ฐฉ์
- Lettuce์ ๋ค๋ฅด๊ฒ ๋๋ถ๋ถ ๊ฐ๋ฐ์๊ฐ ๋ณ๋์ Retry ๋ฐฉ์์ ์์ฑํ์ง ์์๋ ๋จ.
'๐๋ฐฑ์๋ : BackEnd' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
@PathVariable, @RequestParam, @ModelAttribute (0) | 2024.06.08 |
---|---|
๋ก๊น (0) | 2024.06.08 |
์น์์ผ (0) | 2024.02.22 |
์คํ๋ง ์ํ๋ฆฌํฐ (0) | 2024.02.21 |
REST API (1) | 2024.01.12 |