Active-Active 데이터베이스의 JSON
Active-Active 데이터베이스의 JSON (JSON in Active-Active databases) (json-3)
Redis 공식 문서의 json-3 페이지를 한국어로 풀어드릴게요. 옆에서 하나씩 설명해 드리는 느낌으로 읽으시면 돼요.
Active-Active 데이터베이스에서 JSON 데이터 구조를 다루는 방법을 알려드릴게요. Active-Active 데이터베이스는 JSON 데이터 구조를 지원해요. 설계는 Kleppmann과 Beresford의 A Conflict-Free Replicated JSON Datatype 논문을 기반으로 하지만, 구현에는 몇 가지 변경이 있어요. 여러 충돌 해결 규칙 예시도 이 논문에서 가져왔어요.
사전 요구사항 (Prerequisites)
Active-Active 데이터베이스에서 JSON을 쓰려면 데이터베이스 생성 시 JSON을 활성화해야 해요.
- Redis Cloud Active-Active 데이터베이스는 기본적으로 JSON을 추가해요. 자세한 내용은 Redis Cloud 문서의 Create an Active-Active subscription을 참고하세요.
- Redis Software에서는 버전 8 이상으로 만들었거나 업그레이드한 Active-Active 데이터베이스가 자동으로 JSON을 활성화해요. 더 이른 Redis 버전은 Redis Stack/Redis Software 문서의 Create an Active-Active JSON database를 참고하세요.
명령 차이 (Command differences)
일부 JSON 명령은 Active-Active 데이터베이스에서 다르게 동작해요.
JSON.CLEAR
JSON.CLEAR은 JSON 배열과 객체를 초기화해요. Active-Active 데이터베이스에서 서로 다른 인스턴스가 JSON 문서에 동시에 업데이트하는 것을 지원하고, 결과가 병합되도록 해줘요.
충돌 해결 규칙 (Conflict resolution rules)
Active-Active 데이터베이스에서는 두 인스턴스가 동시에 같은 데이터에 쓰기 연산을 시도할 수 있어요. replica들이 이 변경들을 서로 동기화하려고 하면 충돌이 생길 수 있죠. 충돌 해결 규칙은 데이터베이스가 충돌하는 연산을 어떻게 처리할지 결정해요.
충돌 해결에는 두 가지 유형이 있어요.
- Merge (병합):
- 연산들이 결합 법칙(associative)을 만족.
- 두 연산의 결과를 병합.
- Win over (승리):
- 연산들이 결합 법칙을 만족하지 않음.
- 한 연산이 충돌에서 이겨 값을 설정.
- 진 쪽 연산은 무시.
충돌 해결 규칙 요약
| 충돌 상황 | 해결 유형 | 해결 규칙 |
|---|---|---|
| 키에 다른 타입 할당 | Win over | ID가 더 작은 인스턴스가 이김 |
| create vs create | Win over | ID가 더 작은 인스턴스가 이김 |
| create vs update | Win over | 새 문서를 생성하는 연산이 이김 |
| delete vs create | Win over | 문서 생성이 삭제를 이김 |
| delete vs update | Win over | 문서 삭제가 업데이트를 이김 |
| update vs update | Win over | ID가 가장 작은 인스턴스가 이김 |
| update vs clear (RedisJSON < 2.2) | Win over | 문서 생성이 업데이트를 이김 (빈 객체 할당의 경우) |
| pop vs update | Win over | 삭제가 업데이트를 이김 |
| update vs update object | Merge | 객체에 대한 모든 연산 결과를 병합 |
assign different types (서로 다른 타입 할당)
두 인스턴스가 같은 키에 서로 다른 타입의 값을 동시에 할당하는 충돌이에요. 예를 들어 Instance 1은 객체(object)를, Instance 2는 배열(array)을 같은 키에 할당해요. ID가 더 작은 인스턴스가 이기므로, 예시에서는 키가 객체가 돼요.
| 시간 | 설명 | Instance 1 | Instance 2 |
|---|---|---|---|
| t1 | 같은 키를 객체 또는 배열로 설정 | JSON.SET doc $.a '{}' |
JSON.SET doc $.a '[]' |
| t2 | 객체와 배열에 데이터 추가 | JSON.SET doc $.a.x '"y"' → {"a": {"x": "y"}} |
JSON.SET doc $.a '["z"]' → {"a": ["z"]} |
| t3 | Active-Active 동기화 | – Sync – | – Sync – |
| t4 | Instance 1이 이김 | JSON.GET doc $ → {"a": {"x": "y"}} |
JSON.GET doc $ → {"a": {"x": "y"}} |
update versus clear (update vs clear) — RedisJSON 2.2 이전
RedisJSON v2.2 이전 버전에서는 JSON 객체의 내용을 초기화하는 방법이 두 가지였어요.
- 새 빈 JSON 객체를 할당:
JSON.SET doc $.colors '{}'— 이 방법은 동시 업데이트와 병합할 수 없어요. - 키마다
JSON.DEL로 제거:JSON.DEL doc $.colors.blue— 이 방법은 초기화를 동시 업데이트와 병합할 수 있어요.
RedisJSON v2.2부터는 JSON.CLEAR 명령으로 각 키를 수동으로 제거하지 않고도 JSON 문서를 초기화할 수 있어요. 이 방법도 동시 업데이트를 병합할 수 있죠.
빈 객체 할당 (Assign an empty object) — v2.2 이전
Instance 1이 기존 "colors" 객체에 "red"를 추가하고, Instance 2가 "colors"에 새 빈 객체를 할당하는 충돌이에요. 문서 생성이 업데이트를 이기므로 결과는 빈 객체가 돼요.
| 시간 | 설명 | Instance 1 | Instance 2 |
|---|---|---|---|
| t1 | 두 인스턴스에 문서 존재 | JSON.GET doc $ → {"colors": {"blue": "#0000ff"}} |
JSON.GET doc $ → {"colors": {"blue": "#0000ff"}} |
| t2 | Instance 1은 새 색상 추가, Instance 2는 빈 객체로 초기화 | JSON.SET doc $.colors.red '#ff0000' |
JSON.SET doc $.colors '{}' |
| t3 | Instance 2가 새 색상 추가 | JSON.SET doc $.colors.green '#00ff00' |
|
| t4 | JSON.GET doc $ → {"colors": {"blue": "#0000ff", "red": "#ff0000"}} |
JSON.GET doc $ → {"colors": {"green": "#00ff00"}} |
동기화 후에는 Instance 1의 값(객체 생성)이 이겨서 최종적으로 빈 객체가 유지되는 식으로 해석됩니다. 참고: 원문 표기상 이 규칙 예시는 버전에 따라 결과 해석이 달라질 수 있어 확인 필요해요.
pop versus update (pop vs update)
Instance 1이 배열에서 요소를 제거하고, Instance 2가 제거된 요소를 업데이트하는 충돌이에요. 삭제가 업데이트를 이겨요.
| 시간 | 설명 | Instance 1 | Instance 2 |
|---|---|---|---|
| t1 | 두 인스턴스에 문서 존재 | JSON.GET doc $ → {"todo": [{"title": "buy milk", "done": false}]} |
JSON.GET doc $ → {"todo": [{"title": "buy milk", "done": false}]} |
| t2 | Instance 1은 배열 요소 제거, Instance 2는 같은 요소 업데이트 | JSON.ARRPOP doc $.todo 0 |
JSON.SET doc '$.todo[0]["done"]' 'true' |
| t3 | JSON.GET doc $ → {"todo": []} |
JSON.GET doc $ → [{"title": "buy milk", "done": true}] |
|
| t4 | Active-Active 동기화 | – Sync – | – Sync – |
| t5 | Instance 1이 이김 | JSON.GET doc $ → doc = {"todo": []} |
JSON.GET doc $ → doc = {"todo": []} |
update versus update object (객체 업데이트 vs 객체 업데이트)
두 인스턴스가 같은 기존 객체를 서로 다른 내용으로 업데이트하는 충돌이에요. 이 경우에는 Merge로 해결돼요. 객체에 대한 모든 연산 결과를 병합하죠.
| 시간 | 설명 | Instance 1 | Instance 2 |
|---|---|---|---|
| t1 | 두 인스턴스에 문서 존재 | JSON.GET doc $ → '{"grocery": []}' |
JSON.GET doc $ → '{"grocery": []}' |
| t2 | 배열에 새 요소 추가 | JSON.ARRAPPEND doc $.grocery '"eggs"' |
JSON.ARRAPPEND doc $.grocery '"milk"' |
| t3 | 배열에 새 요소 추가 | JSON.ARRAPPEND doc $.grocery '"ham"' |
JSON.ARRAPPEND doc $.grocery '"flour"' |
| t4 | JSON.GET doc $ → {"grocery":["eggs", "ham"]} |
JSON.GET doc $ → {"grocery":["milk", "flour"]} |
|
| t5 | Active-Active 동기화 | – Sync – | – Sync – |
| t6 | 두 인스턴스의 결과 병합 | JSON.GET doc . → {"grocery":["eggs","ham","milk", "flour"]} |
JSON.GET doc . → {"grocery":["eggs","ham","milk", "flour"]} |
이렇게 객체 안의 요소들은 merge로 합쳐지고, 단순 필드 값은 win over로 결정되는 식으로 Active-Active 데이터베이스가 JSON 충돌을 일관되게 처리해요.
더 알아보기 (Learn more)
Active-Active 데이터베이스의 JSON에 대해 더 배우고 싶다면 아래를 참고하세요.