๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Spring & Spring Boot

[Redis] Redisson์„ ์ด์šฉํ•œ ๋ถ„์‚ฐ ๋ฝ ๊ตฌํ˜„์œผ๋กœ ๋™์‹œ์„ฑ ์ด์Šˆ ํ•ด๊ฒฐ

๐Ÿ“– ๋ชฉ์ฐจ

 

 

 


SpringBoot๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ด์ปค๋จธ์Šค ์„œ๋น„์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉด์„œ ๋™์‹œ์„ฑ ์ด์Šˆ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.

Redis์˜ Redisson๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ด์šฉํ•ด ๋™์‹œ์„ฑ ์ด์Šˆ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์ž.                                                        

 

 

 

 

 

 

๐Ÿ“Œ  ๋™์‹œ์„ฑ ์ œ์–ด๋ž€? 

  ๋™์‹œ์„ฑ ์ œ์–ด(Concurrency Control)๋ž€, ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๋‚˜ ํ”„๋กœ์„ธ์Šค๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณต์œ ํ•˜๋Š” ์ž‘์—…์ด ๋™์‹œ์— ์‹คํ–‰๋  ๋•Œ ๊ทธ ์‹คํ–‰ ์ˆœ์„œ๋‚˜ ์กฐ๊ฑด์„ ์ œ์–ดํ•˜์—ฌ ๋ฐ์ดํ„ฐ์˜ ๋ฌด๊ฒฐ์„ฑ์„ ์œ ์ง€ํ•˜๊ณ  ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋™์ž‘์„ ๋ฐฉ์ง€ํ•˜๋Š” ๊ฒƒ์„ ๋งํ•œ๋‹ค. ์—ฌ๋Ÿฌ ์ž‘์—… ๊ฐ„์˜ ์ƒํ˜ธ์ž‘์šฉ, ์ž์› ๊ณต์œ , ๊ฒฝ์Ÿ ์กฐ๊ฑด ๋“ฑ์„ ๊ด€๋ฆฌํ•˜์—ฌ ํ”„๋กœ๊ทธ๋žจ์˜ ์•ˆ์ •์„ฑ๊ณผ ์„ฑ๋Šฅ์„ ๋ณด์žฅํ•˜๋Š” ๋ฐ ์ค‘์š”ํ•œ ์—ญํ• ์„ ํ•œ๋‹ค.

 

 

 

 

 

๐Ÿ“Œ  ๋™์‹œ์„ฑ ์ œ์–ด์˜ ๋ชฉ์  

  • ์—ฌ๋Ÿฌ ์‚ฌ์šฉ์ž๊ฐ€ DB์— ์ ‘๊ทผํ•˜๋”๋ผ๋„ ๋ฐ์ดํ„ฐ์˜ ์ผ๊ด€์„ฑ์„ ๋ณด์žฅํ•˜๊ณ  ๋ฐ์ดํ„ฐ์˜ ๋ฌด๊ฒฐ์„ฑ์„ ์œ ์ง€
  • ์œ„๋ฅผ ๋งŒ์กฑํ•˜๋ฉฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‹œ์Šคํ…œ์˜ ์„ฑ๋Šฅ๊ณผ ํšจ์œจ์„ฑ์„ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ

 

 

 

 

