๐ง ๋ค์ด๊ฐ๊ธฐ์

Redis์ ๋ํ ๊ฐ๋
์ ๋์ง์ด๋ณด๋ค๊ฐ, ๋ถ์ฐ๋ฝ์ ๋ค์ ํ ๋ฒ ์ดํด๋ณด๊ฒ ๋์๊ณ ๋ฌธ๋ ๋ค์๊ณผ ๊ฐ์ ๊ถ๊ธ์ฆ์ด ์๊ธฐ๊ฒ ๋์๋ค.
"๋ถ์ฐ๋ฝ์ ์ด๋ค ๊ตฌ์กฐ๋ก ์ด๋ฃจ์ด์ ธ์์ง? ์ฐ๋ฆฌ๊ฐ ํํ๊ฒ ์ฐ๋ Redisson์ ๋ด๋ถ๊ตฌ์กฐ๋ ์ด๋ป๊ฒ ์๊ฒผ์ง?"
"๋ถ์ฐ๋ฝ์ ๋น๊ด๋ฝ๊ณผ ์ด๋ค ์ฐจ์ด์ ์ ์ง๋๊ณ ์๊ณ ์ด๋ค ์ฅ๋จ์ ์ ์ทจํ๊ณ ์์ง? ์ฃผ์ํด์ผํ ๋ถ๋ถ์ด ๋ญ๊น?"
"๋ถ์ฐ๋ฝ์ ๋ด๋ถ์ ์ผ๋ก ์คํ๋ฝ ํํ๋ฅผ ์ทจํ๊ณ ์๋๊ฐ? ์๋๋ฉด ๋ฎคํ
์ค์ ํํ๋ฅผ ์ทจํ๊ณ ์๋๊ฐ? ์๋๋ฉด ์ธ๋งํฌ์ด?"
์ด ๊ณ ๋ฏผ๋ค์ ํด๊ฒฐํ๊ธฐ ์ํด ํด๋น ํฌ์คํ
์์๋ ๋ ๋์ค ๋ถ์ฐ๋ฝ์ ๊น๊ฒ ๋ค๋ค๋ณผ ์์ ์ด๋ค.
๐ธ 1. Redis ๋ถ์ฐ๋ฝ์ ๊ธฐ๋ณธ ๊ตฌ์กฐ
๋ถ์ฐ ์์คํ ์์๋ ํ๋์ ์์์ ์ฌ๋ฌ ์๋ฒ ๋๋ ํ๋ก์ธ์ค๊ฐ ๋์์ ์ ๊ทผํ๋ ค๊ณ ํ ๋ ๊ฒฝ์ ์กฐ๊ฑด(race condition) ์ด ๋ฐ์ํ ์ ์๋ค. ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ํ์ํ ๊ฒ์ด ๋ฐ๋ก ๋ถ์ฐ๋ฝ์ด๋ค.
1๏ธโฃ ๋ถ์ฐ๋ฝ vs ๋น๊ด๋ฝ?
์ฌ๊ธฐ์ ํ ๊ฐ์ง ์๋ฌธ์ด ์๊ธด๋ค.
"๋น๊ด๋ฝ์ ๋ถ์ฐ ์์คํ ์์ ์ฌ์ฉํ ์ ์๋ ๊ฒ์ธ๊ฐ?"
๋น๊ด๋ฝ์ ์ฃผ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ค์์ ์ ์ฉ๋๋ ํ
์ด๋ธ ๋จ์์ ๋ฝ์ด๋ค. ๋จ์ผ DB ์ธ์คํด์ค์์๋ผ๋ฉด ํธ๋์ญ์
๊ธฐ๋ฐ์ ๋ฝ์ ํตํด ์ ํฉ์ฑ์ ๋ณด์ฅํ ์ ์๋ค. ํ์ง๋ง ๋ถ์ฐ ์์คํ
์ ๊ฒฝ์ฐ์๋ ์ด์ผ๊ธฐ๊ฐ ๋ฌ๋ผ์ง๋ค. ์๋์ ๊ทธ๋ฆผ์ ํตํด ์์ธํ ์ดํด๋ณด์.
๐น1. ๋จ์ผ DB ์ธ์คํด์ค

๋ง์ฝ ์์ ๊ฐ์ด ์ฐ๊ธฐ DB ๋์์ด ํ๋์ธ ๊ฒฝ์ฐ์๋ ํ
์ด๋ธ ๋จ์์ ๋ฝ์ธ ๋น๊ด๋ฝ์ ๊ฑธ์ด๋ ๋ฐ์ดํฐ์ ์ ํฉ์ฑ์ ์งํฌ ์ ์๋ค.
๐น2. ๋ถ์ฐ ํ๊ฒฝ

ํ์ง๋ง ๋ถ์ฐ ํ๊ฒฝ์์๋ DB ์ธ์คํด์ค๊ฐ ์ฌ๋ฌ ๊ฐ๋ก ๋๋๊ฒ ๋๋ฉฐ, ์ด ๊ฒฝ์ฐ ๋น๊ด๋ฝ๋ง์ผ๋ก๋ ๋ฐ์ดํฐ ์ ํฉ์ฑ์ ์งํค๊ธฐ ์ด๋ ค์์ง๋ค.ํ์ง๋ง DB์ ๋ ํ๋ฆฌ์นด๊ฐ ๋ง์์ง๋ ๊ฒฝ์ฐ, ์ฌ์ฉ์ ์์ฒญ์ด ๋ฝ์ด ๊ฑธ๋ฆฌ์ง ์์ ๋ค๋ฅธ ๋
ธ๋๋ก ์ ๋ฌ๋ ์ ์๋ ์ํฉ์ด ๋ฐ์ํ๊ฒ ๋๋ค.
์ด๋ก ์ธํด ํน์ ๋
ธ๋์์๋ ๋ฝ์ด ๊ฑธ๋ ค์์ง๋ง, ๋ค๋ฅธ ๋
ธ๋์์๋ ๋ฝ์ด ์ ์ฉ๋์ง ์์ ์ํ๋ก ์ฐ๊ธฐ ์์
์ด ์ด๋ฃจ์ด์ง๋ ๋ฑ ๋ฐ์ดํฐ ๋ถ์ผ์น ํ์์ด ๋ฐ์ํ ์ ์๋ค.

๊ฒฐ๊ตญ, ๋ถ์ฐ ํ๊ฒฝ์์ ๋น๊ด๋ฝ์ ํ๊ณ๊ฐ ์์ผ๋ฉฐ, ์ค์์์ ๋ฝ ์ํ๋ฅผ ๊ด๋ฆฌํ๊ณ ์ผ๊ด๋ ์ ์ด๊ฐ ๊ฐ๋ฅํ ๋ถ์ฐ๋ฝ ๋ฐฉ์์ด ๋ณด๋ค ์ ์ ํ ์ ํ์ด ๋๋ ๊ฒ์ด๋ค.
๐ก๋ถ์ฐ๋ฝ์ ๋ค์ํ ๊ฒฝ์ฐ์ ์ฌ์ฉ์ด ๊ฐ๋ฅํ๋ค!

๋ถ์ฐ๋ฝ์ ๋ฐ๋์ ์ฐ๊ธฐ DB๊ฐ ์ฌ๋ฌ ๊ฐ ์๋ ๊ฒฝ์ฐ์๋ง ํ์ํ ๊ฒ์ ์๋๋ค.
์์ ์ดํด๋ณธ ๊ฒ์ฒ๋ผ, ๋ถ์ฐ๋ฝ์ ์ค์์ Redis๊ฐ ๋ฝ ํ๋ ์ฌ๋ถ๋ฅผ ๊ด๋ฆฌํ๋ฉฐ ๋์ํ๊ณ DB ์์ค์์ ์คํ๋๋ ๋ฝ์ด ์๋๊ธฐ ๋๋ฌธ์ ๊ผญ ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ์๋๋๋ผ๋ ํ์ผ, ๋ฉ์์ง ํ, ์บ์ ๋ฑ ๋ค์ํ ์์์ ์ ์ฉํ ์ ์๋ค. ์ฆ, ์์คํ
๋ด์์ ์์์ ๊ณต์ ํ๊ฑฐ๋ ์ ๊ทผ ๊ฒฝ์์ด ๋ฐ์ํ๋ ๊ฑฐ์ ๋ชจ๋ ์ํฉ์์ ๋ถ์ฐ๋ฝ์ ์ ํจํ๊ฒ ์ฌ์ฉ๋ ์ ์๋ค.
๋ํ ๋ถ์ฐ๋ฝ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋๋ฌํ๊ธฐ ์ด์ , ์ฆ ๋น์ฆ๋์ค ๋ก์ง ๋จ์์ ์ ์ ์ ์ผ๋ก ๋ฝ์ ์ ์ดํ ์ ์๋ค๋ ์ ์์ ์ ์ฐ์ฑ๊ณผ ํจ์จ์ฑ ์ธก๋ฉด์์๋ ๋งค์ฐ ๊ฐ๋ ฅํ ๋๊ตฌ๊ฐ ๋๋ค.
์ด๋ฌํ ํน์ฑ ๋๋ถ์, ๋ณต์กํ ๋์์ฑ ์ ์ด๋ ์ค๋ณต ์์ฒญ ๋ฐฉ์ง ๋ฑ ๋ค์ํ ์ค๋ฌด ์๋๋ฆฌ์ค์์ ๋๋ฆฌ ํ์ฉ๋๋ค.
2๏ธโฃ Redis Lock ๋ด๋ถ ๋ช ๋ น์ด
SET mylock unique_id NX PX 3000Redis์์ ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ๋ถ์ฐ๋ฝ์ SET ๋ช
๋ น ํ๋๋ก ๊ตฌํ๋๋ค.
- NX : ํค๊ฐ ์กด์ฌํ์ง ์์ ๋๋ง ์ค์ (๋๊ตฐ๊ฐ๊ฐ ํค๋ฅผ ๊ฐ์ง๊ณ ์๋ค๋ฉด ์คํจํ๋ค.)
- PX 3000 : ๋ฝ์ ๋ง๋ฃ์๊ฐ์ ๋ฐ๋ฆฌ์ด ๋จ์๋ก ์ค์ (์ฌ๊ธฐ์ 3์ด)
์ฌ๊ธฐ์์ ํต์ฌ์ NX์ด๋ค.

