๐ŸŒ Backend

์„ ์ฐฉ์ˆœ ์‹œ์Šคํ…œ์—์„œ ๋ฐœ์ƒํ•˜๋Š” Race Condition์„ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•

DevPoong 2025. 1. 6. 15:25

ํ•ด๋‹น ๊ธ€์„ ์ž‘์„ฑํ•˜๊ฒŒ ๋œ ๊ณ„๊ธฐ๋Š” ๋ฐœ๊ธ‰ํ•  ์ˆ˜ ์žˆ๋Š” ์ฟ ํฐ์˜ ๊ฐœ์ˆ˜๊ฐ€ ํ•œ์ •๋˜์–ด ์žˆ๊ณ , ํ˜„์žฌ๊นŒ์ง€ ๋ฐœ๊ธ‰๋œ ์ฟ ํฐ์˜ ์ˆ˜์— ๋”ฐ๋ผ ์„ ์ฐฉ์ˆœ์œผ๋กœ ์ฟ ํฐ์„ ๋ฐœ๊ธ‰ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ์–ด๋–ป๊ฒŒ ๋™์‹œ์„ฑ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์„๊นŒ์— ๋Œ€ํ•œ ํ˜ธ๊ธฐ์‹ฌ์ด์—ˆ๋‹ค.

์šฐ์„  ์ƒํ™ฉ์€ ์ด๋Ÿฌํ•˜๋‹ค.

  1. ์„œ๋ฒ„๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ๋กœ ์‹ฑ๊ธ€ ์Šค๋ ˆ๋“œ๋กœ ํ•ด๊ฒฐ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.
  2. ์„œ๋ฒ„๊ฐ€ 1๋Œ€์ธ ์ƒํ™ฉ์ด๋”๋ผ๋„, ์„ ์ฐฉ์ˆœ ์ฟ ํฐ ์ƒ์„ฑ ์ „์ฒด ๋กœ์ง์„ ์‹ฑ๊ธ€ ์Šค๋ ˆ๋“œ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์€ ๋งค์šฐ ๋น„ํšจ์œจ์ ์ด๋‹ค.
  3. Redisson์˜ RLock๊ณผ ๊ฐ™์€ ๋ฐฉ๋ฒ•์€ ํ˜„์žฌ ์ƒํ™ฉ์— ๋น„ํšจ์œจ์ ์ด๋‹ค.

 

๋จผ์ € 2๋ฒˆ, 3๋ฒˆ์ด ๋น„ํšจ์œจ์ ์ธ ์ด์œ ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค. (RLock์— ๋Œ€ํ•ด์„œ๋Š” ๋‹ค์Œ ํฌ์ŠคํŒ…์„ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”!)

 

 

์ฟ ํฐ์˜ ์žฌ๊ณ ๋ฅผ ํŒŒ์•…ํ•˜๊ณ  ์žฌ๊ณ ๊ฐ€ ๋‚จ์•„์žˆ์œผ๋ฉด ์‹ ๊ทœ ์ฟ ํฐ์„ ์ƒ์„ฑํ•œ๋‹ค๋ผ๋Š” ๊ด€์ ์—์„œ ๊ตณ์ด  ์žฌ๊ณ ์กฐํšŒ๋ถ€ํ„ฐ ์ €์žฅ๊นŒ์ง€์˜ ๋กœ์ง์„ Lock ๊ณผ์ •์„ ํ†ตํ•ด ์ฒ˜๋ฆฌํ•œ๋‹ค๋ฉด Lock์ด ๊ฑธ๋ฆฌ๋Š” ๊ตฌ๊ฐ„์ด ๊ธธ์–ด์ ธ ์„ฑ๋Šฅ์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.  

 

๋ฌผ๋ก , ์‹ค๋ฌด์—์„œ๋Š” ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ์ถ”๊ฐ€๋˜๋Š” ๋น„์ฆˆ๋‹ˆ์Šค๊ฐ€ ์žˆ๊ฒ ์ง€๋งŒ ์šฐ์„  ๊ฐ„๋‹จํ•˜๊ฒŒ ์ƒ๊ฐํ•ด ๋ณด๋ฉด
Redis์™€ ๊ฐ™์ด ์‹ฑ๊ธ€ ์Šค๋ ˆ๋“œ๋กœ ๋™์ž‘ํ•˜๋Š” ๊ณต์œ ์ž์›์—์„œ ์ฟ ํฐ์„ ๋ฐœ๊ธ‰ํ•˜๊ธฐ ์ „์— ํ˜„์žฌ๊นŒ์ง€ ๋งŒ๋“ค์–ด์ง„ ์ฟ ํฐ์˜ ๊ฐœ์ˆ˜๋ฅผ ์ฆ๊ฐ์‹œ์ผœ ์ค„ ์ˆ˜ ์žˆ๋‹ค. 

