Active-Active 데이터베이스의 리스트
Active-Active 데이터베이스의 리스트 (Lists in Active-Active databases) (lists-3)
Redis 공식 문서의 lists-3 페이지를 한국어로 풀어드릴게요. 옆에서 하나씩 설명해 드리는 느낌으로 읽으시면 돼요.
Active-Active 데이터베이스에서 리스트(List) 데이터 구조를 다루는 방법을 알려드릴게요. Active-Active 데이터베이스의 리스트는 일반 Redis 리스트와 똑같아요. 다만 동시 쓰기가 일어날 때의 동작을 잘 알아둬야 해요. 예시를 따라가며 하나씩 살펴볼게요.
리스트 기본 개념
Redis 리스트는 단순히 삽입 순서대로 정렬된 문자열들의 목록이에요. 리스트의 머리(왼쪽, head)나 꼬리(오른쪽, tail)에 새 요소를 밀어 넣을 수 있어요. 큐(queue)는 LPUSH와 RPOP으로, 스택(stack)은 LPUSH와 LPOP으로 쉽게 구현할 수 있죠.
단순 리스트 예시
| 시간 | CRDB Instance 1 | CRDB Instance 2 |
|---|---|---|
| t1 | LPUSH mylist "hello" |
|
| t2 | — Sync — | — Sync — |
| t3 | LPUSH mylist "world" |
|
| t4 | — Sync — | — Sync — |
| t5 | LRANGE mylist 0 -1 => "world" "hello" |
LRANGE mylist 0 -1 => "world" "hello" |
설명: 최종 리스트에는 "world"와 "hello" 두 요소가 그 순서대로 들어 있어요. Instance 2가 "world"를 추가할 때 "hello"를 관찰했기 때문에 그 뒤에 붙은 거예요.
동시 삽입 (Concurrent Insertions) 예시
| 시간 | CRDB Instance 1 | CRDB Instance 2 |
|---|---|---|
| t1 | LPUSH L x |
|
| t2 | — Sync — | — Sync — |
| t3 | LINSERT L AFTER x y1 |
|
| t4 | LINSERT L AFTER x y2 |
|
| t5 | LRANGE L 0 -1 => x y1 |
LRANGE L 0 -1 => x y2 |
| t6 | — Sync — | — Sync — |
| t7 | LRANGE L 0 -1 => x y1 y2 |
LRANGE L 0 -1 => x y1 y2 |
설명: Instance 1이 x 뒤에 y1을, Instance 2가 x 뒤에 y2를 추가했어요. 최종 리스트에는 세 요소가 들어 있어요. x가 첫 요소이고, 그 뒤에 y1, y2 순서죠. Active-Active 데이터베이스는 충돌을 임의로 해결하지만, 그 해결은 모든 Active-Active 인스턴스에 일관되게 적용돼요.
리스트 삭제하면서 새 요소 밀어 넣기 예시
| 시간 | CRDB Instance 1 | CRDB Instance 2 |
|---|---|---|
| t1 | LPUSH L x |
|
| t2 | — Sync — | — Sync — |
| t3 | LRANGE L 0 -1 => x |
LRANGE L 0 -1 => x |
| t4 | LPUSH L y |
DEL L |
| t5 | — Sync — | — Sync — |
| t6 | LRANGE L 0 -1 => y |
LRANGE L 0 -1 => y |
설명: t4~t6에서 DEL은 관찰된 요소만 삭제해요. 그래서 L에는 여전히 y가 남아 있어요. 다른 인스턴스가 추가한 y는 이 인스턴스가 아직 "보지" 못했으니 삭제되지 않죠.
리스트에서 요소 Pop 예시
| 시간 | CRDB Instance 1 | CRDB Instance 2 |
|---|---|---|
| t1 | LPUSH L x y z |
|
| t2 | — Sync — | — Sync — |
| t3 | RPOP L => x |
|
| t4 | — Sync — | — Sync — |
| t5 | RPOP L => y |
|
| t6 | — Sync — | — Sync — |
| t7 | RPOP L => z |
RPOP L => z |
설명: t1에서 x, y, z를 L에 밀어 넣었어요. t3에서는 큐에서 기대되는 대로 순차 pop이 동작해요. t7의 동시 pop은 두 인스턴스 모두 같은 결과(z)를 보여줄 수 있어요. z 제거에 대해 동기화가 되지 않아서, 각 인스턴스 관점에서는 z가 리스트에 있어 pop할 수 있는 상태인 거예요. 동기화 후 두 리스트 모두 비워져요.
주의할 점 (스택/큐로 쓸 때)
리스트를 스택이나 큐로 쓸 때 Active-Active 동작을 잘 알아둬야 해요. 위 예시에서 보듯, 두 인스턴스가 수행한 두 개의 병렬 RPOP 연산은 동시 연산의 경우 같은 요소를 가져올 수 있어요. Active-Active 데이터베이스의 리스트는 각 요소가 적어도 한 번은 POP되는 걸 보장하지만, 정확히 한 번만 POP되는 걸 보장하지는 못해요. 이런 동작은 예를 들어 리스트를 프로세스 간 통신(IPC) 시스템의 구성 요소로 쓸 때 반드시 고려해야 해요.
같은 요소를 애플리케이션이 두 번 처리하면 안 되는 상황이라면, POP 연산은 하나의 Active-Active 인스턴스에서만 수행하고, PUSH 연산은 여러 인스턴스에서 수행하는 것을 권장해요.
더 알아보기 (Learn more)
Active-Active 데이터베이스의 리스트에 대해 더 알아보고 싶다면 아래를 확인해 보세요.