๐Ÿ“Œ  ๋™์‹œ์„ฑ ์ œ์–ด ๊ธฐ๋ฒ•

  ๋™์‹œ์„ฑ ์ด์Šˆ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์„ ์ƒ๊ฐํ•ด ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ๋™์‹œ์„ฑ ์ œ์–ด ๊ธฐ๋ฒ•์—๋Š” ์—ฌ๋Ÿฌ ๊ฐ€์ง€๊ฐ€ ์žˆ๋Š”๋ฐ ๊ทธ์ค‘, Locking๊ธฐ๋ฒ•์„ ์ ์šฉํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค. Locking๊ธฐ๋ฒ•์€ ๋ฐ์ดํ„ฐ์— ์ž ๊ธˆ(Lock)์„ ์„ค์ •ํ•˜๋ฉด ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์€ ํ•ด๋‹น ๋ฐ์ดํ„ฐ์— ์ž ๊ธˆ์ด ํ•ด์ œ(UnLock)๋  ๋•Œ๊นŒ์ง€ ์ ‘๊ทผ, ์ˆ˜์ •, ์‚ญ์ œ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ด๋†“์€ ๋ฐฉ๋ฒ•์ด๋‹ค.

  • synchronized
    • ํ˜„์žฌ ์ ‘๊ทผํ•˜๊ณ  ์žˆ๋Š” ๋ฉ”์†Œ๋“œ์— ํ•˜๋‚˜์˜ ์Šค๋ ˆ๋“œ๋งŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ณด์žฅ ๋ฐ ๋™์ž‘ํ•œ๋‹ค. ์ž๋ฐ”์—์„œ ์ง€์›ํ•œ๋‹ค.
    • ์Šค๋ ˆ๋“œ ํ•˜๋‚˜์”ฉ ์ˆœ์ฐจ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค€๋‹ค.
    • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ํ•˜๋‚˜๋งŒ ๋„์šฐ๋Š” ๊ฒฝ์šฐ๋Š” ๋ฌด๊ด€ํ•˜์ง€๋งŒ, ์„œ๋ฒ„๊ฐ€ ์—ฌ๋Ÿฌ ๋Œ€์ผ ๊ฒฝ์šฐ์—๋Š” ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ์กด์žฌํ•˜๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์‹ค์งˆ์ ์ธ ์šด์˜ ํ™˜๊ฒฝ์—์„œ๋Š” ๋ฐ์ดํ„ฐ์˜ ์ •ํ•ฉ์„ฑ์„ ๋ณด์žฅํ•  ์ˆ˜ ์—†๋‹ค.

 

  • Pessimistic Lock(๋น„๊ด€์  ๋ฝ)
    • ๋ฐ์ดํ„ฐ์— Lock์„ ๊ฑธ์–ด์„œ ์ •ํ•ฉ์„ฑ์„ ๋งž์ถ”๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค. ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ธฐ ์ „์— ๋ฝ์„ ํš๋“ํ•œ๋‹ค.
    • ํ•œ ๋ฒˆ์—์—ฌ๋Ÿฌ ๊ฐœ์˜ ํ…Œ์ด๋ธ”์„ ์ˆ˜์ •ํ•˜๋ ค๊ณ  ํ–ˆ์„ ๋•Œ, ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ๋ฌถ์—ฌ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ˆ˜์ • ํ•˜๋‚˜๊ฐ€ ์‹คํŒจํ•˜๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋‹จ์—์„œ ์ „์ฒด ์ž๋™ ๋กค๋ฐฑ์ด ์ผ์–ด๋‚˜๊ฒŒ ๋œ๋‹ค.
    • ๋™์‹œ์— ์—ฌ๋Ÿฌ ์‚ฌ์šฉ์ž๊ฐ€ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•  ๋•Œ ์„ฑ๋Šฅ ๋ฌธ์ œ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.
    • ํŠนํžˆ ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค๋‚˜ ๋ถ„์‚ฐ ์‹œ์Šคํ…œ์—์„œ ์‚ฌ์šฉ๋  ๊ฒฝ์šฐ, ๋ฝ์ด ๋ฐœ์ƒํ•  ๋•Œ ๋‹ค๋ฅธ ์ž‘์—…์ด ๋Œ€๊ธฐํ•ด์•ผ ํ•˜๋ฏ€๋กœ ์ฒ˜๋ฆฌ๋Ÿ‰์ด ๊ฐ์†Œํ•  ์ˆ˜ ์žˆ๋‹ค.

 

  • Optimistic Lock(๋‚™๊ด€์  ๋ฝ) 
    • ๋น„๊ด€์  ๋ฝ๊ณผ ๋‹ค๋ฅด๊ฒŒ ํŠธ๋žœ์žญ์…˜์„ ์žก์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ถฉ๋Œ ๊ฐ์ง€๋ฅผ ํ•  ์ˆ˜ ์žˆ๋‹ค. ์„ฑ๋Šฅ์ ์œผ๋กœ๋Š” ๋น„๊ด€์  ๋ฝ๋ณด๋‹ค ์ข‹๋‹ค.
    • ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•˜์—ฌ ์ˆ˜์ •์„ ๋ชปํ•œ ๋ถ€๋ถ„์— ๋Œ€ํ•ด์„œ๋Š” ๋กค๋ฐฑ์— ๋Œ€ํ•œ ์ฑ…์ž„์„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‹จ์—์„œ ์ง€๋ฉฐ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ˆ˜๋™์œผ๋กœ ๋กค๋ฐฑ์„ ํ•ด์ค˜์•ผ ํ•œ๋‹ค.
    • ์ถฉ๋Œ์ด ๋งŽ์ด ์˜ˆ์ƒ๋˜๊ฑฐ๋‚˜ ์ถฉ๋Œ์ด ๋ฐœ์ƒํ–ˆ์„ ๋•Œ, ๋น„์šฉ์ด ๋งŽ์ด ๋“ค ๊ฒƒ์ด๋ผ๊ณ  ํŒ๋‹จ๋˜๋Š” ๊ณณ์—์„œ๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.
    • ์—”ํ‹ฐํ‹ฐ์— @Version์„ ์ถ”๊ฐ€ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค. ์กฐํšŒ์™€ ๊ฐฑ์‹  ์‹œ ๋ฒ„์ „ ๋น„๊ต๋ฅผ ํ†ตํ•ด์„œ ์ •ํ•ฉ์„ฑ์„ ๋งž์ถ˜๋‹ค.

 

    • Named Lock
      • ๋ง ๊ทธ๋Œ€๋กœ ์ด๋ฆ„์„ ๊ฐ€์ง„ Lock์ด๋‹ค. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๋ณ„๋„์˜ ์ €์žฅ์†Œ์— ์ด๋ฆ„์„ ๊ฐ€์ง„ ๋ฝ์„ ์ €์žฅํ•˜๊ณ , ํ•ด๋‹น ์ด๋ฆ„์— ๋Œ€ํ•œ ๋ฝ์„ ํ•ด์ œ๋˜๊ธฐ ์ „ ๊นŒ์ง€๋Š” ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์—์„œ ๋ฝ์„ ํš๋“ํ•  ์ˆ˜ ์—†๋‹ค.
      • ๋ณดํ†ต ๋ถ„์‚ฐ๋ฝ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋ฉฐ, MySQL์—์„œ๋งŒ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
      • ๋ฝ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋ณ„๋„์˜ ์ปค๋„ฅ์…˜ ํ’€์„ ๊ด€๋ฆฌํ•ด์•ผ ํ•˜๊ณ , ๋ฝ์— ๊ด€๋ จ๋œ ๋ถ€ํ•˜๋ฅผ RDS์—์„œ ๋ฐ›๋Š”๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค.

 

  • Lettuce
    • redis๋ฅผ dependency์— ์ถ”๊ฐ€ํ•˜๋ฉด ๋ณ„๋„์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜ ์—†์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ์Šคํ•€ ๋ฝ ๋ฐฉ์‹์œผ๋กœ ๋™์‹œ์— ๋งŽ์€ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฝ ํš๋“์„ ๋Œ€๊ธฐ ์ค‘์ด๋ผ๋ฉด, redis ์„œ๋ฒ„์— ๋ถ€ํ•˜๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.
    • redis์— ๊ฐ’์ด ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด ์„ธํŒ…ํ•˜๊ฒŒ ํ•˜๊ณ , ๊ฐ’์ด ์„ธํŒ…๋˜์—ˆ๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ๋ฆฌํ„ด ๊ฐ’์œผ๋กœ ๋ฐ›์•„ ๋ฝ์„ ํš๋“ํ•˜๋Š” ๋ฐ ์„ฑ๊ณตํ•œ๋‹ค. ๋ฝ์„ ์ ์œ ํ•˜๋Š” ์‹œ๊ฐ„์ด ์งง์œผ๋ฉด ์œ ๋ฆฌํ•˜์ง€๋งŒ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฝ์„ ์˜ค๋ž˜ ์ ์œ ํ•˜๊ณ  ์žˆ๋Š” ๊ฒฝ์šฐ CPU์— ๋ถ€๋‹ด์„ ์ค„ ์ˆ˜ ์žˆ๋‹ค.
    • ๋ฝ์˜ ๋งŒ๋ฃŒ ์‹œ๊ฐ„์„ ์ง€์ •ํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋ฝ ํš๋“์— ๋Œ€ํ•œ ์žฌ์‹œ๋„๊ฐ€ ํ•„์š” ์—†๋Š” ๊ฒฝ์šฐ์— ์‚ฌ์šฉํ•˜๋ฉด ์ข‹๋‹ค.

 

  • Redisson
    • ๋ณ„๋„์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
    • Pub/Sub๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์—ฌ ์Šคํ•€ ๋ฝ์ด redis์— ์ฃผ๋Š” ์—„์ฒญ๋‚œ ํŠธ๋ž˜ํ”ฝ์„ ์ค„์˜€๋‹ค.
    • ๋ฝ์˜ ๋งŒ๋ฃŒ ์‹œ๊ฐ„์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์–ด, ๋ฝ ํš๋“์„ ์žฌ์‹œ๋„ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์— ์‚ฌ์šฉํ•˜๋ฉด ์ข‹๋‹ค.

 

 

 

 

 

