Concurrency issues

Concurrency issues

์žฌ๊ณ ์‹œ์Šคํ…œ์œผ๋กœ ์•Œ์•„๋ณด๋Š” ๋™์‹œ์„ฑ์ด์Šˆ ํ•ด๊ฒฐ๋ฐฉ๋ฒ• ๊ฐ•์˜๋ฅผ ๋“ฃ๊ณ  ์š”์•ฝํ•œ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.

Race Condition

Race condition

๊ฒฝ์Ÿ์ƒํƒœ๋Š” ๋‘ ๊ฐœ ์ด์ƒ์˜ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ณต์œ  ๋ฐ์ดํ„ฐ์— ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ๊ณ , ๋™์‹œ์— ๋ณ€๊ฒฝ์„ ํ•˜๋ ค๊ณ  ํ•  ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค.

@Service
@RequiredArgsConstructor
public class StockService {

    private final StockRepository stockRepository;

    public synchronized void decrease(Long id, Long quantity) {
        final Stock stock = stockRepository.findById(id).orElseThrow();
        stock.decrease(quantity);

        stockRepository.saveAndFlush(stock);
    }
}

์šฐ๋ฆฌ๋Š” ์•„๋ž˜ ํ…Œ์ŠคํŠธ์—์„œ 0์˜ ๊ฒฐ๊ณผ๋ฅผ ์˜ˆ์ƒํ•˜์ง€๋งŒ, ๋™์‹œ์— ๋“ค์–ด์˜ค๋Š” ์š”์ฒญ๋“ค์ด ๊ฐฑ์‹  ์ „ ๊ฐ’์„ ์ฝ๊ณ  ์ˆ˜์ •ํ•˜๋ฉด์„œ ์‹ค์ œ ๊ฐฑ์‹ ์ด ๋ˆ„๋ฝ๋˜๋Š” ํ˜„์ƒ์ด ๋ฐœ์ƒํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

Thread-1
Stock
Thread-2

select

quantity:5

quantity:5

select

update

quantity:4

quantity:4

update

Java Synchronized

synchronized ๋ฅผ ๋ฉ”์„œ๋“œ ์„ ์–ธ๋ถ€์— ๋ถ™์—ฌ์ฃผ๋ฉด ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋Š” ํ•œ ๊ฐœ์˜ ์Šค๋ ˆ๋“œ๋งŒ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

.

โš ๏ธ ์—ฌ๊ธฐ์„œ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

Spring Transactional ์˜ ๋™์ž‘ ๋ฐฉ์‹์€ ์•„๋ž˜์™€ ๊ฐ™์ด ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ํด๋ž˜์Šค๋ฅผ ๋ž˜ํ•‘ํ•œ ํด๋ž˜์Šค๋ฅผ ์ƒˆ๋กœ ๋งŒ๋“ค์–ด์„œ ์‹คํ–‰ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ํด๋ž˜์Šค๋ฅผ ํ•„๋“œ๋กœ ๊ฐ€์ง€๊ณ  ํ˜ธ์ถœํ•˜๊ฒŒ ๋˜๋Š”๋ฐ ํŠธ๋žœ์žญ์…˜ ์ข…๋ฃŒ ์ „์— ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐฑ์‹ ๋œ ์ „ ๊ฐ’์„ ์ฝ๊ฒŒ ๋˜๋ฉด ๊ฒฐ๊ตญ ์ด์ „๊ณผ ๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

@Transactional ๊ณผ PROXY

์—ฌ๊ธฐ์„œ ๋‹จ์ˆœํ•˜๊ฒŒ Transactional Annotation ์„ ์ œ๊ฑฐํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜๋Š” ์žˆ์Šต๋‹ˆ๋‹ค.

.

โš ๏ธ ํ•˜์ง€๋งŒ, Java Synchronized ์˜ ๋ฌธ์ œ๋Š” ๋˜ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