NX๋ผ๋ ์ต์
์ ์ค์ผ๋ก์จ ๋๊ตฐ๊ฐ๊ฐ mylock์ด๋ผ๋ ์ด๋ฆ์ผ๋ก ํค๋ฅผ ๊ฐ์ง๊ณ ์์ผ๋ฉด ํค๋ฅผ ์์ฑํ ์ ์๊ณ , ๋๊ตฐ๊ฐ๊ฐ ํค๋ฅผ ๊ฐ์ง์ง ์์๋ค๋ฉด mylock์ด๋ผ๋ ์ด๋ฆ์ผ๋ก ํค๋ฅผ ์์ฑํ๋ ๊ตฌ์กฐ์ด๋ค.
๊ทธ๋ ๋ค๋ฉด ์ด์ ๊ฒฝ์ ์์์์ ์ํ๋ ํด๋น ์์
์ ์น๋ฅด๊ณ ๋ ํ์๋ ๋ฝ์ ํ์ด์ผํ๋ค.
DEL mylock์ด๋๋ DEL ๋ช
๋ น์ด๋ฅผ ํตํด์ ํค๋ฅผ ์ญ์ ํด์ฃผ๋ฉด ๋๋ค.
ํ์ง๋ง ์ด๋ ๊ฒ ๋๋ฉด ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.

"์๋ฌด๋ ํค๋ฅผ ์ ๊ฑฐํ ์ ์๋๊ฑฐ ์๋๊ฐ? ํค๋ฅผ ํ๋ํ ์ฃผ์ฒด๋ง ์ ๊ฑฐํ๋๋ก ๊ฐ์ ํด์ผํ๋ ๊ฒ ์๋๊ฐ?"
๋ฐ๋ผ์ ๋ ๊ฐ์ง์ ์กฐ๊ฑด์ ๋ง์กฑํด์ผ ํค๋ฅผ ์ ๊ฑฐํ ์ ์๋๋ก ์ค์ ํด์ผํ๋ค.
- ํค๊ฐ ์กด์ฌํด์ผํ๋ค.
- ๊ฐ์ด ์ผ์นํด์ผํ๋ค. (์ด๋ ๊ฐ์ UUID์ ๊ฐ์ ๊ณ ์ ๋๋ค๊ฐ์ผ๋ก ์ค์ ํ์ฌ ํค๋ฅผ ์ค์ ํ ํด๋ผ์ด์ธํธ๋ง ํด๋น ๊ฐ์ ์ ์ ์๋๋ก ํด์ผํ๋ค.)
์์ ๊ฐ์ ์กฐ๊ฑด์ ์ ์ฉํ๊ธฐ ์ํด์๋ ์๋์ ๊ฐ์ Lua ์คํฌ๋ฆฝํธ๋ฅผ ํตํด ์ญ์ ํ ๊ฒ์ ๊ถ๊ณ ํ๋ค.
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end3๏ธโฃ Redis์ ๊ตฌ์กฐ์ ํ๊ณ
๐จRedis๊ฐ ๋จ์ผ ๋ฆฌ์์ค๋ผ๋ฉด?
๋ง์ฝ Redis๊ฐ ๋จ์ผ ๋ฆฌ์์ค๋ผ๋ฉด ์์ ๊ฐ์ด Lua ์คํฌ๋ฆฝํธ๋ฅผ ํตํด ๋ฝ์ ํด์ ํ๋ ๋ฐฉ์์ ๋จ์ผ ์ฅ์ ์ง์ ์ด ๋ ์ ์๋ค. ๋ง์ฝ ํ๋์ ๋ ๋์ค ์์์ ์์กดํ์ฌ ๋ฝ์ ๊ฑธ๊ณ ๊ณต์ ์์์ ์ฐ๊ธฐ ์์
์ ์คํํ๋ค๊ฐ ํด๋น ๋ ๋์ค๊ฐ ์ฅ์ ๊ฐ ์ผ์ด๋ ๊ฒฝ์ฐ์ ๋ํด์๋ ๋ฝ์ ๋ํ ์ฃผ๋๊ถ์ ์์ด๋ฒ๋ฆด ๋ฟ๋ง ์๋๋ผ ๊ณต์ ์์์ ๋ํ ๋ฝ์ ๊ฑธ ์ ์๋ ์ํฉ์ด ๋๋ค.
๐ง๊ทธ๋ฌ๋ฉด Redis๋ฅผ ์ฌ๋ฌ ๊ฐ ๋๋ฉด ๋๊ฒ ๋ค~
๋ฐ๋ผ์ Master-slave๋ฐฉ์์ ๋ณต์ ๊ตฌ์กฐ๋ฅผ ์๊ฐํด๋ณผ ์ ์๋ค. Redis๋ ์๋์ผ๋ก fail over๋ฅผ ์ ๊ณตํ๊ธฐ์ master๊ฐ ์ฃฝ์ ๊ฒฝ์ฐ slave๊ฐ ์๋์ผ๋ก master๋ก ์น๊ฒฉํ๊ฒ ๋๋ฉฐ ์์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์์ ๊ฒ์ฒ๋ผ ๋ณด์ธ๋ค.

ํ์ง๋ง ๋ณต์ ๊ตฌ์กฐ๊ฐ ๋น๋๊ธฐ์ด๊ธฐ ๋๋ฌธ์ ์์ ๊ฐ์ ์ํฉ์ด ๋ฐ์ํ ์ ์๋ค.
- Client A๊ฐ Master์๊ฒ์ mylock์ ํ๋ํ์๋ค.
- ํค์ ๋ํ ์ฐ๊ธฐ๊ฐ ๋ณต์ ๋ณธ์ผ๋ก ์ ์ก๋๊ธฐ ์ Master๊ฐ ๋ค์ด๋๋ค.
- ํด๋น ํค๋ฅผ ๋ฐ์ง ๋ชปํ Slave๊ฐ Master๋ก ์น๊ฒฉํ๋ค.
- Client B๊ฐ Master์๊ฒ mylock key๋ฅผ ์์ฒญํ๋ฉด ํ๋ํ ์ ์๋ค.
์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด RedLock์ด ๋ฑ์ฅํ๊ฒ ๋๋ค.
๐ธ2. RedLock์ ๊ตฌ์กฐ
์์ ๊ฐ์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ ๋์ค์์๋ RedLock ์๊ณ ๋ฆฌ์ฆ์ ์ ์ํ์๋ค. RedLock์ N๋์ Redis ์๋ฒ๊ฐ ์๋ค๊ณ ๊ฐ์ ํ ๋, ๊ณผ๋ฐ ์ ์ด์์ ๋ ธ๋์์ Lock์ ํ๋ํ๋ค๋ฉด Lock์ ํ๋ํ ๊ฒ์ผ๋ก ๊ฐ์ฃผํ๋ค. ๊ทธ๋ฆฌ๊ณ ์ด๋ฐ์์ consensus๋ฅผ ๊ฑฐ์น๋ ค๋ฉด ๊ธฐ๋ณธ์ ์ผ๋ก 2n+1๊ฐ์ ๋ ธ๋๋ฅผ ๊ฐ์ง๊ณ ์์ด์ผํ๋ค.

์๋ฐ ์ง์์์๋ ์ด๋ฌํ RedLock์ด Redisson์ ํตํด ๊ตฌํ์ด ๋์ด์๋ค.
1๏ธโฃ RedLock์ ๋์ ์๋ฆฌ
๐น1. ํ์ฌ ์๊ฐ์ ๋ฐ๋ฆฌ์ด ๋จ์๋ก ์ธก์ ํ๋ค.
๋ฝ ํ๋ ์๋๋ฅผ ์์ํ๊ธฐ ์ ์ System.currentTimeMillis()์ ๊ฐ์ ๋ฐฉ์์ผ๋ก ํ์ฌ ์๊ฐ์ ๋ฐ๋ฆฌ์ด(ms) ๋จ์๋ก ์ธก์ ํ๋ค. ์ด ๊ฐ์ ๋ฝ์ ์ป๊ธฐ ์ํด ์์๋ ์๊ฐ์ ๊ณ์ฐํ๋ ๋ฐ ์ฌ์ฉ๋๋ค.
๐น2. N๋์ Redis ์๋ฒ์ ์์ฐจ์ ์ผ๋ก ๋ฝ์ ์์ฒญํ๋ค.