โœ”๏ธ ์œ„์˜ ๋ฐฉ๋ฒ•๋“ค ์ค‘, ๋‚˜๋Š” Redisson ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋™์‹œ์„ฑ ์ œ์–ด๋ฅผ ๊ตฌํ˜„ํ–ˆ๋‹ค.

  ํŒ€ ํ”„๋กœ์ ํŠธ๋กœ ์ด์ปค๋จธ์Šค ์„œ๋น„์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ฒŒ ๋˜์—ˆ๋Š”๋ฐ, ์ด์ปค๋จธ์Šค์˜ ํŠน์„ฑ์ƒ ์—ฌ๋Ÿฌ ์‚ฌ์šฉ์ž๊ฐ€ ๋™์‹œ์— ํ•œ ๊ฐ€์ง€ ์ƒํ’ˆ์„ ๊ตฌ๋งคํ•˜๊ธฐ ์œ„ํ•œ ์š”์ฒญ์ด ๋งŽ์„ ๊ฒƒ์ด๊ณ , ์—ฌ๋Ÿฌ ์‚ฌ์šฉ์ž๊ฐ€ ๋™์‹œ์— ํ•œ ๊ฐ€์ง€์˜ ์ƒํ’ˆ์— ๋Œ€ํ•ด ์ฃผ๋ฌธ์„ ์ƒ์„ฑ์„ ์š”์ฒญํ•˜๋ฉด ์ž˜๋ชป๋œ ํŠธ๋žœ์žญ์…˜์ด ์ƒ์„ฑ๋  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํŒ๋‹จํ•˜์˜€๋‹ค. ์ด๋Ÿฌํ•œ ๋™์‹œ์„ฑ ์ด์Šˆ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ฐฉ๋ฒ•์„ ์ฐพ์•„๋ณด๋˜ ์ค‘, ๋งˆ์นจ ์ธํ”„๋ผ์— redis๊ฐ€ ๊ตฌ์ถ•๋˜์–ด ์žˆ์—ˆ๊ณ  redis์˜ ์—ฌ๋Ÿฌ ๊ธฐ๋Šฅ ์ค‘์—  ๋ถ„์‚ฐ ๋ฝ์„ ์ ์šฉํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค.

 

 

 

 

 

 

