Database/Cassandra

Cassandra db - 데이터 모델링 - Logical 데이터 모델링

소농배 2022. 2. 7. 20:33

 쿼리를 정의했다면, 카산드라 테이블을 설계한 준비가 된 것이다. 첫번째로 각 쿼리에 포함되어있는 논리 모델을 포함하는 테이블을 만들고 구상중인 모델의 엔티티와 관계를 그린다.

 

 각 테이블의 이름을 지정하려면 주로 조회하는 엔티티 이름으로 시작하는 이름을 지을 수 있다. 다른 관계가 있는 에티티의 요소로 Query 를 한다면 이 두가지 테이블의 이름을 붙이고 by 로 구분을 지을 수 있다. 예를 들어 hotels_by_poi

 

다음에 테이블의 PK 를 결정하고 추가 Parititon Key 를 Query 요소를 기반으로 추가하고 Clustering 요소를 고유성과 필요한 정렬을 제공하기 위해 추기해야한다.

 

 PK 를 설계하는 것은 특히 중요하다. PK 가 각 Partition 에 어떻게 저장될지 그리고 Disk 에 어떻게 정리될지, 카산드라가 얼마나 빨리 Read 를 수행할지를 결정하기 때문이다.

 

 이제 여기까지는 상당히 복잡한 프로세스에 대한 매우 빠른 설명이었기 때문에 자세한 예제를 통해 작업하는 것이 더 나을 것이다. 첫번째로 논리적 모델을 나태나는데 표현할 수 있는 표시를 소개한다.

 

 카산드라 커뮤니티의 몇몇 개인들은 다이어그램으로 표현할 수 있는 데이터 모댈을 제안하였다. 이 문서는 Artem Chebotko 에 의해 알려진 것이다.

  각 테이블은 제목과 Column 리스트를 나타낸다. PK Column 의 Parititon Key 는 K 심볼로 식별될 수 있다. Clustering Key Column 은 C 로 구분된다. 선은 테이블에 진입점을 나타내거나 테이블간의 쿼리를 가리킨다

 

Hotel Logical Data Model

호텔, 관광지, 객실, 어메니티를 포함한 논리 데이터 모델은 아래와 같다. 주목할만한 점은 카산드라 설계에서 객실과 어메니티만을 위한 테이블은 포함되어있지 않은 것이다. Workflow 를 정의할때 각 테이블에 직접적으로 접근이 필요한 Query 가 나타나지 않았기 때문이다. 

첫번째 쿼리(Q1)는 관광지 근처에 있는 호텔을 찾는 것이므로 이름이 hotels_by_poi 이다. 관광지의 이름으로 찾는것은 관광지 이름이 PK 의 하나가 되어야 한다는 것이다. 관광지 이름으로 찾는 것을 찾고하자 왜냐햐면 Workflow 에 따르면 유저가 검색을 시작하는 시작점이기 때문이다. 

 

 관광지 하나에 여러개의 인접한 호텔들이 있다. 따라서 각 호텔별로 유일한 Parititon 을 갖도록 하기 위해 PK 에 또 다른 요소가 필요하다. 따라서 Hotel id 를 Clustering Column 의 하나로 추가한 것이다.

 

  PK 를 설계할때 주의할 점은 유일한 데이터 요소를 정의한다는 것이다. 그렇지 않으면 실수로 데이터를 덮어쓸 위험이 있다. 

 

 두번째 쿼리(Q2)는 특정 호텔의 정보를 얻어와야한다. 호텔의 모든 정보를 pois_by_hotel 테이블에 넣는 것이 하나의 방법이 될 수 있다. 그러나 Application 의 Workflow 에서 필요한 요소들만 추가했다.

 

 Workflow 다이어그램에서, 우리는 hotels_by_poi 테이블은 관광지 근처에 있는 호텔 리스트를 보여주기 위해 만든 테이블이다. 그리고 Application 은 각 호텔의 식별자를 알 수 있다. 유저가 자세한 정보를 알 기 위해 호텔을 클릭했을때, 우리는 Q2 를 사용하고, 호텔의 자세한 정보를 얻을 수 있다. 왜냐하면 우리는 hotel_id 를 Q1 으로부터 얻어서 가지고 있기 때문에, 호텔 정보를 얻기 위해 이 것을 참조할 수 있다. 그래서 두번째 테이블은 hotels 라고 이름 붙여졌다.

 

 또 다른 선택지 중의 하나는 poi_names 집합을 hotels 테이블에 추가하는 것이다. 이 것은 동일하게 유효한 접근이다. 어떤 접근이 우리의 Application 에 최적의 선택일지 알게 될 것이다. 

 

 Q3 는 Q1 의 반대로 호텔 근처에 있는 관광지를 찾는 것이다. 그러나 이번에는 각 관광지의 자세한 정보 접근이 필요하여 pois_by_hotel 로 표현되었다. 이전과 마찬가지로 유일성을 보장하기 위해 관광지 이름을 Clustering Key 로 추가하였다.

 

 이 관점에서 사용자가 원하는 호텔을 예약할 수 있는지를 확인할 수 있도록 Q4 를 지원해보자. 이 쿼리는 시작 날짜와 마지막 날짜를 포함하고있다. 단일 날짜로 조회하는 것이 아니라 기간으로 조회해야하기 때문에 날짜를 Clustering Key 로 사용해야한다. hotel_id 를 Parititon Key 로 지정하여 같은 호텔의 객실들은 단일 Parititon 으로 되도록 하여 조회 성능을 매우 빠르게 할 수 있다. 이 테이블을 available_rooms_by_hotel_date 라고 부른다.

 

 범위로 조회할 수 있도록, clustering columns <clustering-columns> 를 사용하여 범위로 조회할 수 있도록 하였다. Clustering Column 의 순서기 중요하다는 것을 강조한다.

 

 available_rooms_by_hotel_date 테이블의 설계가 Wide Partition 패턴의 예이다. 이 패턴은 비슷한 모델을 지원하기위한 논의에서 Wide Row 라고도 불린다. 하지만 Wide Parititon 은 카산드라 관점에서 더 정확한 설명이다. 이 패턴의 본질은 단일 Query 에서 parititon 안에서 빠르게 여러개의 Row 에 접근할 수있도록 한다.

 

 데이터 모델의 쇼핑 부분을 풍성하게 하기 위하여 amentities_by_room 테이블을 Q5 를 지원하기 위하여 추가한다. 이것은 사용 가능한 한 객실의 어메니티를 확인할 수 있도록 해준다.

 