RedLock์ N๋์ Redis ๋
ธ๋๊ฐ ์กด์ฌํ๋ค๊ณ ๊ฐ์ ํ๋ค. ์๋ฅผ ๋ค์ด 5๋์ ์๋ฒ๊ฐ ์๋ค๊ณ ํ์.
ํด๋ผ์ด์ธํธ๋ ๊ฐ Redis ์๋ฒ์ SET mylock my_unique_value NX PX 10000 ์ ๊ฐ์ ํ์์ผ๋ก ๋ฝ ์์ฒญ์ ๋ณด๋ธ๋ค.
์ด๋ ์ค์ํ ์ ์ ๊ฐ ์์ฒญ์ ๋ํด ์งง์ timeout์ ์ค์ ํด์ผ ํ๋ค๋ ๊ฒ์ด๋ค.
์๋ฅผ ๋ค์ด, ์ ์ฒด ๋ฝ ์ ํจ์๊ฐ์ด 10์ด๋ผ๋ฉด ๊ฐ Redis ์๋ฒ์ ๋ํ timeout์ 5~50ms ์์ค์ผ๋ก ์๊ฒ ์ค์ ํ๋ค.
์ด๋ ๊ฒ ์ค์ ํ๋ ์ด์ ๋, ํน์ Redis ์๋ฒ๊ฐ ์ฅ์ ์ํ์ผ ๊ฒฝ์ฐ ํด๋น ์๋ฒ์์ ํต์ ์ผ๋ก ๊ณผ๋ํ ์๊ฐ์ด ์ง์ฒด๋๋ ๊ฒ์ ๋ฐฉ์งํ๊ธฐ ์ํจ์ด๋ค.
๐น3. ๊ณผ๋ฐ์ ์ด์์ ์๋ฒ๋ก๋ถํฐ ๋ฝ์ ํ๋ํ๋ค.

ํด๋ผ์ด์ธํธ๋ Redis ์๋ฒ๋ค๋ก๋ถํฐ ์๋ต์ ๋ฐ์ ํ, ๋ฝ์ ์ฑ๊ณต์ ์ผ๋ก ํ๋ํ ์๋ฒ์ ๊ฐ์๋ฅผ ์ง๊ณํ๋ค. ์๋ฅผ ๋ค์ด ์ด 5๋์ Redis ์๋ฒ ์ค 3๋ ์ด์(๊ณผ๋ฐ์)์์ ๋ฝ์ ํ๋ํ ๊ฒฝ์ฐ, ์ด๋ ๋ฝ ํ๋์ ์ฑ๊ณตํ ๊ฒ์ผ๋ก ๊ฐ์ฃผํ๋ค.
๋จ, ์ฌ๊ธฐ์๋ ํ ๊ฐ์ง ์ถ๊ฐ ์กฐ๊ฑด์ด ์๋ค.
๋ฝ ํ๋์ ์์๋ ์๊ฐ์ด ์ ํจ์๊ฐ๋ณด๋ค ์์์ผ ํ๋ค.
๋ฝ ์์ฒญ์ ์์ํ ์์ ๊ณผ ๋ชจ๋ ์๋ต์ ๋ฐ์ ์์ ์ฌ์ด์ ์ด ์์ ์๊ฐ(T_elapsed)์ ์ธก์ ํ๊ณ , ์ด ๊ฐ์ด ๋ฝ์ ์ ํจ์๊ฐ(TTL)๋ณด๋ค ์์์ผ ํ๋ค.
์ฆ, ๋ค์ ์กฐ๊ฑด์ด ๋์์ ๋ง์กฑํด์ผ ํ๋ค:
- ๊ณผ๋ฐ์์ Redis ๋ ธ๋์์ ๋ฝ์ ํ๋
- ๋ฝ์ ํ๋ํ๋ ๋ฐ ๊ฑธ๋ฆฐ ์๊ฐ์ด TTL๋ณด๋ค ์งง์
TTL์ด 10์ด์ธ๋ฐ ๋ฝ์ ํ๋ํ๋ ๋ฐ 11์ด๊ฐ ๊ฑธ๋ ธ๋ค๋ฉด, ์ด๋ฏธ ๋ฝ์ด ๋ง๋ฃ๋ ๊ฐ๋ฅ์ฑ์ด ์์ด ๋ฝ ํ๋์ ์คํจํ ๊ฒ์ผ๋ก ๊ฐ์ฃผํ๋ค.
๐น4. ๋ฝ์ ์ค์ ์ ํจ์๊ฐ์ ๋จ์ ์๊ฐ์ผ๋ก ๊ณ์ฐํ๋ค.
์ ํจ์๊ฐ = TTL - T_elapsed๋ฝ์ ์ฑ๊ณต์ ์ผ๋ก ํ๋ํ๋ค๋ฉด, ์ค์ ๋ก ์ฌ์ฉ ๊ฐ๋ฅํ ์๊ฐ์ ์์ ๊ฐ๋ค.
์ฆ, TTL์ด 10์ด์ด๊ณ ๋ฝ์ ์ป๋ ๋ฐ 3์ด๊ฐ ๊ฑธ๋ ธ๋ค๋ฉด, ๋ฝ์ ๋จ์ 7์ด ๋์๋ง ์ ํจํ๋ค. ๋ฐ๋ผ์ ๋ฝ์ ์ฌ์ฉํ๋ ์์
์ ํด๋น ์๊ฐ ์์ ์๋ฃ๋์ด์ผ ํ๋ค.
๐น5. ๋ฝ ํ๋์ ์คํจํ๋ค๋ฉด, ๋ชจ๋ ์๋ฒ์ ๋ฝ ํด์ ๋ฅผ ์์ฒญํ๋ค.

๊ณผ๋ฐ์ ๋ฝ ํ๋์ ์คํจํ๊ฑฐ๋ TTL์ ์ด๊ณผํ ๊ฒฝ์ฐ, ๋ฝ ํ๋์ ์คํจํ ๊ฒ์ผ๋ก ๊ฐ์ฃผํ๋ค. ์ด๋, ํด๋ผ์ด์ธํธ๋ ๋ฝ ํ๋์ ์ฑ๊ณตํ Redis ์๋ฒ๋ค์ ๋ํด ๋ฝ ํด์ ์์ฒญ์ ์ ์กํ๋ค.
- ์ด๋ ์ผ๊ด์ฑ์ ๋ณด์ฅํ๊ณ ๋ฝ ์๋ฅ๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํ ์กฐ์น์ด๋ค.
- ๋ฝ ํด์ ์์๋ ํด๋ผ์ด์ธํธ๊ฐ ์ค์ ํ ๊ณ ์ ์๋ณ์(UUID)๋ฅผ ์ฌ์ฉํ์ฌ, ์์ ์ด ์ค์ ํ ๋ฝ๋ง ํด์ ํ๋๋ก ํด์ผ ํ๋ค.
2๏ธโฃ ๊ณผ๋ฐ์ ์ด์์ ๋ฝ์ ํ๋ํ์ ๋ ๋ฝ์ ํ๋ํ์ง ๋ชปํ ๋ ธ๋๋?

์์ ๊ฐ์ ํ๋ฆ์ ๋ฐ๋ฅด๋ฉด Client 1์ด 3๊ฐ์ ๋
ธ๋๋ก๋ถํฐ ๋ฝ์ ํ๋ํ๊ฒ ๋๋ฉด ๊ณผ๋ฐ์ ์ด์์ ๋ฝ์ ํ๋ํ ๊ฒ์ด๋ฏ๋ก, ๋ฝ์ ํ๋ํ ๊ฒ์ผ๋ก ๊ฐ์ฃผ๋๋ค. ํ์ง๋ง ๊ทธ๋ ๊ฒ ๋๋ ๊ฒฝ์ฐ๋ Node 2์ Node 4์ ๋ํด์๋ ๋ฝ์ ํ๋ํ ์ ์๋ ์ํ์ด๋ค.
๊ทธ๋ฌ๋ฉด ์ด ์คํจํ 2๊ฐ์ ๋
ธ๋์ ๋ํด์๋ ์ด๋ป๊ฒ ์ฒ๋ฆฌ๋ ๊น?
์คํจํ ๋
ธ๋๋ ์๋ฌด ์ผ๋ ํ์ง ์๋๋ค.
๋ฝ์ด ์ค์ ๋์ง ์์ ์ฑ ๋จ์์์ด๋ ์ถํ์ ๋ค์ด์ค๋ ๋ฝ ์์ฒญ์ ๋ํด์ ๊ฐ์ ๋ฐฉ์์ผ๋ก ๋์ํ๊ธฐ์ ์ ๋๋ก ๊ณผ๋ฐ์๋ฅผ ๋์ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.