๐Ÿ“Œ  ๋™์‹œ์„ฑ ์ œ์–ด ์ ์šฉ ์ „

  • ํ…Œ์ŠคํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค: ์ดˆ๋‹น 100๋ฒˆ์˜ ์š”์ฒญ์„ ํ–ˆ์„ ๋•Œ, ์ƒํ’ˆ์˜ ์žฌ๊ณ ๋Š” 100๊ฐœ -> 0๊ฐœ, ์ฃผ๋ฌธ์€ 100๊ฐœ ์ƒ์„ฑ๋˜์–ด์•ผ ํ•œ๋‹ค.
  • ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ: 3์ดˆ ๋™์•ˆ ์ด 300๋ฒˆ์˜ ์š”์ฒญ์„ ํ•œ ๊ฒฐ๊ณผ, ์ƒํ’ˆ์˜ ์žฌ๊ณ ๋Š” 100๊ฐœ-> 0๊ฐœ๋กœ ์ค„์—ˆ๊ณ , ์ฃผ๋ฌธ์€ 107๊ฐœ๊ฐ€ ์ƒ์„ฑ๋˜์—ˆ๋‹ค.

<7๋ฒˆ ์ƒํ’ˆ์˜ ์žฌ๊ณ ๋ฅผ 100๊ฐœ๋กœ ์„ค์ •ํ•œ๋‹ค.>

 