๊ทธ๋ฆฌ๊ณ  redis์—์„œ ๋ฆฌํ„ด๋˜๋Š” ๋ฐœ๊ธ‰๋œ ์ฟ ํฐ์˜ ๊ฐœ์ˆ˜๊ฐ€ ์ œํ•œ๋œ ์ˆซ์ž๋ณด๋‹ค ํฌ๋‹ค๋ฉด ๋ฐœ๊ธ‰์„ ํ•˜์ง€ ์•Š๋Š” ๋กœ์ง์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

redis-cli์—์„œ ํŠน์ • key์˜ value๋ฅผ ์ฆ๊ฐ์‹œํ‚ค๋Š” ๋ช…๋ น์–ด๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

incr key:value

 

์—ฌ๊ธฐ์„œ key๋Š” ํ˜„์žฌ๊นŒ์ง€ ๋ฐœ๊ธ‰๋œ ์ฟ ํฐ์˜ ๊ฐœ์ˆ˜์ด๊ณ , ์„œ๋ฒ„์˜ ์š”์ฒญ ๋•Œ๋งˆ๋‹ค value๋Š” 1์”ฉ ์ฆ๊ฐ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

 

 

Spring์—์„œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค๋ฉด Redis์— ์ €์žฅ๋œ ๋ฐœ๊ธ‰๋œ ์ฟ ํฐ์˜ ๊ฐœ์ˆ˜๋ฅผ ์ถ”์ ํ•˜๋Š” Repository๋ฅผ ์ƒ์„ฑํ•˜๊ณ 
์‹ค์ œ ์•„๋ž˜ ์ฝ”๋“œ๋กœ ์ฆ๊ฐ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

public Long increment() {
	return redisTemplate
    		.opsForValue()
           	 .increment("key_name")
}

 

 

์ถ”๊ฐ€์ ์œผ๋กœ, 1์ธ๋‹น 1๊ฐœ๋ผ๋Š” ์ œํ•œ์ด ์ƒ๊ธด๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ๋™์‹œ์„ฑ์„ ์ฒ˜๋ฆฌํ•ด์•ผ ํ• ์ง€ ์ƒ๊ฐํ•ด๋ด์•ผ ํ•œ๋‹ค.

 

 

์ƒ๊ฐํ•ด ๋ณผ ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์€ ๋‹จ์ˆœํ•˜๊ฒŒ ์ฟ ํฐ ๋ฐœ๊ธ‰ ๋กœ์ง์— Lock์„ ์ด์šฉํ•˜์—ฌ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๊ฒ ์ง€๋งŒ Kafka์™€ ๊ฐ™์€ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆฌ๋ฐ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•œ ๊ฒฝ์šฐ์—๋Š” ์‹ค์ œ Consumer๊ฐ€ ์ฟ ํฐ์„ DB์— insert ํ•˜๋Š” ์‹œ๊ฐ„๊นŒ์ง€ ํ…€์ด ๋ฐœ์ƒํ•˜๋ฏ€๋กœ ์ ์šฉํ•˜์ง€ ๋ชปํ•  ์ˆ˜ ์žˆ๋‹ค.

 

๋”ฐ๋ผ์„œ, ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์œผ๋กœ Redis์—์„œ Set ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ์ด์šฉํ•˜์—ฌ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.
๊ด€๋ จ redis-cli ๋ช…๋ น์–ด๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

sadd key value

ํ•ด๋‹น ๋ช…๋ น์–ด๋Š” ํ•ด๋‹น key์˜ Set์— value๊ฐ€ ์—†์œผ๋ฉด ์ถ”๊ฐ€๋œ value์˜ ๊ฐœ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ , ์ด๋ฏธ value๊ฐ€ ์กด์žฌํ•˜๋ฉด  0์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

 

Spring์—์„œ Set์„ ํ™œ์šฉํ•˜๊ธฐ ์œ„ํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด ๋ณธ๋‹ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

public Long addCouponSet(Long userId) {
	return redisTemplate
    		.opsForSet()
            	.add("key_name", userId.toString());
}

๋”ฐ๋ผ์„œ ์ฟ ํฐ ๋ฐœ๊ธ‰ ๋กœ์ง์—์„œ๋Š” redis์—์„œ ๋ฐ˜ํ™˜๋œ Long๊ฐ’์ด 1์ด ์•„๋‹Œ ๊ฒฝ์šฐ์—๋Š” ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋„๋ก ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.