profile image

L o a d i n g . . .

728x90

이번 포스팅에서는 분산환경에서 대규모 쓰기 처리를 지원하는 Cassandra 데이터베이스에 대해 살펴보겠습니다. Cassandra는 데이터를 저장할 때 SSTable을 활용합니다. SSTable은 상업용 데이터베이스에서 데이터를 저장하기 위해 사용되는 B tree와 성격이 많이 다르기 때문에 Cassandra를 이해하기 위해서는 SSTable에 대한 이해가 필요합니다.  SSTable이 궁금하시다면 아래 포스팅을 참고해 주세요. 

 

Chapter 3. Storage and Retrieval

이번 챕터에서는 데이터베이스에 저장되는 데이터의 형태와 저장 방식에 따른 장단점에 대해 살펴보겠습니다. 데이터베이스는 데이터를 효율적으로 저장하고 찾기 위해 인덱스를 활용합니다.

code-run.tistory.com

 

Abstract 

Cassandra는 다음과 같은 특징을 지닙니다. 

  • 분산 저장 시스템(distributed storage system) 
  • 값싼 하드웨어(commodity hardware)를 사용함으로써 대규모 요청을 분산 환경에서 처리
  • 단일 실패 지점을 제거함으로써 HA(high availability)를 제공
  • Full relational data model을 지원하지 않습니다. 
  • Read 성능을 희생하지 않고 write 성능이 좋은 데이터베이스입니다. 

Related Work 

Cassandra를 살펴보기 앞서 왜 Cassandra가 등장했는지와 그리고 이전에 사용되던 분산 저장 시스템에는 어떤 한계가 있는지 살펴보겠습니다. 

Dynamo Database 

Cassandra가 등장하기 이전에 존재한 대표적인 분산 저장 시스템의 개념으로는 Dynamo가 있습니다. Dynamo는 다음과 같은 특징을 지닙니다. 

  • 어떠한 상황에서도 쓰기 작업을 지원합니다. Network partition이 발생하더라도 어떻게든 쓰기 작업을 수행할 수 있는 알고리즘을 사용합니다. 모든 데이터는 해당 데이터의 version과 함께 저장됩니다(vector clock). Version은 추후 데이터의 일관성이 깨졌을 때 일관성을 복구하는 데 사용될 수 있습니다. 
  • 하나의 데이터에 동시에 쓰기(update) 작업이 발생하면 일관성이 깨질 수 있습니다. Dynamo는 일관성을 application level에서 복구할 수 있도록 쓰기 작업 이전에 해당 데이터를 읽습니다. 즉, 모든 쓰기(update)에는 읽기 작업이 수반됩니다. 게시된 논문에서는 이러한 작동 방식이 대량의 쓰기 요청을 처리할 때 제한이 될 수 있음을 강조합니다.

Dynamo != DynamoDB

AWS에서 Dynamo라는 개념을 소개했지만 우리가 AWS에서 사용할 수 있는 DynamoDB는 Dynamo paper에서 소개한 개념을 정확히 반영하지 않았습니다. Dynamo의 vector clock은 실용적인 측면에서 바라봤을 때 conflict가 발생했음을 알리는 것 뿐이지 conflict를 어떻게 해결할 수 있는지를 알려주지 못합니다. 따라서 DynamoDB는 vector clock을 활용하지 않습니다. 실제로 DynamoDB의 조회 API를 확인해보면 vector clock의 정보를 포함하지 않는 단일 데이터만 제공하는 것을 확인할 수 있습니다.

 

Dynamo (storage system) - Wikipedia

From Wikipedia, the free encyclopedia Cloud-based service Dynamo is a set of techniques that together can form a highly available key-value structured storage system[1] or a distributed data store.[1] It has properties of both databases and distributed has

en.wikipedia.org

Relational Database 

관계형 데이터베이스는 높은 일관성을 보장해야하기 때문에 쓰기 작업을 처리하는데 오래 걸릴 수 있습니다. 만약 데이터베이스 클러스터의 일부 노드가 죽으면 일관성을 보장할 수 없기 때문에 전체 클러스터에서 쓰기 작업을 할 수 없는 상황이 발생할 수 있습니다. 이외에도 network partition으로 인해 데이터가 충분히 복제되지 않을 때 쓰기 작업이 실패하는 등 대규모 쓰기가 발생하는 환경에서 사용하기 어려운 점이 많습니다. 

 

