
Distributed Counter분산 카운터
분산 카운터(Distributed Counter)는 여러 서버에서 동시에 증가하는 카운터를 일관성 있게 관리하는 시스템이다. 좋아요 수, 조회수, 재고 관리에 활용된다.
분산 카운터의 과제
단일 서버:
counter += 1 # 간단하지만 확장 불가
분산 환경 문제:
서버A: counter=100 읽기
서버B: counter=100 읽기
서버A: counter=101 쓰기
서버B: counter=101 쓰기 ← 101이어야 하는데 102가 됨 (Lost Update)
해결책:
1. Redis INCR (원자적 연산)
2. DB 낙관적 락 (Optimistic Lock)
3. CRDT (충돌 없는 복제 자료구조)
4. 배치 집계 (근사치 허용)Redis 원자적 카운터
python
import redis
r = redis.Redis()
def increment_like(post_id: str, delta: int = 1) -> int:
"""원자적 증가 (경쟁 조건 없음)"""
key = f"likes:{post_id}"
return r.incrby(key, delta)
def get_like_count(post_id: str) -> int:
key = f"likes:{post_id}"
count = r.get(key)
return int(count) if count else 0
# 여러 카운터 동시 업데이트 (파이프라인)
def batch_increment(updates: dict):
pipe = r.pipeline()
for post_id, delta in updates.items():
pipe.incrby(f"likes:{post_id}", delta)
return pipe.execute()CRDT G-Counter
python
class GCounter:
"""
증가 전용 CRDT 카운터
각 노드는 자신의 카운터만 증가
합산으로 글로벌 값 계산
"""
def __init__(self, node_id: str, nodes: list):
self.node_id = node_id
self.counts = {n: 0 for n in nodes}
def increment(self):
self.counts[self.node_id] += 1
def value(self) -> int:
return sum(self.counts.values())
def merge(self, other: 'GCounter'):
"""다른 노드의 상태와 병합 (최대값 선택)"""
for node_id, count in other.counts.items():
self.counts[node_id] = max(
self.counts.get(node_id, 0), count)
# 사용 예시
node1 = GCounter("n1", ["n1", "n2", "n3"])
node2 = GCounter("n2", ["n1", "n2", "n3"])
node1.increment()
node1.increment()
node2.increment()
# 병합 전: node1.value()=2, node2.value()=1
node1.merge(node2)
print(node1.value()) # 3 (올바른 합계)조회수 카운터 최적화
정확한 카운터 (낮은 TPS):
- 매 요청마다 DB 업데이트
- 분산 락으로 경쟁 조건 방지
근사 카운터 (높은 TPS):
- 각 서버 로컬 카운터 유지
- 주기적 (5초마다) 중앙 집계
- 오차 범위 허용: ±(서버 수 × 로컬 카운터 최대값)
하이브리드:
- Redis로 실시간 집계
- DB에 주기적 영속화