Java Synchronized ๋Š” ํ•˜๋‚˜์˜ ํ”„๋กœ์„ธ์Šค ์•ˆ์—์„œ๋งŒ ๋ณด์žฅ์ด ๋ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‹ค๋ณด๋‹ˆ ์„œ๋ฒ„๊ฐ€ 1๋Œ€์ผ ๊ฒฝ์šฐ์—๋Š” ๊ดœ์ฐฎ๊ฒ ์ง€๋งŒ, ์„œ๋ฒ„๊ฐ€ 2๋Œ€ ์ด์ƒ์ผ ๊ฒฝ์šฐ ๊ฒฐ๊ตญ ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ์—์„œ ๋™์‹œ์— ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜๋ฉด์„œ ๋ ˆ์ด์Šค ์ปจ๋””์…˜์ด ๋ฐœ์ƒํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

Time
Thread-1
Stock
Thread-2

10:00

select

quantity:5

quantity:5

select

10:05

update

quantity:4

quantity:4

update

๋Œ€๋ถ€๋ถ„์˜ ์šด์˜ ์„œ๋น„์Šค๋Š” 2๋Œ€ ์ด์ƒ์˜ ์„œ๋ฒ„๋กœ ์šด์˜๋˜๊ธฐ ๋•Œ๋ฌธ์— Java Synchronized ๋Š” ๊ฑฐ์˜ ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

DataBase Lock

MySQL

Pessimistic Lock

๋น„๊ด€์  ๋ฝ.

  • ์‹ค์ œ๋กœ ๋ฐ์ดํ„ฐ์— Lock์„ ๊ฑธ์–ด์„œ ์ •ํ•ฉ์„ฑ์„ ๋งž์ถ”๋Š” ๋ฐฉ๋ฒ•

  • Row or Table ๋‹จ์œ„๋กœ Locking

  • Exclusive Lock์„ ๊ฑธ๊ฒŒ ๋˜๋ฉฐ, ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์—์„œ๋Š” Lock ํ•ด์ œ ์ „์— ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ๊ฐˆ ์ˆ˜ ์—†์Œ

  • ๋ฐ๋“œ๋ฝ(๋‘ ๊ฐœ ์ด์ƒ์˜ ์ž‘์—…์ด ์„œ๋กœ ์ƒ๋Œ€๋ฐฉ์˜ ์ž‘์—…์ด ๋๋‚˜๊ธฐ ๋งŒ์„ ๊ธฐ๋‹ค๋Š” ์ƒํƒœ) ์ฃผ์˜ ํ•„์š”

  • ์ถฉ๋Œ์ด ๋นˆ๋ฒˆํ•˜๊ฒŒ ์ผ์–ด๋‚˜๊ฑฐ๋‚˜ ์˜ˆ์ƒ๋œ๋‹ค๋ฉด ์ถ”์ฒœํ•˜๋Š” ๋ฐฉ์‹

์žฅ์ .

  • ์ถฉ๋Œ์ด ๋นˆ๋ฒˆํ•˜๊ฒŒ ์ผ์–ด๋‚œ๋‹ค๋ฉด Optimistic Lock ๋ณด๋‹ค ์„ฑ๋Šฅ์ด ์ข‹์„ ์ˆ˜ ์žˆ๋‹ค.

  • ๋ฝ์„ ํ†ตํ•ด ์—…๋ฐ์ดํ„ฐ๋ฅผ ์ œ์–ดํ•˜๋ฏ€๋กœ ๋ฐ์ดํ„ฐ ์ •ํ•ฉ์„ฑ์ด ๋ณด์žฅ๋œ๋‹ค.

๋‹จ์ .

  • ๋ณ„๋„์˜ ๋ฝ์„ ์žก๊ธฐ ๋•Œ๋ฌธ์— ์„ฑ๋Šฅ ๊ฐ์†Œ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ๋‹ค.

commit

Optimistic Lock