์์ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด Client 1์ด ๋ฝ์ ์ ์ ํ๊ณ ์๋ ๋์ Client 2๊ฐ ๋ฝ์ ์์ฒญํ๋ค๊ณ ๊ฐ์ ํด๋ณด์.
๊ทธ๋ ๋ค๋ฉด ์ด๋ฏธ ์ ์ ๋์ด์๋ Node 1, Node 2, Node 3์ ๋ํด์๋ ๋ฝ์ ํ๋ํ์ง ๋ชปํ๊ณ Node 2์ Node 4์ ๋ํด์๋ง ๋ฝ์ ํ๋ํ ์ ์๋ค. ํ์ง๋ง ๋ ๊ฐ์ ๋
ธ๋๋ ๊ณผ๋ฐ์๊ฐ ์๋๋ฏ๋ก ๋ฝ์ ํ๋ํ์ง ์๋ ๊ฒ์ผ๋ก ๊ฐ์ฃผ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋ฝ์ ํ๋ํ์ง ๋ชปํจ์ ์ ๋ฌ๋ฐ์ Client๋ ์ ์ฐจ์ ๋ฐ๋ผ Lua Script๋ก ๋ฝ์ ํด์ ํ๋ ์ ์ฐจ๋ฅผ ๋ฐ๋๋ค.
์ด์ ๊ฐ์ ์ด์ ๋ก ๋ฝ์ ๋ณด์ฅ๋ฐ์ ์ ์๋ ๊ฒ์ด๋ค.
๐ธ3. RedLock ํ๊ณ
RedLodck ์๊ณ ๋ฆฌ์ฆ์ ์ค๋ฌด์์ ์์ ํ ๋ถ์ฐ ํฉ์ ํ๋กํ ์ฝ์ด ์๋๋ฉฐ, ๋ช ๊ฐ์ง ๊ตฌ์กฐ์ ํ๊ณ์ ์ํ์ฑ์ ์๊ณ ์๋ค.
RedLock์ ๊ตฌ์กฐ์ ํ๊ณ๋ฅผ CAP ์ด๋ก ๊ณผ ์ฐ๊ด์ง์ด์ ๋ค๋ค๋ณด๊ณ ์ํ๋ค.
1๏ธโฃ ๋จผ์ , CAP ์ด๋ก ์ด๋?
CAP ์ด๋ก ์ ๋ถ์ฐ ์์คํ ์์ ์ธ ๊ฐ์ง ํน์ฑ ์ค ๋์์ ๋ ๊ฐ์ง๋ง ๋ง์กฑํ ์ ์๋ค๋ ์ด๋ก ์ด๋ค.
| C (Consistency) | ๋ชจ๋ ๋ ธ๋๊ฐ ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ๊ณ ์๋ค๋ ์ผ๊ด์ฑ |
| A (Availability) | ๋ชจ๋ ์์ฒญ์ ๋ํด ์๋ต์ ๋ฐํํ ์ ์๋ ๊ฐ์ฉ์ฑ |
| P (Partition tolerance) | ๋คํธ์ํฌ ๋ถํ ์ํฉ์์๋ ์์คํ ์ด ๊ณ์ ๋์ํ ์ ์์ |
๋ถ์ฐ ํ๊ฒฝ์์๋ P๋ ๊ธฐ๋ณธ ์ ์ ๋ก ๋ฐ์๋ค์ฌ์ผ ํ๋ฏ๋ก, ์ค์ ์ ํ์ง๋ C์ A ์ค ํ๋๋ฅผ ์ ํํ๊ฒ ๋๋ค.
RedLock์ ์ด๋ค ์์คํ ์ ๊ฐ๊น์ด๊ฐ?
- P (Partition Tolerance) : ํ์ฉ
โ ๋คํธ์ํฌ ๋ถํ ์ด ๋ฐ์ํด๋ ๋์ ๊ฐ๋ฅํด์ผ ํ๋ค. - A (Availability) : ์ฐ์ ์
โ ๋ฝ ์์ฒญ์ ๋น ๋ฅด๊ฒ ์ฒ๋ฆฌํ๊ธฐ ์ํด ๊ณผ๋ฐ์ ์๋ต๋ง์ผ๋ก ์ฑ๊ณต ํ๋จ - C (Consistency) : ๋ถ๋ถ์ ์ผ๋ก๋ง ๋ณด์ฅ
โ ์ด๋ก ์ TTL๊ณผ UUID ๋ฑ์ผ๋ก ์ต์ํ์ ์ผ๊ด์ฑ ์ ์งํ์ง๋ง, ๊ฐํ ์ผ๊ด์ฑ์ ๋ณด์ฅํ์ง ๋ชปํจ
์ฆ RedLock์ CAP ์ด๋ก ์ AP ์์คํ
์ ๊ฐ๊น๋ค.
ํ์ง๋ง ์ฌ๊ธฐ์ ๋ฌธ์ ๊ฐ ์๊ธฐ๋ ์ด์ ๊ฐ ๋ฐ๋ก ๋ถ์ฐ๋ฝ์ ๋ณธ์ง์ ์ผ๋ก ๋ฐ์ดํฐ ๋ณดํธ, ๋์์ฑ ์ ์ด, ๋ฌด๊ฒฐ์ฑ ๋ณด์ฅ์ ์ํ ์๋จ์ด๋ผ๋ ์ ์ด๋ค.
๋ฐ๋ผ์ ๋ฝ ์์คํ
์ ์ผ๋ฐ์ ์ธ ๋ฐ์ดํฐ ์ ์ฅ์๋ณด๋ค ์ผ๊ด์ฑ(C)์ ํจ์ฌ ๋ ์ค์ํด์ผ ํ๋๋ฐ ์ฌ๊ธฐ์ ๋ชจ์์ด ๋ฐ์ํ๋ ๊ฒ์ด๋ค.
2๏ธโฃ RedLock์ ์ฃผ์ ํ๊ณ
๐น1. ๊ฐํ ์ผ๊ด์ฑ(C)๋ฅผ ๋ณด์ฅํ์ง ๋ชปํ๋ค
๋ ๋์ค๊ฐ ๊ฐํ ์ผ๊ด์ฑ์ ๋ณด์ฅ๋ฐ์ง ๋ชปํ๋ ๋ค์๊ณผ ๊ฐ์ ๋ ๊ฐ์ง case๊ฐ ์๋ค.
- Clock Drift๋ก ์ธํ ์ผ๊ด์ฑ ๋ถ๊ดด
- ์ ํ๋ฆฌ์ผ์ด์ ์ค๋จ/GC๋ก ์ธํ ๋ฌธ์
[Clock Drift๋ก ์ธํ ์ผ๊ด์ฑ ๋ถ๊ดด]
RedLock์ ๋
ธ๋ ๊ฐ์ ์๊ณ๊ฐ ์์ ํ ๋๊ธฐํ๋์ด ์๋ค๋ ๊ฐ์ ์์ด, ๊ฐ ๋
ธ๋์ ๋ก์ปฌ ์๊ฐ์ ๊ธฐ์ค์ผ๋ก TTL์ ๊ณ์ฐํ๋ค. ํ์ง๋ง ์ค์ ํ๊ฒฝ์์๋ ์๊ณ๊ฐ ๋์ผํ ์๋๋ก ํ๋ฅด์ง ์์ผ๋ฉฐ, ์ด๋ฅผ Clock Drift ๋ผ๊ณ ๋ถ๋ฅธ๋ค.
- ํด๋ผ์ด์ธํธ 1์ด Redis ๋ ธ๋ A, B, C์์ ๋ฝ์ ํ๋ํ๋ค.
- ๋คํธ์ํฌ ๋ฌธ์ ๋ก D, E์์๋ ๋ฝ์ ํ๋ํ์ง ๋ชปํ๋ค.
- ์ด๋ ๋ ธ๋ C์ ์๊ณ๊ฐ ๋น์ ์์ ์ผ๋ก ๋นจ๋ผ์ง๋ค โ TTL์ด ๋นจ๋ฆฌ ๋๋ฌ๋ค๊ณ ์๋ชป ํ๋จ
- ํด๋ผ์ด์ธํธ 2๊ฐ ๋ค์ด์ด ๋ ธ๋ C, D, E์์ ๋ฝ์ ํ๋ํ๋ค. (C๋ TTL ๋ง๋ฃ๋ก ์ธํด ๋ฝ์ด ์๋ค๊ณ ํ๋จ)
- A์ B์์๋ ์ฌ์ ํ ํด๋ผ์ด์ธํธ 1์ ๋ฝ์ด ์ด์์๋ค.
- ๊ฒฐ๊ณผ์ ์ผ๋ก ํด๋ผ์ด์ธํธ 1๊ณผ 2๊ฐ ๋์์ ๋ฝ์ ์์ ํ๊ณ ์๋ค๊ณ ์ฐฉ๊ฐํ๊ฒ ๋๋ค.
- ๋ ํด๋ผ์ด์ธํธ๊ฐ ๋์ผ ์์์ ๋์ ์์ ์ ์ํํ๋ฉด์ ์ผ๊ด์ฑ ๋ถ๊ดด
[์ ํ๋ฆฌ์ผ์ด์ ์ค๋จ/GC๋ก ์ธํ ๋ฌธ์ ]
RedLock์ ๋ฝ์ ์ก๊ณ ์ผ์ ์๊ฐ ์์ ์์
์ด ์๋ฃ๋ ๊ฒ์ด๋ผ ๊ฐ์ ํ๋ค.
ํ์ง๋ง ํ์ค์์๋ ํด๋ผ์ด์ธํธ ์ธก์์ ์์์น ๋ชปํ ์ค๋จ, GC(Garbage Collection), ํ๋ก์ธ์ค ์ ์ง, ๋คํธ์ํฌ ์ง์ฐ ๋ฑ์ ์ด์ ๋ก ์์
์ด ์ง์ฐ๋ ์ ์๋ค.
function writeData(filename, data) {
var lock = lockService.acquireLock(filename);
if (!lock) {
throw 'Failed to acquire lock';
}
try {
var file = storage.readFile(filename);
var updated = updateContents(file, data);
storage.writeFile(filename, updated);
} finally {
lock.release();
}
}์์ ์ฝ๋๋ ๋ฝ์ ํ๋ํ๊ณ ํ์ผ์ ์ ์ฅํ๋ ์ฝ๋์ด๋ค.
์ ์ฝ๋์์ ๋ฐ์ํ ์ ์๋ ์๋๋ฆฌ์ค๋ ์๋์ ๊ฐ๋ค.