<์žฌ๊ณ ๋Š” 0๊ฐœ๊ฐ€ ๋˜์—ˆ์ง€๋งŒ, ์ฃผ๋ฌธ์€ 107๊ฐœ๊ฐ€ ์ƒ์„ฑ๋˜์—ˆ๋‹ค.>

 

 

 

  • ํ…Œ์ŠคํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค: ์ดˆ๋‹น 100๋ฒˆ์˜ ์š”์ฒญ์„ ํ–ˆ์„ ๋•Œ, ์ƒํ’ˆ์˜ ์žฌ๊ณ ๋Š” 1000๊ฐœ -> 900๊ฐœ, ์ฃผ๋ฌธ์€ 100๊ฐœ ์ƒ์„ฑ๋˜์–ด์•ผ ํ•œ๋‹ค.
  • ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ: 3์ดˆ ๋™์•ˆ ์ด 300๋ฒˆ์˜ ์š”์ฒญ์„ ํ•œ ๊ฒฐ๊ณผ, ์ƒํ’ˆ์˜ ์žฌ๊ณ ๋Š” 1000๊ฐœ-> 727๊ฐœ๋กœ ์ค„์—ˆ๊ณ , ์ฃผ๋ฌธ์€ 281๊ฐœ๊ฐ€ ์ƒ์„ฑ๋˜์—ˆ๋‹ค.

<7๋ฒˆ ์ƒํ’ˆ์˜ ์žฌ๊ณ ๋ฅผ 1000๊ฐœ๋กœ ์„ค์ •ํ•œ๋‹ค.>

 

<์žฌ๊ณ ๋Š” 727๊ฐœ๊ฐ€ ๋˜์—ˆ์ง€๋งŒ, ์ฃผ๋ฌธ์€ 281๊ฐœ๊ฐ€ ์ƒ์„ฑ๋˜์—ˆ๋‹ค.>

 

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

 

 

 

 

 

 

๐Ÿ“Œ  Redisson์„ ์ด์šฉํ•œ ๋ถ„์‚ฐ ๋ฝ ๊ตฌํ˜„ํ•˜๊ธฐ 

 

  Spring์—์„œ Redisson์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด build.gradle์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

dependencies {
  // redisson
  implementation 'org.redisson:redisson-spring-boot-starter:3.23.2'
}

 

 

 

  RedissonClient๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด Redis Config๋ฅผ ์„ค์ •ํ•ด ์ค€๋‹ค.

@Configuration
@EnableRedisRepositories
public class RedisConfig {

    @Value("${spring.data.redis.host}")
    private String redisHost;