๋‚™๊ด€์  ๋ฝ.

  • ์‹ค์ œ๋กœ Lock ์„ ์ด์šฉํ•˜์ง€ ์•Š๊ณ  ๋ฒ„์ „์„ ์ด์šฉํ•จ์œผ๋กœ์จ ์ •ํ•ฉ์„ฑ์„ ๋งž์ถ”๋Š” ๋ฐฉ๋ฒ•

  • ๋จผ์ € ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์€ ํ›„ update ์ˆ˜ํ–‰ ์‹œ, ํ˜„์žฌ ๋‚ด๊ฐ€ ์ฝ์€ ๋ฒ„์ „์ด ๋งž๋Š”์ง€ ํ™•์ธํ•˜๋ฉฐ ์—…๋ฐ์ดํŠธ ์ˆ˜ํ–‰

  • ๋‚ด๊ฐ€ ์ฝ์€ ๋ฒ„์ „์—์„œ ์ˆ˜์ •์‚ฌํ•ญ์ด ์ƒ๊ฒผ์„ ๊ฒฝ์šฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋‹ค์‹œ ์ฝ์€ ํ›„์— ์ž‘์—…์„ ์ˆ˜ํ–‰

  • ์ถฉ๋Œ์ด ๋นˆ๋ฒˆํ•˜๊ฒŒ ์ผ์–ด๋‚˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ์ถ”์ฒœํ•˜๋Š” ๋ฐฉ์‹

์žฅ์ .

  • ๋ณ„๋„์˜ ๋ฝ์„ ์žก์ง€ ์•Š์œผ๋ฏ€๋กœ Pessimistic Lock ๋ณด๋‹ค ์„ฑ๋Šฅ์ƒ ์ด์ ์ด ์žˆ๋‹ค.

๋‹จ์ .

  • ์—…๋ฐ์ดํŠธ ์‹คํŒจ ์‹œ ์žฌ์‹œ๋„ ๋กœ์ง์„ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค.

commit

Named Lock

  • ์ด๋ฆ„์„ ๊ฐ€์ง„ Metadata Locking

  • ์ด๋ฆ„์„ ๊ฐ€์ง„ Lock ํš๋“ ํ›„, ํ•ด์ œํ•  ๋•Œ๊นŒ์ง€ ๋‹ค๋ฅธ ์„ธ์…˜์€ ์ด Lock ์„ ํš๋“ํ•  ์ˆ˜ ์—†๋„๋ก ํ•จ

  • Transaction ์ข…๋ฃŒ ์‹œ Lock ์ด ์ž๋™์œผ๋กœ ํ•ด์ œ๋˜์ง€ ์•Š๋Š” ์  ์ฃผ์˜

    • ๋ณ„๋„์˜ ๋ช…๋ น์–ด๋กœ ํ•ด์ œ๋ฅผ ์ˆ˜ํ–‰ํ•ด ์ฃผ๊ฑฐ๋‚˜ ์„ ์  ์‹œ๊ฐ„์ด ๋๋‚˜์•ผ ํ•ด์ œ

  • ๋น„๊ด€์  ๋ฝ๊ณผ ์œ ์‚ฌํ•˜์ง€๋งŒ ๋กœ์šฐ๋‚˜ ํ…Œ์ด๋ธ”์ด ์•„๋‹Œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์˜ ๋ฝํ‚น์„ ํ•˜๋Š” ๋ฐฉ์‹

  • ์ปค๋„ฅ์…˜ ํ’€ ๋ถ€์กฑ ํ˜„์ƒ์„ ๋ง‰๊ธฐ ์œ„ํ•ด ๋ฐ์ดํ„ฐ ์†Œ์Šค๋ฅผ ๋ถ„๋ฆฌํ•ด์„œ ์‚ฌ์šฉํ•  ๊ฒƒ์„ ๊ถŒ์žฅ

  • ์ฃผ๋กœ ๋ถ„์‚ฐ๋ฝ ๊ตฌํ˜„ ์‹œ ์‚ฌ์šฉ

์žฅ์ .

  • Timeout์„ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ ๊ฐ€๋Šฅ

๋‹จ์ .

  • ํŠธ๋žœ์žญ์…˜ ์ข…๋ฃŒ ์‹œ ๋ฝ ํ•ด์ œ, ์„ธ์…˜ ๊ด€๋ฆฌ ํ•„์š”

  • ๊ตฌํ˜„ ๋ฐฉ๋ฒ•์ด ๋ณต์žกํ•ด์งˆ ์ˆ˜ ์žˆ์Œ

commit

Reference.

Redis

MySQL์˜ Named Lock๊ณผ ์œ ์‚ฌํ•œ ๋ฐฉ์‹