- ํด๋ผ์ด์ธํธ 1์ด ๋ฝ์ ํ๋ํ๊ณ ํ์ผ์ ์ฝ๋๋ค.
- ์ด๋ stop-the-world GC ๋๋ ํ๋ก์ธ์ค ์ค๋จ์ผ๋ก ํด๋ผ์ด์ธํธ 1์ด ๋ฉ์ถ๋ค.
- ๋ฝ์ TTL ๋ง๋ฃ๋ก ์ธํด Redis์์ ์๋์ผ๋ก ํด์ ๋๋ค.
- ํด๋ผ์ด์ธํธ 2๊ฐ ์๋ก์ด ๋ฝ์ ํ๋ํ๊ณ ํ์ผ์ ์์ ํ ๋ค ์ ์ฅํ๋ค.
- ํด๋ผ์ด์ธํธ 1์ด ๋ณต๊ตฌ๋์ด ๋ฉ์ถ ์์ ์ดํ์ ํ์ผ ์ํ๋ฅผ ๋ชจ๋ฅธ ์ฑ ๋ค์ ์ ์ฅํ๋ค.
- ๊ฒฐ๊ณผ์ ์ผ๋ก ํด๋ผ์ด์ธํธ 2์ ๋ณ๊ฒฝ์ฌํญ์ด ํด๋ผ์ด์ธํธ 1์ ์ํด ๋ฎ์ฌ์จ์ง๋ค.
์ผ๋ฐ์ ์ผ๋ก GC์ ์๊ฐ์ ๋งค์ฐ ์งง์ ์์ ๊ฐ์ ์ํฉ์ด ๋ฐ์ํ๋ ๊ฒฝ์ฐ๋ ๊ทนํ ๋๋ฌผ๋ค. ํ์ง๋ง GC ์ด์ธ์๋ ๋คํธ์ํฌ ์ง์ฐ์ด๋ timing ์ด์์ ๊ฐ์ ๋ฌธ์ ๋ก ์์ ๋น์ทํ ์ํฉ์ด ๋ฐ์ํ ์ ์๋ค. ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์๋ ํ์ฑํ ํฐ๊ณผ ๊ฐ์ด ๋ฒ์ ์ ๋ช
์ํ๋ ํ ํฐ์ ๋์
ํจ์ผ๋ก์จ ํด๊ฒฐํ ์ ์๋ค. ๋๊ด๋ฝ๊ณผ ๋น์ทํ ๋ฉ์ปค๋์ฆ์ผ๋ก ๋์ํ๋ค.

ํ์ฑ ํ ํฐ์ ์์ ๊ฐ์ด ๋ฒ์ ๋ช
์ ๋ช
์ํจ์ผ๋ก์จ ์ดํ์ client 2๊ฐ ์ฐ๊ธฐ๋ฅผ ์งํํ์์ ๋ ํด๋น ํ ํฐ์ version์ด ๋ ๋์ผ๋ฏ๋ก ์ถํ์ ๋ค์ด์ค๋ Client1์ ํ ํฐ์ ๋ํด์๋ rejectํ ์ ์๋ ๋ฐฉ์์ด๋ค.
ํ์ง๋ง RedLock์ ํ์ฑ ํ ํฐ ๊ธฐ๋ฅ์ ์ ๊ณตํ์ง ์๋๋ค. ํ์ฑ ํ ํฐ์ ๋์
ํ๋ ค๋ฉด, ๋ฝ์ ํ๋ํ ๋๋ง๋ค ์ฆ๊ฐํ๋ Global Counter๊ฐ ํ์ํ๋ค. ์ด๋ฅผ ์ํด์๋ ๋
ธ๋ ๊ฐ์ ๊ฐ์ ๋๊ธฐํํ ์ ์๋ ํฉ์ ์๊ณ ๋ฆฌ์ฆ์ด ์ ์ ๋์ด์ผ ํ๋ค. ๊ทธ๋ฌ๋ RedLock์ ์ด๋ฌํ ๋๊ธฐํ ์๋จ์ ๊ฐ์ถ๊ณ ์์ง ์์ผ๋ฉฐ, ๊ฒฐ๊ตญ ์ ์ญ ์นด์ดํฐ๋ฅผ ๊ตฌ์ฑํ ๋ฐฉ๋ฒ์ด ์๋ค. ๊ทธ๋ ๋ค๊ณ ํ๋์ Redis ์ธ์คํด์ค์ ์นด์ดํฐ๋ฅผ ๊ตฌํํ๋ค๋ฉด, ์ด๋ ๋จ์ผ ์ฅ์ ์ง์ (SPOF)์ด ๋์ด ์์คํ
์ ์ฒด์ ์์ ์ฑ์ ํด์น ์ ์๋ค.
๊ฒฐ๊ตญ RedLock์ ๊ฐํ ์ผ๊ด์ฑ์ ๋ณด์ฅ๋ฐ์ง ๋ชปํ๋ ๊ฒ์ด๋ค.
๐น2. ๋ฆฌ๋ ๋ ธ๋๋ ์ค์ ํฉ์ ๋ฉ์ปค๋์ฆ์ด ์๋ค
RedLock์ Paxos, Raft์ฒ๋ผ ๋ฆฌ๋ ์ ์ถ, ๋ก๊ทธ ํฉ์ ๋ฑ์ ํ์ง ์๋๋ค.
(์์์ ๋ณด์๋ ๊ฒ์ฒ๋ผ ๋ฝ์ ํ๋ํ์ง ๋ชปํ ๋
ธ๋์ ๋ํด์๋ ๋ณ๋ค๋ฅธ ์กฐ์น๋ฅผ ์ทจํ์ง ์๋๋ค.)
๋จ์ํ ๋ค์ ๋
ธ๋์ ์๋ต๋ง์ผ๋ก "๋ฝ์ ์ก์๋ค"๊ณ ํ๋จํ๋ ๊ตฌ์กฐ๋ค.
๋ฐ๋ผ์ ๋
ธ๋ ๊ฐ ์ํ ์ ํ, ์ปค๋ฐ ํ์ , ๋กค๋ฐฑ์ด ์๊ธฐ ๋๋ฌธ์ ๊ฐ๋ ฅํ ํธ๋์ญ์
์์ค์ ๋ฝ ํฉ์์๋ ๋ฏธ์น์ง ๋ชปํ๋ค
๐ธ4. ๋ถ์ฐ๋ฝ์ ์คํ๋ฝ, ๋ฎคํ ์ค, ์ธ๋งํฌ์ด?
Redis๋ฅผ ํ์ฉํ ๋ถ์ฐ๋ฝ ๊ตฌํ ๋ฐฉ์์ ๋ค์ํ ํด๋ผ์ด์ธํธ๋ฅผ ํตํด ๊ฐ๋ฅํ์ง๋ง, ์ผ๋ฐ์ ์ผ๋ก ๋ง์ด ์ฌ์ฉ๋๋ ๋ํ์ ์ธ ํด๋ผ์ด์ธํธ๋ ๋ค์ ๋ ๊ฐ์ง์ด๋ค.
- Lettuce: ์ ์์ค Redis ํด๋ผ์ด์ธํธ๋ก, ๋ถ์ฐ๋ฝ์ ์ง์ ๊ตฌํํด์ผ ํจ
- Redisson: ๊ณ ์์ค ์ถ์ํ๋ฅผ ์ ๊ณตํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก, ๋ฝ ๊ด๋ จ ๊ธฐ๋ฅ์ด ๋ด์ฅ๋์ด ์์
1๏ธโฃ Lettuce๋ฅผ ํ์ฉํ Lock
๐นLettuceConfig.kt
@Configuration
class LettuceConfig(
private val redisProperties: RedisProperties
) {
@Bean
fun redisConnectionFactory(): RedisConnectionFactory {
val config = RedisStandaloneConfiguration(redisProperties.host, redisProperties.port)
config.password = RedisPassword.of(redisProperties.password)
return LettuceConnectionFactory(config)
}
@Bean
fun redisTemplate(connectionFactory: RedisConnectionFactory): RedisTemplate<String, String> {
val template = RedisTemplate<String, String>()
template.setConnectionFactory(connectionFactory)
template.keySerializer = StringRedisSerializer()
template.valueSerializer = StringRedisSerializer()
return template
}
@Bean
fun stringRedisTemplate(connectionFactory: RedisConnectionFactory): StringRedisTemplate {
val template = StringRedisTemplate()
template.setConnectionFactory(connectionFactory)
template.keySerializer = StringRedisSerializer()
template.valueSerializer = StringRedisSerializer()
return template
}
}๐นRedisLockRepository.kt (v1)
@Component
class RedisLockRepository(
private val redisTemplate: RedisTemplate<String, String>
) {
/**
* SETNX + PX ๋ก ๋ฝ ํ๋ ์๋
*/
fun lock(key: String, timeoutMillis: Long): Boolean {
return redisTemplate
.opsForValue()
.setIfAbsent(key, "LOCK", Duration.ofMillis(timeoutMillis)) ?: false
}
/**
* ๋จ์ DEL ๋ช
๋ น์ผ๋ก ๋ฝ ํด์
*/
fun unlock(key: String): Boolean {
return redisTemplate.delete(key)
}
}์์ ๊ฐ์ด Lettuce์ ๊ฒฝ์ฐ์๋ ์ง์ lock ๋ฉ์๋์ unlock ๋ฉ์๋๋ฅผ ๊ตฌํํด์ผํ๋ค.
์์ ์ฝ๋์ฒ๋ผ ๋จ์ํ DEL ๋ช
๋ น์ด๋ฅผ ์ฌ์ฉํ์ฌ ๋ฝ์ ํด์ ํ๋ ๋ฐฉ์์ผ๋ก ๊ตฌํํ ์ ์์ง๋ง, ์ด ๋ฐฉ์์ ๋ฝ์ ์์ ํ์ง ์์ ํด๋ผ์ด์ธํธ๊ฐ ๋ฝ์ ํด์ ํ ์ ์๋ ์ํ์ด ์์ด ์น๋ช
์ ์ด๋ค.
๐นRedisLockRepository.kt (v2)
@Component
class RedisLockRepository(
private val redisTemplate: RedisTemplate<String, String>
) {
private lateinit var unlockScript: DefaultRedisScript<Long>
private val lockExpiration = Duration.ofSeconds(10)
private val lockPrefix = "LOCK:"
@PostConstruct
fun init() {
// Lua ์คํฌ๋ฆฝํธ: ์์ ์(UUID)๊ฐ ์ผ์นํ ๋๋ง ๋ฝ ํด์
unlockScript = DefaultRedisScript<Long>().apply {
scriptText = """
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
""".trimIndent()
resultType = Long::class.java
}
}
/**
* UUID๋ฅผ ํฌํจํ ๋ฝ ํ๋
* @param key ๋ฝ ํค
* @param uuid ํด๋ผ์ด์ธํธ ๊ณ ์ ID
*/
fun tryLock(key: String, uuid: String): Boolean {
return redisTemplate
.opsForValue()
.setIfAbsent(lockPrefix + key, uuid, lockExpiration) ?: false
}
/**
* Lua ์คํฌ๋ฆฝํธ๋ก ์์ ํ๊ฒ ๋ฝ ํด์
*/
fun releaseLock(key: String, uuid: String) {
redisTemplate.execute(
unlockScript,
listOf(lockPrefix + key),
uuid
)
}
}๋ฐ๋ผ์ ๋ณด๋ค ์์ ํ ๋ฐฉ๋ฒ์ผ๋ก ๋ฝ์ ์ค์ ํ ๋ ๊ณ ์ ์๋ณ์(UUID)๋ฅผ ํจ๊ป ์ ์ฅํ๊ณ ,
ํด์ ์ Lua ์คํฌ๋ฆฝํธ๋ฅผ ํตํด ํด๋น UUID๊ฐ ์ผ์นํ ๊ฒฝ์ฐ์๋ง ๋ฝ์ ํด์ ํ๋ ๋ฐฉ์์ผ๋ก ๋์ฒดํ ์ ์๋ค.
๐ง ์คํ๋ฝ์ ํตํ ๋ฝ ํ๋
@Service
class GroupLettuceService(
private val redisLockRepository: RedisLockRepository,
private val gatherService: GatherService
) {
fun join(groupId: Long, userId: Long) {
val key = groupId.toString()
val uuid = UUID.randomUUID().toString()
while (!redisLockRepository.tryLock(key, uuid)) {
Thread.sleep(100)
}
try {
gatherService.join(groupId, userId)
} finally {
redisLockRepository.releaseLock(key, uuid)
}
}
}Lettuce๋ฅผ ํตํ ๋ฝ ํ๋ ์๋ ๋ก์ง์ ์์ ๊ฐ๊ณ ์์ ๋ก์ง์ ํตํด ๋ฝ์ ์ป๋ ๋ฐฉ์์ด spin lock์ ํํ์์ ์ ์ ์๋ค.
๋ฝ์ ํ๋ํ์ง ๋ชปํ ๊ฒฝ์ฐ, ์ผ์ ์๊ฐ ๊ฐ๊ฒฉ์ผ๋ก ๊ณ์ Redis์ ๋ฝ ํ๋ ์์ฒญ์ ๋ณด๋ด๋ ๋ฐฉ์์ด๋ฉฐ ๋ฝ์ด ์ฑ๊ณตํ ๋๊น์ง ์ด ๊ณผ์ ์ ๋ฐ๋ณตํ๊ฒ ๋๋ค.

