[๋์์ฑ ๋ฌธ์ ] ๋ค์ํ ๊ธฐ๋ฒ์ ํ์ฉํ ํด๊ฒฐ ๋ฐฉ์
๋ ๊ฐ ์ด์์ ํธ๋์ญ์ ์ด ๋์ผํ ๋ฐ์ดํฐ๋ฅผ ๋์์ ์ฝ๊ณ ์์ ํ ๋,
ํ ํธ๋์ญ์ ์ ์์ ๋ด์ฉ์ด ๋ค๋ฅธ ํธ๋์ญ์ ์ ์์ ๋ด์ฉ์ ๋ฎ์ด์ฐ๋ ํ์
ex) ํธ๋์ญ์ A๊ฐ x = 10์ ์ฝ๊ณ x = 20์ผ๋ก ์ ๋ฐ์ดํธ, ํธ๋์ญ์ B๊ฐ ๋์์ x = 10 ์ ์ฝ๊ณ x = 30์ผ๋ก ์ ๋ฐ์ดํธ ํ ์ปค๋ฐ
A์ ์ ๋ฐ์ดํธ ๊ฒฐ๊ณผ(20)๊ฐ B์ ์ ๋ฐ์ดํธ ๊ฒฐ๊ณผ(30)์ผ๋ก ๋ฎ์ฌ์ฐ์ฌ์ง
ํธ๋์ญ์ ์ด ์์ง ์ปค๋ฐ๋์ง ์์ ๋ค๋ฅธ ํธ๋์ญ์ ์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด ์ฐ์ฐ์ ์ฌ์ฉํจ.
์ด๋, ๋ค๋ฅธ ํธ๋์ญ์ ์ด ๋กค๋ฐฑ๋๋ฉด ์ฝ์๋ ๊ฐ์ด ์๋ชป๋ ๋ฐ์ดํฐ๊ฐ ๋๋ ํ์
ex) ํธ๋์ญ์ A๊ฐ x = 20์ผ๋ก ์์ ํ ๋ค ์ปค๋ฐํ์ง ์์, ํธ๋์ญ์ B๊ฐ A์ ์์ ๋ ๊ฐ์ ์ฝ๊ณ x = 20 ๊ธฐ๋ฐ์ผ๋ก ์ฐ์ฐ ์ํ. A๊ฐ ๋กค๋ฐฑ๋๋ฉด์ x = 10์ผ๋ก ๋ณ๊ฒฝ๋จ (ํธ๋์ญ์ B๋ ์๋ชป๋ ๋ฐ์ดํฐ ์ฌ์ฉํ ๊ฒ ๋จ)
ํธ๋์ญ์ ์ด ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์ฌ๋ฌ ๋ฒ ์ฝ์๋๋ฐ,
๋ค๋ฅธ ํธ๋์ญ์ ์ด ๊ทธ ์ฌ์ด์ ํด๋น ๋ฐ์ดํฐ๋ฅผ ์์ ํ์ฌ ์ฒ์ ์ฝ์์ ๋์ ๋ค๋ฅธ ๊ฐ์ ์ฝ๊ฒ ๋๋ ํ์
ex) ํธ๋์ญ์ A๊ฐ x = 10์ ์ฝ๊ณ , ์ดํ ํธ๋์ญ์ B๊ฐ x = 20์ผ๋ก ๊ฐ์ ์์ ํ ํ ํธ๋์ญ์ A๊ฐ x ๋ฅผ ์ฝ์ ๋, x = 20์ ์ฝ๋ ํ์
ํธ๋์ญ์ ์ด ๋ฒ์ ๋ด์์ ์ฌ๋ฌ ํ์ ์ฝ์์ ๋, ๋ค๋ฅธ ํธ๋์ญ์ ์ด ์๋ก์ด ๋ฐ์ดํฐ๋ฅผ ์ฝ์ ํ๊ฑฐ๋ ์ญ์ ํ์ฌ ์ฝ์ ๋ฐ์ดํฐ์ ๋ฒ์๊ฐ ๋ฐ๋๋ ํ์
ํธ๋์ญ์ A๊ฐ ํน์ ์กฐ๊ฑด์ ๋ง๋ ๋ ์ฝ๋๋ฅผ ์ฝ๊ณ , ํธ๋์ญ์ B๊ฐ ํด๋น ์กฐ๊ฑด์ ๋ง์กฑํ๋ ๋ ์ฝ๋๋ฅผ ์ฝ์ ํ ํ,
ํธ๋์ญ์ A๊ฐ ๋ค์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ ๋ ์๋ก์ด ๋ ์ฝ๋๊ฐ ํฌํจ๋จ
์ ํ๋ฆฌ์ผ์ด์ ๋ ๋ฒจ์์ ๋ฝ์ ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ์ด๋ค.
synchronized, ReentrantLock, Semaphore ๋ฑ์ ์ฌ์ฉํ๋ค.
Semaphore
: ๋์ ์ ๊ทผ ๊ฐ๋ฅํ ์์์ ์๋ฅผ ์ ํํ๋ ๋๊ธฐํ ๋ฉ์ปค๋์ฆ. ๋ฆฌ์์ค ํ์ด๋ ์ค๋ ๋ ํ๊ณผ ๊ฐ์ ํ์ ๋ ์์์ ๊ด๋ฆฌํ๋๋ฐ ์ ์ฉํจ
| ํน์ง | synchronized | ReentrantLock | Semaphore |
| ๋ฝ ํ๋/ํด์ ๋ฐฉ์ | ์๋(์๋ฐ VM์ด ๊ด๋ฆฌ) | ๋ช ์์ (lock()/unlock()) | ๋ช ์์ (acquire()/release()) |
| ์ฌ์ง์ ์ฑ | ๋ถ๊ฐ๋ฅ | ๊ฐ๋ฅ(์๊ธฐ ์์ ์ด ๋ฝ์ ์ฌ๋ฌ ๋ฒ ํ๋ ๊ฐ๋ฅ) | ์์ |
| ๋ฐ๋๋ฝ ๋ฐฉ์ง |
X | X | X |
| ์ฑ๋ฅ | ๋ฎ์ ์ฑ๋ฅ (๊ฒฝ์ ์ํ ์ ์ฑ๋ฅ ์ ํ) | ๋์ ์ฑ๋ฅ(๋ค์ค ์ค๋ ๋ ํ๊ฒฝ์์ ํจ์จ์ ) | ์์ ๊ฐ์์ ๋ฐ๋ผ ์ฑ๋ฅ์ด ๋ฌ๋ผ์ง |
| ๋น์ฐจ๋จ ๋ฝ ์ง์ | ์์ | ๊ฐ๋ฅ(tryLock()) | ๊ฐ๋ฅ(tryAcquire()) |
| ๊ธฐํ ๊ธฐ๋ฅ | ์์ | ๊ณต์ ์ฑ ์ค์ , ํ์์์ ๋ฑ | ์์ ์ ํ ๊ฐ์ ์กฐ์ |
| ๊ฐ๋จํ๊ณ ์๋์ผ๋ก ๊ด๋ฆฌ๋๋ ๋ฝ์ ์ฌ์ฉํ ๋ ์ ํฉ ์ฑ๋ฅ์ด ์ค์ํ์ง ์๊ฑฐ๋ ๋ฝ์ด ๊ฐ๋จํ ๊ฒฝ์ฐ์ ์ ์ฉ |
ํ์์์์ ์ค์ ํ๊ฑฐ๋ ๋ฝ์ ์๋ํ๊ณ ์คํจํ์ ๋ ๋ค๋ฅธ ์์ ์ ํ๊ณ ์ถ์ ๋ ์ฌ์ฉ |
๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ํ, ๋์ ์ ๊ทผ์ด ์ ํ๋ ์์ ๊ด๋ฆฌํ ๋ |
์ค์ ๋ก ๋ฐ์ด์ Lock์ ๊ฑธ์ด์ ์ ํฉ์ฑ์ ๋ง์ถ๋ ๋ฐฉ๋ฒ์ด๋ค.
execlusive lock์ ๊ฑธ๊ฒ ๋๋ฉฐ, ๋ค๋ฅธ ํธ๋์ญ์ ์์๋ lock์ด ํด์ ๋๊ธฐ ์ ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ๊ฐ ์ ์๊ฒ ๋๋ค.
๋ฐ๋๋ฝ์ด ๊ฑธ๋ฆด ์ ์๊ธฐ ๋๋ฌธ์ ์ฃผ์ํ์ฌ ์ฌ์ฉํด์ผ ํ๋ค.
์ฅ์
1. ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ด์์ ๋์์ฑ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ค.
2. ํธ๋์ญ์ ์ ์ผ๊ด์ฑ์ ๋ณด์ฅํ๋ค.
๋จ์
ํธ๋ํฝ์ด ๋์ ํ๊ฒฝ์์๋ ๋ฝ ๊ฒฝ์์ด ์ฌํด์ ธ ์ง์ฐ์ด ๋ฐ์ํ ์ ์๋ค.
ํน์ ํ์ ๋ํด์ ๋ฝ์ ๊ฑธ์ด ๋ค๋ฅธ ํธ๋์ญ์ ๋ค์ด ํด๋น ํ์ ์์ ํ์ง ๋ชปํ๋๋ก ํ๋ค.
-- ํ ๋ ๋ฒจ ๋ฝ
SELECT * FROM users WHERE user_id = 1 FOR UPDATE;
ํ ์ด๋ธ ์ ์ฒด์ ๋ฝ์ ๊ฑธ์ด ๋์์ ์ฌ๋ฌ ํธ๋์ญ์ ์ด ์ ๊ทผํ์ง ๋ชปํ๋๋ก ํ๋ค.
-- ํ
์ด๋ธ ๋ ๋ฒจ ๋ฝ
LOCK TABLE users IN EXCLUSIVE MODE;
์ ๋ฐ์ดํธ ์ ์ ๋ฒ์ ๋ฒํธ๋ฅผ ์ฒดํฌํ๊ฑฐ๋ ํ์์คํฌํ๋ฅผ ํ์ธํ์ฌ ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ด ์ผ์ด๋ฌ๋์ง ํ๋ณํ๋ ๋ฐฉ๋ฒ์ด๋ค.
๋ฐ์ดํฐ ์ถฉ๋์ด ๋ฐ์ํ๋ฉด ๋กค๋ฐฑํ๊ฑฐ๋ ๋ค์ ์๋ํ๋ค.
JPA์์๋ @Version ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ์ฌ ์ํฐํฐ์ ๋ฒ์ ์ ๊ด๋ฆฌํ๋ค.
@Entity
public class Product {
@Id
private Long id;
@Version
private Long version;
private String name;
private int price;
}
์ถฉ๋์ ์๋ฐฉํ๋ฉด์๋ ์ฑ๋ฅ์ ์ต์ ํํ ์ ์๊ธฐ์ ๋๊ด์ ์ฒ๋ฆฌ์ ์ ํฉํ๋ค.
์์ฃผ ๋ณ๊ฒฝ๋์ง ์๋ ๋ฐ์ดํฐ์ ๋ํด ํจ์จ์ ์ด๋ค.
์ฌ์๋ ๋ก์ง์ด ํ์ํ๋ค.
์๋ฒ๊ฐ ์ฌ๋ฌ ๋์ผ ๊ฒฝ์ฐ, ๋ถ์ฐ ํ๊ฒฝ์์ ๋๊ธฐํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ถ์ฐ ๋ฝ์ ์ฌ์ฉํ๋ค.
Redis, ZooKeeper, Etcd์ ๊ฐ์ ๋ถ์ฐ ์์คํ ์ ์ฌ์ฉํ์ฌ ๋ฝ์ ๊ด๋ฆฌํ๋ค.
- Lettuce
SETNX ๋ช ๋ น์ด(set if not exists)๋ฅผ ์ฌ์ฉํ์ฌ ํ๋์ ์๋ฒ๋ง ์์์ ์ฌ์ฉํ ์ ์๋๋ก ๋ฝ์ ๊ฑธ ์ ์๋ค.
Spin Lock ๋ฐฉ์์ผ๋ก Retry ๋ก์ง์ ๊ฐ๋ฐ์๊ฐ ์์ฑํด์ฃผ์ด์ผ ํ๋ค.
- Redisson
pub-sub ๊ธฐ๋ฐ์ผ๋ก Lock ๊ตฌํ์ ์ ๊ณตํ๋ค.
pub-sub ๋ฐฉ์์ด๋ ์ฑ๋์ ํ๋ ๋ง๋ค๊ณ , ๋ฝ์ ์ ์ ์ค์ธ ์ค๋ ๋๊ฐ ๋ฝ์ ํด์ ํ์์ ๋๊ธฐ ์ค์ธ ์ค๋ ๋์ ์๋ ค์ค์ผ๋ก์จ ๋๊ธฐ์ค์ธ ์ค๋ ๋๊ฐ ๋ฝ ์ ์ ๋ฅผ ์๋ํ๋ ๋ฐฉ์์ด๋ค.
ZooKeeper/Etcd: ๋ถ์ฐ ์์คํ ์์ ์์์ ๋๊ธฐํ๋ฅผ ์ํด ์ฌ๋ฌ ์๋ฒ ๊ฐ์ ๋ฝ์ ๊ด๋ฆฌํ๋ค.
// Redis์์ ๋ถ์ฐ ๋ฝ ๊ตฌํ
Jedis jedis = new Jedis("localhost");
boolean isLockAcquired = jedis.setnx("lock_key", "locked") == 1;
if (isLockAcquired) {
// ์์
์ํ
jedis.del("lock_key"); // ์์
์๋ฃ ํ ๋ฝ ํด์
}