Data Model 

Cassandra는 NoSQL의 column-family 데이터베이스입니다(칼럼형 데이터베이스 전혀 다른 개념입니다). Cassandra를 구성하는 데이터 모델은 다음과 같습니다. 

  • Row: RDBMS의 row와 유사한 개념입니다. Row는 row key를 통해 식별됩니다. 
  • Column: RDBMS의 column과 유사한 개념입니다. 
  • Column Family: 유사한 column 구조를 가진 row를 담는 컨테이너입니다. 

하지만 Cassandra의 version 3.0 이후에는 column family의 개념이 사라지고 keyspace와 table이라는 개념이 새로 등장하였습니다. 

  • Keyspace: 데이터를 논리적으로 분할하기 위한 최상위 단위입니다. 데이터의 복제 수, 복제 전략 그리고 동일 keyspace의 데이터 노드 간 통신을 제어하는 권한 등 속성에 대한 정보를 포함합니다. Keyspace는 테이블과 테이블의 데이터를 포함하는 논리적인 단위입니다. 
  • Table: 하나의 테이블은 하나의 colum family에 포함됩니다. 테이블은 primary key에 의해 분할됩니다. 

 

System Architecture 

Cassandra는 다음과 같은 특징이 있습니다. 

  • 확장성 (scalability) 
  • 요청을 분산할 수 있는 견고한 설계(robust solution for load balancing)
  • 실패 감지(failure detection) 
  • 복제 및 데이터 동기화(replica synchronization) 
  • 그 외) state transfer, concurrency and job scheduling, request routing 등 

Read / Write 

모든 읽기와 쓰기 요청은 Cassandra의 모든 노드로 전달될 수 있습니다. 해당 노드는 요청을 처리할 수 있는 노드로 요청을 전달합니다. 

  • 읽기 요청: 정족수(quorum) 이상의 노드로부터 응답을 받았을 때 클라이언트에게 값을 반환합니다. 
  • 쓰기 요청: 정족수(quorum) 이상의 노드로부터 쓰기 작업이 완료됐음을 응답받았을 때 클라이언트에게 쓰기 작업이 성공적으로 이뤄졌음을 알립니다. 

Partitioning 

Cassandra 클러스터에는 언제든 노드가 다운되고 새로 추가될 수 있습니다. 따라서 노드의 추가 및 삭제에 따라 발생하는 영향이 최소화돼야 합니다. Cassandra는 이를 위해 consistent hashing을 활용합니다. Consistent hashing을 사용하는 이유와 동작원리는 아래 포스팅을 참고해 주세요. 

 

[Database] DBA급 개발자로 - #21 Distributed Database 1/3

이전 포스팅에서 로깅을 활용해서 시스템 장애 시 데이터베이스를 어떻게 복구하는지 살펴봤습니다. 이번 포스팅에서는 분산 데이터베이스에 대해 살펴보겠습니다. Distributed Database 데이터베이

code-run.tistory.com

하지만 기본 consistent hashing 알고리즘은 다음과 같은 문제가 있습니다. 

  • 각 노드는 hash ring의 랜덤 한 자리에 위치합니다. 따라서 부하가 모든 노드로 균등하게 나뉘지 않을 수 있습니다.
  • 기본 알고리즘은 각 노드의 스펙을 고려하지 않습니다. 성능이 매우 다른 노드가 부하를 동일하게 나눠가질 수 있습니다. 

위에서 설명한 기본 consistent hashing 알고리즘의 단점을 극복하기 위해 Cassandra는 consistent hashing을 약간 변형하여 사용합니다. 

  • 하나의 노드를 hash ring의 여러 곳에 배치합니다(virtual node). 이는 Dynamo DB에서 활용하는 consistent hashing 방식과 유사합니다. 
  • Hash ring에서 부하가 작은 노드의 위치를 부하가 큰 노드의 위치로 이동시킴으로써 부하를 분산합니다. 

Replication 

복제 수(replication factor)는 노드별로 설정이 가능합니다. 데이터 처리 요청이 처음 들어왔을 때 hash ring에서 해당 요청을 처리해야 하는 노드를 coordinator node라고 부릅니다. Coordinator은 설정에 따라 어떤 노드에 데이터를 복제할지 결정합니다(Rack unware, rack aware, datacenter aware) 