ํ์ง๋ง ์์ ๊ฐ์ ๋จ์ํ spin lock์ ๋ฐฉ์์ ๋ฝ์ ํ๋ํ์ง ๋ชปํ ๊ฒฝ์ฐ, ๋ฝ์ด ํ๋ฆด ๋๊น์ง Redis์ ๋ฐ๋ณต์ ์ผ๋ก while ๋ฃจํ๋ก ์์ฒญ์ ๋ณด๋ด์ผ ํ๋ฉฐ ์ด ๊ณผ์ ์์ ์ค๋ ๋๋ ๊ณ์ ๊นจ์ด ์๋ ์ํ๋ก CPU๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋ค.
ํนํ Redis๋ ์ฑ๊ธ ์ค๋ ๋๋ก ๋์ํ๊ธฐ ๋๋ฌธ์, ๋ฝ ํ๋์ ์ํ ๋ฐ๋ณต ์์ฒญ์ด ๋ง์์ง์๋ก Redis ์์ฒด์ ๋ถํ๊ฐ ์์ด๊ฒ ๋๊ณ ์ด๋ ์ ์ฒด ์ฒ๋ฆฌ ์ฑ๋ฅ ์ ํ๋ก ์ด์ด์ง ์ ์๋ค.
์ฆ, ๋จ์ ๊ตฌํ์ ๊ฐ๋ฅํ์ง๋ง, ๋ฝ ๊ฒฝํฉ์ด ๋ง์ ์ํฉ์์๋ Redis์ ์ ํ๋ฆฌ์ผ์ด์
๋ชจ๋์ ๋ถ๋ด์ ์ค ์ ์๋ ๋ฐฉ์์ด๋ค.
2๏ธโฃ Redisson์ ํ์ฉํ Lock
Lettuce์์๋ ๋ถ์ฐ๋ฝ์ ๊ฐ๋ฐ์๊ฐ ์ง์ ๊ตฌํํด์ผ ํ์ง๋ง, Redisson์ ๋ถ์ฐ๋ฝ ๊ธฐ๋ฅ์ ๊ณ ์์ค์ผ๋ก ์ถ์ํํ์ฌ ์ ๊ณตํ๋ค.
๋ฐ๋ผ์ ๋ฝ ํ๋, ํด์ , ์๋ ์ฐ์ฅ ๋ฑ ๋ณต์กํ ๋ก์ง์ ์ง์ ์์ฑํ ํ์ ์์ด ๊ฐ๋จํ API ํธ์ถ๋ง์ผ๋ก ๋ถ์ฐ๋ฝ์ ์ฌ์ฉํ ์ ์๋ค.

Redisson์์๋ RLock์ด๋ผ๋ ๋ถ์ฐ ๋ฝ ๊ฐ์ฒด ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํด์ค๋ค.
๊ทธ๋ฆฌ๊ณ ๋ฝ์ ๊ฑฐ๋ ๊ตฌํ์ฒด๋ก๋ tryLock()์ ์ ๊ณตํ๋ ๊ฒ์ ์ดํด๋ณผ ์ ์๋ค.

์ด๋ tryLock์ ๊ตฌํํ๋ ํด๋์ค๋ฅผ ์ดํด๋ณด ๋ค์๊ณผ ๊ฐ์ ์ธ ๊ฐ์ง์ ํด๋์ค๊ฐ ์์์ ํ์ธํ์๋ค.
- RedissonLock
- RedissonMultiLock
- RedissonSpinLock
๊ฐ๊ฐ์ ๋ฐฉ์์ ๋ํด ์์๋ณด์.
๐นRedissonLock
redisson/redisson/src/main/java/org/redisson/RedissonLock.java at master ยท redisson/redisson
Redisson - Valkey & Redis Java client. Real-Time Data Platform. Sync/Async/RxJava/Reactive API. Over 50 Valkey and Redis based Java objects and services: Set, Multimap, SortedSet, Map, List, Qu...
github.com
[ ๋ฉ์๋ ์๊ทธ๋์ฒ ]
public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException- waitTime: ๋ฝ์ ์ป๊ธฐ ์ํด ์ต๋ ๊ธฐ๋ค๋ฆด ์๊ฐ
- leaseTime: ๋ฝ์ ์ ์ ํ ์ ์๋ ์๊ฐ (์๋์ผ๋ก ํด์ ๋จ)
- unit: ์๊ฐ ๋จ์
- ๋ฐํ๊ฐ: ๋ฝ ํ๋ ์ฌ๋ถ
[ ์๊ฐ ๊ณ์ฐ ]
long time = unit.toMillis(waitTime);
long current = System.currentTimeMillis();
long threadId = Thread.currentThread().getId();ํ์์์ ๊ณ์ฐ์ ์ํ ์์ ์๊ฐ๊ณผ thread ID ์ค์ ํ๋ค.
[ ๋ฝ ํ๋ ์๋ ]
Long ttl = this.tryAcquire(waitTime, leaseTime, unit, threadId);
if (ttl == null) {
return true;
}์์ ๋ก์ง์ ์ดํด๋ณด๋ฉด tryAcquire() ๋ฉ์๋๋ฅผ ํธ์ถํ๋ ๊ฒ์ ์ดํด๋ณผ ์ ์๋ค.
private Long tryAcquire(long waitTime, long leaseTime, TimeUnit unit, long threadId) {
return (Long)this.get(this.tryAcquireAsync(waitTime, leaseTime, unit, threadId));
}๊ทธ๋ฆฌ๊ณ tryAcquire()๋ฉ์๋๋ ๋ด๋ถ์ ์ผ๋ก tryAcquireAsync()๋ฅผ ํธ์ถํ๋ค.