Reservation Logical Data mode

 예약을 위한 Query 에 집중해보자. 아래 도표는 예약을 위한 논리 데이터 모델을 보여준다. 이 테이블들은 비정규화 설계를 나타낸다. 동일한 데이터가 여러 테이블에 나타난다.

 Q6 를 만족하기 위하여 reservations_by_guest 테이블은 고객 이름으로 예약을 조회할 수 있다. 고객이 웹사이트에서 본인의 이름으로 예약을 조회하거나 고객 센터에서 고객을 돕기 위해 사용되는 것을 상상할 수 있다. 고객 이름은 유일하지 않기 때문에 guest id 를 clustering column 에 추가하였다.

 

 Q8 과 Q9 는 특히 다양한 Application 의 stakeholders 뿐만 아니라 고객이 아닌 직원 그리고 아마도 분석 시스템도 이 쿼리들을 사용할 수 있다는 것을 상기할 수 있다. 

 

 호텔 직원은 다가오는 예약을 날짜 별로 확인하고 싶을 것이다. Q8 은 날짜별로 호텔의 예약을 확인할 수 있도록 한다.

 

 마침내, guests 테이블을 생성한다. 이 테이블은 고객 정보를 저장하는 단일 저장소를 제공한다. 이 경우에 고유 식별자를 추가하여 구분한다. 많은 기업에서 guests 테이블과 같은 고객 데이터베이스는 별도의 고객 관리 Application 의 일부가 되므로 다른 guest 접근 패턴은 예제에서 생략되었다.

 

Reservation Logical Data mode

 다른 소프트웨어 설계와 마찬가지로 카산드라 모델링에 잘 알려진 Pattern 과 Anti-Pattern 이 존재한다. 호텔 모델에서 가장 흔한 패턴중의 하나인 Wide Partition Pattern 을 사용하였다.

 

 Time Series 패턴은 Wide Paritition Pattern 의 확장이다. 이 패턴에서는 특정 시간 간격의 일련의 측정값이 Parititon Key 의 일부로 Wide Parititon 에 저장된다. 이 패턴은 주로 비지니스 분석, 센서 데이터 관리, 과학 실험 도메인에 주로 사용된다.

 

Time Series 패턴은 측정 외의 데이터에도 유횽하다. 은행 Application 예제를 고려해보자. 각 고객의 이자를 Row 로 저장해야한다. 그러나 다양한 고객이 그들의 이자를 확인하거나 트랜잭션을 만들어냄으로서 많은 Read 와 Write 가 발생할 수 있다. 당신은 아마도 Write 시에 발생할 수 있는 에러로 부터 데이터를 보호하기 위해 트랜잭션으로 감쌀것이다. 대조적으로 Time Series 스타일 설계는 각 트랜잭션을 타임스탬프 Row 로 저장하고 이자를 계산하는 것은 Application 에 위임한다.

 

 많은 사용자들이 빠지는 함정은 카산드라를 Queue 로 사용하려는 시도이다. Wide Parititon 에서 각 아이템들은 Timestamp 와 함께 저장된다. 아이템들은 큐의 마지막에 이어지고 앞에서 부터 Read 된다. Read 된 후에는 제거된다. 이 것은 매력적인 설계로 보인다. 문제는 이 접근의 아이템 삭제는 현재 tombstones <async-deletes> 로 카산드라는 Queue 앞 부분을 Read 하기 위하여 과거 데이터를 scan 해야한다는 것이다. 

 

 Queue Anti-Pattern 은 데이터 삭제에 의존하는 어떠한 설계든지 낮은 성능의 설계 가능성이 있다.