Local Persistence 

데이터는 local filesystem을 활용해 저장합니다. 쓰기 요청은 다음과 같은 순서로 처리됩니다. 

  1. Commit log에 데이터를 기록합니다(log append 방식)
  2. In-memory 자료구조에 쓰기 작업을 반영합니다. 
  3. In-memory 자료구조의 크기가 threshold를 넘어가면 디스크로 플러쉬 됩니다. 

읽기 요청은 다음과 같은 순서로 처리됩니다. 

  1. In-memory 자료구조를 확인합니다. 있는 경우 데이터를 반환합니다. 
  2. 디스크에 저장된 files(SSTables)을 읽습니다. 가장 최근에 생성된 순서로 file을 읽습니다. 

데이터가 in-memory 자료구조와 file 모두에 없는 경우 읽기 요청을 처리하는데 오래 걸릴 수 있습니다(메모리와 디스크를 모두 확인해야 하기 때문입니다). 존재하지 않는 데이터에 대한 읽기 요청을 효과적으로 필터링하기 위해 bloom filter을 활용합니다. 

 

Cassandra 사용해 보기 

최신 Cassandra는 "brew install cassandra"를 통해 설치할 수 있습니다. 데이터를 저장하기에 앞서 keyspace와 table을 생성해야 합니다.

Cassandra keyspace(local) and table(employee, user)

테이블이 생성된 이후 로컬 파일시스템을 확인해 보면 다음과 같이 테이블별로 디렉터리가 생성됩니다. 

테이블이 디렉터리로 관리됨

하지만 데이터 insert 이후 해당 디렉터리를 아무리 확인해도 데이터가 보이지 않습니다. 이는 Cassandra가 in-memory 자료구조에 데이터를 보관하고 특정 threshold를 넘어야 디스크에 저장하기 때문입니다. 따라서 in-memory 데이터를 디스크로 저장하기 위해서는 별로의 조작이 필요합니다. 

nodetool flush local employee

위 커맨드 실행 이후 employee 테이블을 관리하는 디렉터리 밑에 아래 파일들이 생성됐습니다. 

해당 파일은 Cassandra가 생성한 SSTable 데이터를 보관합니다. Cassandra를 설치하며 함께 설치된 sstabledump 명령어를 통해 어떤 데이터가 저장됐는지 확인할 수 있습니다. 

sstabledump <file-name>

결과는 다음과 같습니다. 

[
  {
    "partition" : {
      "key" : [ "Sales" ], 
      "position" : 0 
    },
    "rows" : [ 
      {
        "type" : "static_block", 
        "position" : 32, 
        "cells" : [ 
          { "name" : "manager", "value" : "Brad", "tstamp" : "2023-04-21T13:24:36.385Z" }
        ]
      },
      {
        "type" : "row",
        "position" : 32,
        "clustering" : [ 301 ], 
        "liveness_info" : { "tstamp" : "2023-04-21T13:24:36.385Z" }, 
        "cells" : [
          { "name" : "name", "value" : "Emily" },
          { "name" : "salary", "value" : 75000.00 }
        ]
      }
    ]
  }......
]

 

마무리

이번 포스팅을 통해 SSTable을 활용해 구현된 Cassandra에 대해 살펴봤습니다. 쓰기 작업을 처리하기 위해 commit log에 로그를 추가하고 in-memory 자료구조에 변경을 반영하는 작업만 필요하기 때문에 쓰기 성능이 좋을 것 같습니다. 하지만 이후 생성되는 SSTable과 이를 compaction 하는 수행하는 과정에서 발생하는 오버헤드가 Cassandra의 쓰기 성능에 영향을 미칠 수 있기 때문에 쓰기 성능이 얼마나 좋은지는 다른 데이터베이스와 비교해보지 않는 이상 판단하기가 어려울 것 같습니다.

Reference 

https://www.cs.cornell.edu/projects/ladis2009/papers/lakshman-ladis2009.pdf

728x90

'논문' 카테고리의 다른 글

분산 환경의 합의 알고리즘 Paxos  (0) 2023.04.13
LSM-Tree는 왜 사용할까  (0) 2023.04.11
복사했습니다!