    @Value("${spring.data.redis.port}")
    private int redisPort;

    private static final String REDISSON_HOST_PREFIX = "redis://";

    @Bean
    public RedissonClient redissonClient() {
        RedissonClient redisson = null;
        Config config = new Config();
        config.useSingleServer().setAddress(REDISSON_HOST_PREFIX + redisHost + ":" + redisPort);
        redisson = Redisson.create(config);

        return redisson;
    }

    @Bean
    public RedissonConnectionFactory redisConnectionFactory(RedissonClient redissonClient) {
        return new RedissonConnectionFactory(redissonClient);
    }
}

 

 

 

 

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DistributedLock {

    String key(); // ๋ฝ์˜ ์ด๋ฆ„

    TimeUnit timeUnit() default TimeUnit.SECONDS; // ๋ฝ์˜ ์‹œ๊ฐ„ ๋‹จ์œ„

    long waitTime() default 5L; // ๋ฝ์„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ์‹œ๊ฐ„

    long leaseTime() default 3L; // ๋ฝ ์ž„๋Œ€ ์‹œ๊ฐ„
}

 

 

 

  Redisson์„ ์ด์šฉํ•œ ๋กœ์ง์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. ์–ด๋…ธํ…Œ์ด์…˜ ์„ ์–ธ ์‹œ ์ˆ˜ํ–‰๋˜๋Š” AOPํด๋ž˜์Šค์ด๋‹ค.

@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class DistributedLockAop {

    private static final String REDISSON_LOCK_PREFIX = "LOCK: ";
    private static final long RETRY_DELAY = 3L; // ์žฌ์‹œ๋„ ์‚ฌ์ด์˜ ๋Œ€๊ธฐ ์‹œ๊ฐ„
    private static final long MAX_RETRY_COUNT = 5L; // ์ตœ๋Œ€ ์žฌ์‹œ๋„ ํšŸ์ˆ˜

    private final RedissonClient redissonClient;
    private final AopForTransaction aopForTransaction;

    @Around("...")
    public Object lock(final ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        DistributedLock distributedLock = method.getAnnotation(DistributedLock.class);

        String key = REDISSON_LOCK_PREFIX + CustomSpringELParser.getDynamicValue(
            signature.getParameterNames(),
            joinPoint.getArgs(),
            distributedLock.key());

        RLock rLock = redissonClient.getLock(key);

        // ๋ฝ ํš๋“ ์‹œ๋„ ๋ฐ ์žฌ์‹œ๋„ ๋กœ์ง
        int retryCount = 0;
      
        while (retryCount < MAX_RETRY_COUNT) {
            try { // ๋ฝ์„ ํš๋“ํ•˜๋ ค๊ณ  ์‹œ๋„ํ•˜๊ณ , ์ •์˜๋œ ์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉด ์ž ๊ธˆ์„ ํ•ด์ œํ•œ๋‹ค.
                boolean available = rLock.tryLock(distributedLock.waitTime(),
                    distributedLock.leaseTime(), distributedLock.timeUnit()); 

                if (available) {
                    System.out.println("Lock acquired with key: " + key);
                    try {
                        return aopForTransaction.proceed(joinPoint);
                    } finally {
                        if (rLock.isLocked() && rLock.isHeldByCurrentThread()) {
                        // isLocked(): ํŠน์ • ๋ฝ์ด ํ˜„์žฌ Redis ์„œ๋ฒ„์— ๋ฝ ๋˜์–ด ์žˆ๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ํ™•์ธ
                        // isHeldByCurrentThread(): ํŠน์ • ๋ฝ์„ ํ˜„์žฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ณด์œ ํ•˜๊ณ  ์žˆ๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ํ™•์ธ
                            System.out.println("Lock released with key: " + key);
                            rLock.unlock();
                        }
                    }
                } ... {
                    // ๋ฝ ํš๋“ ์‹คํŒจ ์‹œ ๋Œ€๊ธฐํ•˜๊ณ  ์žฌ์‹œ๋„ํ•˜๋Š” ๋กœ์ง
                }
            } catch (InterruptedException e) {
                log.error("DistributedLock lock interrupted");
                System.out.println("DistributedLock lock interrupted");
                throw new InterruptedException(e.getMessage());
            }
        }
        throw new IllegalStateException("DistributedLock lock failed after retries");
    }
}

 

 

 

  ๋™์‹œ์„ฑ ํ™˜๊ฒฝ์—์„œ์˜ ๋ฐ์ดํ„ฐ ์ •ํ•ฉ์„ฑ์„ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•ด Propagation.REQUIRES_NEW ์˜ต์…˜์„ ์ง€์ •ํ–ˆ๊ณ , ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹ ์ดํ›„์— ๋ฝ์ด ํ•ด์ œ๋˜๊ฒŒ ์ฒ˜๋ฆฌํ–ˆ๋‹ค.