๊ทธ๋ฆฌ๊ณ tryAcquireAsync()๋ ๋น๋๊ธฐ์ ์ผ๋ก tryLockInnerAsync()๋ฅผ ํธ์ถํ๋ ๊ฒ์ ์ดํด๋ณผ ์ ์๋ค.
<T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
return this.evalWriteAsync(this.getRawName(),
LongCodec.INSTANCE, command,
"if ((redis.call('exists', KEYS[1]) == 0) or (redis.call('hexists', KEYS[1], ARGV[2]) == 1)) then redis.call('hincrby', KEYS[1], ARGV[2], 1); redis.call('pexpire', KEYS[1], ARGV[1]); return nil; end; return redis.call('pttl', KEYS[1]);", Collections.singletonList(this.getRawName()), new Object[]{unit.toMillis(leaseTime), this.getLockName(threadId)});
}์ด๋, tryLockIntterAsync()๋ ๋ด๋ถ์ ์ผ๋ก Lua Script๋ฅผ ํธ์ถํ์ฌ Lock์ ํ๋ํ๋ ๋ก์ง์ ์์ฑํ ๊ฒ์ ์ดํด๋ณผ ์ ์๋ค.
Redisson์ Netty ๊ธฐ๋ฐ์ ๋น๋๊ธฐ ๋คํธ์ํฌ ํด๋ผ์ด์ธํธ์ด๊ธฐ ๋๋ฌธ์,
Redis์ ๋ฝ ์์ฒญ์ ๋ณด๋ผ ๋ ๋ธ๋กํน ์์ด ์์ฒญ์ ๋ ๋ฆฌ๊ณ , ๊ฒฐ๊ณผ๊ฐ ๋์์ค๋ฉด ์ฝ๋ฐฑ ๊ธฐ๋ฐ์ผ๋ก ํ์ฒ๋ฆฌ๋ฅผ ์งํํ๋ค..
[ Pub/Sub ๊ตฌ๋
(๋ฝ ํด์ ์ด๋ฒคํธ ๋๊ธฐ) ]
CompletableFuture<RedissonLockEntry> subscribeFuture = this.subscribe(threadId);๋ฝ ํด์ ์ด๋ฒคํธ๊ฐ Redis ์ฑ๋์ ํตํด publish๋๋ฉด ์ด๋ฅผ ์์ ํ๊ธฐ ์ํด ๋ฝ ์ฑ๋์ ๊ตฌ๋ ํ๋ ๊ฒ์ ์ดํด๋ณผ ์ ์๋ค.