Lettuce

  • setnx(set if not exist) ๋ช…๋ น์–ด๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋ถ„์‚ฐ๋ฝ ๊ตฌํ˜„

  • Spin Lock ๋ฐฉ์‹

    • ๋ฝ์„ ํš๋“ํ•˜๋ ค๋Š” ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฝ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ๋ฐ˜๋ณต์ ์œผ๋กœ ํ™•์ธํ•˜๋ฉด์„œ ๋ฝ ํš๋“์„ ์‹œ๋„ํ•˜๋Š” ๋ฐฉ์‹

    • ์žฌ์‹œ๋„ ๋กœ์ง ๊ฐœ๋ฐœ ํ•„์š”

์žฅ์ .

  • ๊ตฌํ˜„์ด ๋‹จ์ˆœ

  • ๋ณ„๋„์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ๋ถˆํ•„์š”(spring data redis ์‚ฌ์šฉ ์‹œ ๊ธฐ๋ณธ์ ์œผ๋กœ Lettuce ์ ์šฉ)

๋‹จ์ .

  • Spin Lock ๋ฐฉ์‹์ด๋ฏ€๋กœ ๋™์‹œ์— ๋งŽ์€ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฝ ํš๋“ ๋Œ€๊ธฐ ์ƒํƒœ๋ผ๋ฉด ๋ ˆ๋””์Šค์— ๋ถ€ํ•˜๊ฐ€ ๊ฐˆ ์ˆ˜ ์žˆ์Œ

    • ๋ฝ ํš๋“ ์žฌ์‹œ๋„ ๊ฐ„ ๋Œ€๊ธฐ ์‹œ๊ฐ„๋„ ํ•„์š”

commit

Spin Lock

Redisson

Redisson/Spring Boot Starter

  • pub-sub ๊ธฐ๋ฐ˜์œผ๋กœ Lock ๊ตฌํ˜„ ์ œ๊ณต

    • ์ฑ„๋„์„ ๋งŒ๋“ค๊ณ  ๋ฝ์„ ์ ์œ ์ค‘์ธ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฝ ํš๋“์„ ๋Œ€๊ธฐ์ค‘์ธ ์Šค๋ ˆ๋“œ์—๊ฒŒ ๋ฝ ํ•ด์ œ๋ผ๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ์ „์†กํ•˜๋ฉด, ๋ฉ”์‹œ์ง€๋ฅผ ์ „๋‹ฌ๋ฐ›์€ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฝ ํš๋“์„ ์‹œ๋„ํ•˜๋Š” ๋ฐฉ์‹

  • ๋ฝ ํš๋“์„ ์œ„ํ•ด ๋ฐ˜๋ณต์ ์œผ๋กœ ๋ฝ ํš๋“์„ ์‹œ๋„ํ•˜๋Š” Lettuce ๋Œ€๋น„ ๋ถ€ํ•˜๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ์Œ

์žฅ์ .

  • pub-sub ๊ธฐ๋ฐ˜ ๊ตฌํ˜„์œผ๋กœ lettuce ๋Œ€๋น„ ๋ ˆ๋””์Šค์— ๋ถ€ํ•˜๊ฐ€ ๋œ ๊ฐ

  • ๋ฝ ํš๋“ ์žฌ์‹œ๋„๋ฅผ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณต

๋‹จ์ .

  • ๋ณต์žกํ•œ ๊ตฌํ˜„

  • ๋ณ„๋„์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ•„์š”

์‹ค๋ฌด์—์„œ๋Š” ๋ณดํ†ต

์žฌ์‹œ๋„๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์€ ๋ฝ์€ Lettuce๋ฅผ ํ™œ์šฉํ•˜๊ณ ,

์žฌ์‹œ๋„๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ Redisson์„ ํ™œ์šฉ

commit

Finish

Java Synchronized

  • ํ•œ ๊ฐœ์˜ ์Šค๋ ˆ๋“œ๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๋„๋ก ์ œํ•œ

  • @Transactional ์ ์šฉ ๋ถˆ๊ฐ€

  • 2๋Œ€ ์ด์ƒ์˜ ์„œ๋ฒ„๋กœ ์šด์˜๋  ๊ฒฝ์šฐ ๋ ˆ์ด์Šค ์ปจ๋””์…˜์€ ๋˜ ๋‹ค์‹œ ๋ฐœ์ƒ