@Component
public class AopForTransaction {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public Object proceed(final ProceedingJoinPoint joinPoint) throws Throwable {
        return joinPoint.proceed();
    }
}

 

 

 

  ๋ถ„์‚ฐ ๋ฝ์„ ๊ตฌํ˜„ํ•œ ์–ด๋…ธํ…Œ์ด์…˜์„ ์ ์šฉํ•˜์—ฌ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•ด ๋ณด์ž.

@Service
@RequiredArgsConstructor
@Component
public class OrderService {
...

    @DistributedLock(key = "#orderProducts.![productId].toString()")
    public OrderInfo createOrder(
        Long memberId,
        Long receiverId,
        Long totalAmount,
        PayType type,
        List<OrderProduct> orderProducts
    ) {
	...
        OrderEntity saved = orderJpaRepository.save(orderEntity);

        historyService.createHistory(saved.getId(), orderProducts);

        return ...
    }

 

 

 

 

 

 

 

๐Ÿ“Œ  ๋™์‹œ์„ฑ ์ œ์–ด ์ ์šฉ ํ›„

  • ํ…Œ์ŠคํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค: ์ดˆ๋‹น 100๋ฒˆ์˜ ์š”์ฒญ์„ ํ–ˆ์„ ๋•Œ, ์ƒํ’ˆ์˜ ์žฌ๊ณ ๋Š” 100๊ฐœ -> 0๊ฐœ, ์ฃผ๋ฌธ์€ 100๊ฐœ ์ƒ์„ฑ๋˜์–ด์•ผ ํ•œ๋‹ค.
  • ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ: 3์ดˆ ๋™์•ˆ ์ด 300๋ฒˆ์˜ ์š”์ฒญ์„ ํ•œ ๊ฒฐ๊ณผ, ์ƒํ’ˆ์˜ ์žฌ๊ณ ๋Š” 100๊ฐœ-> 0๊ฐœ๋กœ ์ค„์—ˆ๊ณ , ์ฃผ๋ฌธ์€ 100๊ฐœ๊ฐ€ ์ƒ์„ฑ๋˜์—ˆ๋‹ค.

<๋™์ผํ•˜๊ฒŒ 7๋ฒˆ ์ƒํ’ˆ์˜ ์žฌ๊ณ ๋ฅผ 100๊ฐœ๋กœ ์„ค์ •ํ•œ๋‹ค.>

 

<์žฌ๊ณ ๋Š” 0๊ฐœ๋กœ ๊ฐ์†Œํ•˜์˜€๊ณ , ๊ทธ์— ๋”ฐ๋ผ ์ฃผ๋ฌธ์€ 100๊ฐœ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์ƒ์„ฑ๋˜์—ˆ๋‹ค.>

 

 

 