์ด๋ subscribe() ๋ฉ์๋๋ฅผ ์กฐ๊ธ ๋ ์์ธํ ์ดํด๋ณด๋ฉด threadId๋ก ๊ตฌ๋
ํ๋ ๊ฒ์ ์ดํด๋ณผ ์ ์๊ณ , ๊ตฌ๋
ํด์ ์ด๋ฒคํธ์ ๋ํด ๋ด๋ถ์ ์ผ๋ก ์ธ๋งํฌ์ด์์ ๋์์ฑ ์ ์ด๋ฅผ ํ๋ ๊ฒ์ ์ดํด๋ณผ ์ ์๋ค.
์ด๋, ์ธ๋งํฌ์ด์ ์ญํ ์ ๋ค์๊ณผ ๊ฐ๋ค.
- ํ๋์ Pub/Sub ์ฑ๋์ ๋ํด ๋์์ ๋๋ฌด ๋ง์ ๊ตฌ๋ ์ ์์ฑํ์ง ์๋๋ก ๋์์ฑ ์ ์ด
- ํ ๋ฒ์ ํ๋์ ์ค๋ ๋๋ง Redis์ SUBSCRIBE ์์ฒญ์ ๋ ๋ฆด ์ ์๋๋ก ์ ํ
- Redis์ ์ค๋ณต ๊ตฌ๋ ์์ฒญ์ ๋ณด๋ด๋๊ฑธ ๋ฐฉ์ง โ ๊ธฐ์กด ๊ตฌ๋ ์ด ์๋ค๋ฉด ์ฌ์ฌ์ฉ
[ TTL ๋ด์ ๋ฝ ์ฌ์๋ ๋ฃจํ ]
do {
long currentTime = System.currentTimeMillis();
ttl = this.tryAcquire(waitTime, leaseTime, unit, threadId);
if (ttl == null) {
return true;
}
time -= System.currentTimeMillis() - currentTime;
if (time <= 0L) {
this.acquireFailed(waitTime, unit, threadId);
return false;
}
if (ttl >= 0L && ttl < time) {
entry.getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
} else {
entry.getLatch().tryAcquire(time, TimeUnit.MILLISECONDS);
}
time -= System.currentTimeMillis() - currentTime;
} while (time > 0L);
- ๋ฝ์ ์์ง ์ป์ง ๋ชปํ๋ค๋ฉด TTL๋งํผ tryAcquire() ๋ฐ๋ณต
- Redis ์ฑ๋์์ ๋ฝ ํด์ ์ด๋ฒคํธ ์์ ์, ๋๊ธฐ ์ค์ธ ์ค๋ ๋๊ฐ ๊นจ์ด๋ ๋ค์ ๋ฝ์ ์๋
- ํ์์์ ๋ด์์ ๋ฐ๋ณต ์๋
[ ๊ตฌ๋ ํด์ ]
this.unsubscribe((RedissonLockEntry)this.commandExecutor.getNow(subscribeFuture), threadId);๋ฝ์ ํ๋ํ๊ฑฐ๋ ์คํจ ์ ๊ตฌ๋ ํด์ ํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
์ ๋ฆฌํ์๋ฉด, ๋ค์๊ณผ ๊ฐ์ด Redisson Lock์ ๋ด๋ถ์ ์ผ๋ก ์ธ๋งํฌ์ด ๊ธฐ๋ฐ Pub/Sub์ผ๋ก ๋์ํ๋ ๊ฒ์ ์ดํด๋ณผ ์ ์๋ค.
- ๋ฝ ์๋
- Redis์ ์ง์ Lua ์คํฌ๋ฆฝํธ๋ก ๋ฝ ํ๋ ์๋ (SETNX + TTL ๋ฑ)
- ์คํจ ์ โ Redis ์ฑ๋์ ๊ตฌ๋ ํด์ ๋ฝ ํด์ ์๋ฆผ์ ๊ธฐ๋ค๋ฆผ
- ๊ตฌ๋
์ฒ๋ฆฌ (subscribe)
- RedissonLock์ ๋ด๋ถ์ ์ผ๋ก LockPubSub์ ํตํด Redis Pub/Sub ์ฑ๋์ ๊ตฌ๋
- ์ด๋ AsyncSemaphore๋ก ์ค๋ณต ๊ตฌ๋
์ ๋ฐฉ์ง
- ์ฆ, ํ๋์ ์ฑ๋๋น ํ๋์ ์ฐ๊ฒฐ๋ง SUBSCRIBE ํ๋๋ก ์ธ๋งํฌ์ด๋ก ๋ณดํธ
- ํด๋น ์ฑ๋์์ ๋ฝ ํด์ ์๋ฆผ ์์ ์ โ ๋ค์ tryLock ์๋
- ๋ฝ์ ์ก์ ํด๋ผ์ด์ธํธ๊ฐ unlock() ํ๋ฉด Redis ์ฑ๋์ "UNLOCK" ๋ฉ์์ง ๋ฐํ
- ๋ค๋ฅธ ๋๊ธฐ ์ค ํด๋ผ์ด์ธํธ๋ค์ ์ด ๋ฉ์์ง๋ฅผ ๊ตฌ๋ ์ค์ด๋ฏ๋ก ์๋ฆผ ๋ฐ๊ณ ๋ฝ ์ฌ์๋
๐นRedissonMultiLock
redisson/redisson/src/main/java/org/redisson/RedissonMultiLock.java at master ยท redisson/redisson
Redisson - Valkey & Redis Java client. Real-Time Data Platform. Sync/Async/RxJava/Reactive API. Over 50 Valkey and Redis based Java objects and services: Set, Multimap, SortedSet, Map, List, Qu...
github.com
RedissonMultiLock์ ์ฌ๋ฌ ๊ฐ์ RLock์ ๋์์ ํ๋ํ์ฌ, ๋ชจ๋ ์ฑ๊ณตํด์ผ ํ๋์ ๋ฝ์ผ๋ก ๊ฐ์ฃผํ๋ ๋ฉํฐ๋ฝ ๊ตฌํ์ด๋ค.
[ ๋ฉ์๋ ์๊ทธ๋์ฒ ]
public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit)- waitTime: ๋ฝ์ ์ป๊ธฐ ์ํด ์ต๋ ๊ธฐ๋ค๋ฆด ์๊ฐ
- leaseTime: ๋ฝ์ ์ ์ ํ ์ ์๋ ์๊ฐ (์๋์ผ๋ก ํด์ ๋จ)
- unit: ์๊ฐ ๋จ์
- ๋ฐํ๊ฐ: ๋ฝ ํ๋ ์ฌ๋ถ
[ ๋ฝ ํ๋ ์๋ ๋ฃจํ ์์ ]
List<RLock> acquiredLocks = new ArrayList(this.locks.size());
ListIterator<RLock> iterator = this.locks.listIterator();์์ฐจ์ ์ผ๋ก ๋ชจ๋ ๋ฝ ํ๋์ ์๋ํ๋ฉฐ ์ฑ๊ณตํ ๋ฝ์ ๋ฆฌ์คํธ์ ์ ์ฅํ๋ค.
[ ๊ฐ RLock์ ๋ํด tryLock ์๋ ]
boolean lockAcquired = lock.tryLock(awaitTime, newLeaseTime, TimeUnit.MILLISECONDS);๋ด๋ถ์ ์ผ๋ก ์ฌ๋ฌ ๊ฐ์ RedissonLock์ ๊ฐ์ง๊ณ , ๊ฐ ๋ฝ์ ๋ํด ๊ฐ๋ณ์ ์ผ๋ก tryLock()์ ์์ฐจ ์๋ํ๋ค.
if (!lockAcquired) {
if (this.locks.size() - acquiredLocks.size() == this.failedLocksLimit()) {
break; // ์คํจ ํ์ฉ ๊ฐ์ ์ด๊ณผ
}
if (failedLocksLimit == 0) {
this.unlockInner(acquiredLocks); // ์ง๊ธ๊น์ง ํ๋ํ ๋ฝ ์ ๋ถ ํด์
if (waitTime <= 0L) return false;
failedLocksLimit = this.failedLocksLimit();
acquiredLocks.clear();
iterator = this.locks.listIterator(); // ์ฒ์๋ถํฐ ๋ค์ ์๋
} else {
--failedLocksLimit;
}
}์์ ๋ค๋ฃฌ Lock๊ณผ ํด๋น Lock ๋ก์ง์ ์ฐจ์ด์ ์ ์์ ๊ฐ๋ค.
- ํ๋์ ๋ฝ์ด๋ผ๋ ์คํจํ๋ฉด ์ ์ฒด ๋กค๋ฐฑ
- ์คํจ ํ์ฉ ๋ฒ์ ์ด๊ณผ ์ ๋ฐ๋ณต ์ข ๋ฃ
- ์๋๋ฉด ์ ๋ถ ํด์ ํ ๋ค์ ์ฒ์๋ถํฐ ์๋
์ ๋ฆฌํ์๋ฉด,
- ์ฌ๋ฌ ๊ฐ์ RLock์ ์์ฐจ์ ์ผ๋ก tryLock() ์๋
- ํ๋๋ผ๋ ์คํจํ๋ฉด ํ๋ํ ๋ชจ๋ ๋ฝ์ ํด์
- ๋จ์ ์๊ฐ ๊ณ์ฐ ํ ๋ค์ ์ฒ์๋ถํฐ ์ฌ์๋
- ์ด ๊ณผ์ ์ waitTime์ด ์์ง๋ ๋๊น์ง ๋ฐ๋ณต
RedissonMultilLock์ ์ฌ๋ฌ ๊ฐ์ RLock ์ธ์คํด์ค๋ฅผ ๋ฌถ์ด์, ๋ชจ๋ ๋์์ ๋ฝ์ ๊ฑธ ์ ์์ ๋๋ง ์ฑ๊ณตํ๋ ๊ตฌ์กฐ์ด๋ค.
๋ํ ๋ด๋ถ์ ์ผ๋ก Pub/Sub์ ๋ฐฉ์์ด ์๋ Spin Lock์ ํํ๋ฅผ ์ทจํ๊ณ ์๋ค.
๐นRedissonSpinLock
redisson/redisson/src/main/java/org/redisson/RedissonSpinLock.java at master ยท redisson/redisson
Redisson - Valkey & Redis Java client. Real-Time Data Platform. Sync/Async/RxJava/Reactive API. Over 50 Valkey and Redis based Java objects and services: Set, Multimap, SortedSet, Map, List, Qu...
github.com
[ ๋ฉ์๋ ์๊ทธ๋์ฒ ]
public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit)- waitTime: ๋ฝ์ ์ป๊ธฐ ์ํด ์ต๋ ๊ธฐ๋ค๋ฆด ์๊ฐ
- leaseTime: ๋ฝ์ ์ ์ ํ ์ ์๋ ์๊ฐ (์๋์ผ๋ก ํด์ ๋จ)
- unit: ์๊ฐ ๋จ์
- ๋ฐํ๊ฐ: ๋ฝ ํ๋ ์ฌ๋ถ
[ ๋ฝ ํ๋ ์๋ ๋ฃจํ ์์ ]
Long ttl = this.tryAcquire(leaseTime, unit, threadId);
if (ttl == null) return true;
๋ด๋ถ์ ์ผ๋ก tryLockInnerAsync()๋ฅผ ํตํด Lua ์คํฌ๋ฆฝํธ๋ก Redis์ ๋ฝ์ ์๋ํ๋ค.
[ TTL ๋ด์์ ์ฌ์๋ ๋ฃจํ (์คํ๋ฝ) ]
LockOptions.BackOffPolicy backOffPolicy = this.backOff.create();
do {
current = System.currentTimeMillis();
Thread.sleep(backOffPolicy.getNextSleepPeriod()); // ๋ฐฑ์คํ ์๊ฐ๋งํผ ๋๊ธฐ
ttl = this.tryAcquire(leaseTime, unit, threadId);
if (ttl == null) return true;
time -= System.currentTimeMillis() - current;
} while(time > 0L);- Pub/Sub ์์ด while ๋ฃจํ๋ก ๊ณ์ Redis์ ๋ฝ ์์ฒญ์ ๋ณด๋ ๋๋ค.
- ์ค๊ฐ์ Thread.sleep()์ ๋ฃ์ด busy-wait ๋ฐฉ์ง (๋ฐฑ์คํ ์ ์ฑ )
- TTL์ด ๊ณ์ ๋ฐํ๋๋ฉด ๊ณ์ ์ฌ์๋
- ํ์์์ ์ด๊ณผ ์ false ๋ฐํ
์ ๋ฆฌํ์๋ฉด,
- Redis์ ๋ฝ์ Lua ์คํฌ๋ฆฝํธ๋ก ํ๋ ์๋ (tryAcquire)
- ์คํจ ์ โ ๋ฐฑ์คํ ์ ์ฑ ์ ๋ฐ๋ผ sleep ํ ์ฌ์๋ (spin lock)
- waitTime์ด ์๋ค๋ฉด ๋จ์ ์๊ฐ ๊ณ์ฐ, 0์ด๋ฉด ์คํจ ์ข ๋ฃ
- ๋ฝ์ ํ๋ํ๋ฉด ์ฑ๊ณต, leaseTime ์์ผ๋ฉด watchdog ์ฐ์ฅ ๋ฑ๋ก
RedissonSpinLock์ Pub/Sub ๊ธฐ๋ฐ์ ๋ธ๋กํน ๋๊ธฐ๊ฐ ์๋, ์ฌ์๋ ๋ฃจํ ๊ธฐ๋ฐ ๋ฝ์ด๋ค.
lock์ ๋น ๋ฅด๊ฒ ์ก๊ฑฐ๋ ๊ฒฝ์์ด ์ ์ ํ๊ฒฝ์ ์ ํฉํ์ง๋ง, ๊ฒฝ์์ด ์ฌํ๋ฉด ์ฑ๋ฅ ์ ํ ์ฐ๋ ค๊ฐ ์๋ค.
๐ช์ ๋ฆฌ
ํ ์ด๋ธ ๋จ์์ ๋ฝ๋ณด๋ค Redis๋ฅผ ํ์ฉํ ๋ฝ์ด ๋น์ฆ๋์ค ๋ก์ง ์์ค์์ ํจ์ฌ ์ ์ฐํ๊ฒ ์ฌ์ฉ๋ ์ ์์ผ๋ฉฐ, ๋ค์ํ ์์์ ์ ์ฉ ๊ฐ๋ฅํ๋ค๋ ์ฅ์ ์ด ์๋ค. ํนํ Redisson์ ํตํด ์ธ๋งํฌ์ด Pub/Sub ๊ธฐ๋ฐ์ ๋ฝ๊ณผ Spin ๊ธฐ๋ฐ์ ๋ฝ์ด ๊ฐ๊ฐ ์ ๊ณต๋๊ณ ์๊ณ , ์ฌ์ฉ ๋ชฉ์ ๊ณผ ์์คํ ํ๊ฒฝ์ ๋ฐ๋ผ ์ ์ ํ ๋ฐฉ์์ ์ ํํด์ผ ํ๋ค. ํ์ง๋ง RedLock์ ๊ฒฝ์ฐ ๋ด๋ถ์ ํฉ์ ์๊ณ ๋ฆฌ์ฆ์ด ์์ด ๊ฐํ ์ผ๊ด์ฑ์ ๋ณด์ฅํ์ง ๋ชปํ๋ค๋ ํ๊ณ๊ฐ ์กด์ฌํ๋ฏ๋ก ์ค๋ฌด์์๋ ์ ์คํ๊ฒ ์ ์ฉํด์ผ ํ๋ค. ๋ ๋์๊ฐ ์ด๋ฌํ ์ ์ฝ์ ๋ณด์ํ๊ณ ์ ํ๋ค๋ฉด Zookeeper์ ๊ฐ์ ํฉ์ ๊ธฐ๋ฐ ์์คํ ์ ๊ณ ๋ คํ ์ ์๋ค.
๐ ์ถ์ฒ
https://redis.io/docs/latest/develop/clients/patterns/distributed-locks/
Distributed Locks with Redis
A distributed lock pattern with Redis
redis.io
https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html
How to do distributed locking โ Martin Kleppmannโs blog
How to do distributed locking Published by Martin Kleppmann on 08 Feb 2016. As part of the research for my book, I came across an algorithm called Redlock on the Redis website. The algorithm claims to implement fault-tolerant distributed locks (or rather,
martin.kleppmann.com
'Redis' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| ๋ ๋์ค ์บ์ ์คํฌํผ๋ ํ์ (Cache Stampede) (1) | 2025.02.07 |
|---|---|
| ๋ ๋์ค ์บ์ฑ ์ ๋ต, ์บ์ ๋ง๋ฃ, maxmemory-policy (3) | 2025.02.04 |
| Redis๋? (๋ ๋์ค๊ฐ ์ฑ๊ธ์ฐ๋ ๋๋ก ๋์ํ๋ ์ด์ ) (0) | 2025.01.30 |