DataBase Lock

  • Pessimistic Lock

    • ๋กœ์šฐ, ํ…Œ์ด๋ธ” ๋‹จ์œ„๋กœ ๋ฝํ‚น

    • ๋ฝ ํ•ด์ œ ์ „๊นŒ์ง€ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ๊ฐˆ ์ˆ˜ ์—†์Œ(๋ฐ๋“œ๋ฝ ์ฃผ์˜)

    • ์ถฉ๋Œ์ด ๋นˆ๋ฒˆํ•˜๊ฒŒ ์ผ์–ด๋‚  ๊ฒฝ์šฐ ์ถ”์ฒœ

  • Optimistic Lock

    • ๋ฒ„์ „์„ ์ด์šฉ

    • ์—…๋ฐ์ดํŠธ ์‹คํŒจ ์‹œ ์žฌ์‹œ๋„

    • ์ถฉ๋Œ์ด ๋นˆ๋ฒˆํ•˜๊ฒŒ ์ผ์–ด๋‚˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ์ถ”์ฒœ

    • ์—…๋ฐ์ดํ„ฐ ์‹คํŒจ ์‹œ ์žฌ์‹œ๋„ ๋กœ์ง ๊ฐœ๋ฐœ ํ•„์š”

  • Named Lock

    • Pessimistic Lock ๊ณผ ์œ ์‚ฌํ•˜์ง€๋งŒ ์ด๋ฆ„๋“ค ๊ฐ€์ง„ ๋ฐ์ดํ„ฐ์— ๋ฝํ‚น

    • ํŠธ๋žœ์žญ์…˜ ์ข…๋ฃŒ ์‹œ ๋ฝ ํ•ด์ œ, ์„ธ์…˜์€ ์ง์ ‘ ๊ด€๋ฆฌ ํ•„์š”

    • ์ฃผ๋กœ ๋ถ„์‚ฐ๋ฝ ๊ตฌํ˜„ ์‹œ ์‚ฌ์šฉ

Redis

  • Lettuce

    • Spin Lock ๋ฐฉ์‹(๋ ˆ๋””์Šค์— ๋ถ€ํ•˜ ๊ฐ€๋Šฅ์„ฑ ์กด์žฌ)

    • ์žฌ์‹œ๋„ ๋กœ์ง ๊ฐœ๋ฐœ์ด ํ•„์š”

    • ์žฌ์‹œ๋„๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ๊ถŒ์žฅ

  • Redisson

    • pub-sub ๊ธฐ๋ฐ˜

    • ๋ณ„๋„์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๊ณ  ๊ตฌํ˜„์ด ๋ณต์žก

    • ์žฌ์‹œ๋„๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ๊ถŒ์žฅ

MySql vs. Redis

Mysql
Redis

Mysql์„ ์‚ฌ์šฉ์ค‘์ด๋ผ๋ฉด ๋ณ„๋„ ๋น„์šฉ์—†์ด ์‚ฌ์šฉ ๊ฐ€๋Šฅ

ํ™œ์šฉ์ค‘์ธ Redis๊ฐ€ ์—†๋‹ค๋ฉด ๋ณ„๋„์˜ ๊ตฌ์ถ• ๋น„์šฉ๊ณผ ์ธํ”„๋ผ ๊ด€๋ฆฌ ๋น„์šฉ์ด ๋ฐœ์ƒ

Redis ๋ณด๋‹ค ์„ฑ๋Šฅ์ด ์ข‹์ง€ ์•Š์Œ(์–ด๋А์ •๋„์˜ ํŠธ๋ž˜ํ”ฝ๊นŒ์ง€๋Š” ๋ฌธ์ œ์—†์ด ํ™œ์šฉ ๊ฐ€๋Šฅ)

Mysql ๋ณด๋‹ค ์„ฑ๋Šฅ์ด ์ข‹์Œ

Reference

Repository

Last updated