  • ํ…Œ์ŠคํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค: ์ดˆ๋‹น 100๋ฒˆ์˜ ์š”์ฒญ์„ ํ–ˆ์„ ๋•Œ, ์žฌ๊ณ  1000๊ฐœ -> 900๊ฐœ, ์ฃผ๋ฌธ์€ 100๊ฐœ ์ƒ์„ฑ๋˜์–ด์•ผ ํ•œ๋‹ค.
  • ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ: 3์ดˆ ๋™์•ˆ ์ด 300๋ฒˆ์˜ ์š”์ฒญ์„ ํ•œ ๊ฒฐ๊ณผ, ์žฌ๊ณ ๋Š” 1000๊ฐœ -> 700๊ฐœ, ์ฃผ๋ฌธ์€ 300๊ฐœ๊ฐ€ ์ƒ์„ฑ๋˜์—ˆ๋‹ค.

<๋™์ผํ•˜๊ฒŒ 7๋ฒˆ ์ƒํ’ˆ์˜ ์žฌ๊ณ ๋ฅผ 1000๊ฐœ๋กœ ๋งž์ถฐ์ค€๋‹ค.>

 

<์žฌ๊ณ ๋Š” 700๊ฐœ๋กœ ๊ฐ์†Œํ•˜์˜€๊ณ , ๊ทธ์— ๋”ฐ๋ผ ์ฃผ๋ฌธ์€ 300๊ฐœ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์ƒ์„ฑ๋˜์—ˆ๋‹ค.>

 

 

 

์—ฌ๊ธฐ๊นŒ์ง€ ๋™์‹œ์„ฑ ์ด์Šˆ๊ฐ€ ์ผ์–ด๋‚ฌ์„ ๋•Œ Redisson์„ ์‚ฌ์šฉํ•œ ๋ถ„์‚ฐ ๋ฝ์˜ ๊ตฌํ˜„์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์•˜๋‹ค.

 

 

 

 

 

๐Ÿ“Œ  ์ฐธ๊ณ 

 

ํ’€ํ•„๋จผํŠธ ์ž…๊ณ  ์„œ๋น„์ŠคํŒ€์—์„œ ๋ถ„์‚ฐ๋ฝ์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ• - Spring Redisson

์–ด๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ถ„์‚ฐ๋ฝ์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค.

helloworld.kurly.com

 

 

 

๋ ˆ๋””์Šค์™€ ๋ถ„์‚ฐ ๋ฝ(1/2) - ๋ ˆ๋””์Šค๋ฅผ ํ™œ์šฉํ•œ ๋ถ„์‚ฐ ๋ฝ๊ณผ ์•ˆ์ „ํ•˜๊ณ  ๋น ๋ฅธ ๋ฝ์˜ ๊ตฌํ˜„

๋ ˆ๋””์Šค๋ฅผ ํ™œ์šฉํ•œ ๋ถ„์‚ฐ ๋ฝ์— ๋Œ€ํ•ด ์•Œ์•„๋ด…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์„ฑ๋Šฅ์„ ๋†’์ด๊ณ  ์ผ๊ด€์„ฑ์„ ๋ณด์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ด…๋‹ˆ๋‹ค.

hyperconnect.github.io

 

 

[Redis] ๋ถ„์‚ฐ ๋ฝ (feat. Redisson)

๋ถ„์‚ฐ ๋ฝ์ด๋ž€? ๋ถ„์‚ฐ ๋ฝ(Distributed Lock)์€ ๋‹ค์ˆ˜์˜ ์„œ๋ฒ„(๋˜๋Š” ํ”„๋กœ์„ธ์Šค)๊ฐ€ ๋™์‹œ์— ๊ฐ™์€ ์ž์›์— ์ ‘๊ทผํ•  ๋•Œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋Š” ๋™๊ธฐํ™” ๋ฉ”์ปค๋‹ˆ์ฆ˜์ž…๋‹ˆ๋‹ค. ๋ถ„์‚ฐ ๋ฝ์˜ ํ•ต

inma.tistory.com

 

 

8. Distributed locks and synchronizers

Redisson - Valkey and Redis Java client. Complete Real-Time Data Platform. Sync/Async/RxJava/Reactive API. Over 50 Valkey and Redis based Java objects and services: Set, Multimap, SortedSet, Map, L...

github.com