RDBMS 설계
RDBMS 설계 (RDBMS design)
관계형 데이터베이스를 사용할 새 데이터 중심 애플리케이션을 만들기 시작할 때, 도메인을 적절히 정규화된 테이블 집합으로 모델링하고 외래 키를 사용해 다른 테이블의 관련 데이터를 참조하는 것부터 시작할 수 있어요.
출처: RDBMS design
본문
아래 그림은 관계형 데이터베이스 모델을 사용해 애플리케이션의 데이터 저장을 표현하는 방법을 보여줘요. 관계형 모델은 호텔-관심 지점, 객실-편의 시설, 객실-가용성, 손님-객실(예약 경유)의 개념적 모델에서 다대다(many-to-many) 관계를 실현하기 위해 여러 "조인(join)" 테이블을 포함해요.

RDBMS와 Cassandra의 설계 차이
Cassandra용 데이터 모델링과 관계형 데이터베이스용 데이터 모델링의 주요 차이점 몇 가지를 짚어볼게요.
조인 없음 (No joins)
Cassandra에서는 조인을 수행할 수 없어요. 데이터 모델을 설계했는데 조인 같은 것이 필요하다는 것을 발견했다면, 클라이언트 측에서 작업을 수행하거나 조인 결과를 나타내는 비정규화된 두 번째 테이블을 만들어야 해요. 후자의 옵션이 Cassandra 데이터 모델링에서 선호돼요. 클라이언트에서 조인을 수행하는 것은 매우 드문 경우여야 해요. 대신 데이터를 중복(비정규화)하는 것이 정말로 필요해요.
참조 무결성 없음 (No referential integrity)
Cassandra는 경량 트랜잭션과 배치 같은 기능을 지원하지만, Cassandra 자체에는 테이블 간 참조 무결성 개념이 없어요. 관계형 데이터베이스에서는 다른 테이블의 레코드 기본 키를 참조하는 외래 키를 테이블에 지정할 수 있어요. 하지만 Cassandra는 이를 강제하지 않아요. 테이블에 다른 개체와 관련된 ID를 저장하는 것은 여전히 일반적인 설계 요구 사항이지만, 연쇄 삭제(cascading deletes) 같은 연산은 사용할 수 없어요.
비정규화 (Denormalization)
관계형 데이터베이스 설계에서는 정규화의 중요성을 자주 배워요. 이는 Cassandra 작업에서는 장점이 아니에요. Cassandra는 데이터 모델이 비정규화될 때 가장 좋은 성능을 내기 때문이에요. 회사들이 관계형 데이터베이스에서도 데이터를 비정규화하는 경우가 종종 있어요. 두 가지 일반적인 이유가 있어요. 하나는 성능이에요. 수년치 데이터에 대해 너무 많은 조인을 해야 할 때 필요한 성능을 얻지 못해, 알려진 쿼리 방향으로 비정규화해요. 이는 결국 동작하지만 관계형 데이터베이스가 의도된 설계 방식과는 상충되며, 궁극적으로 이런 상황에서 관계형 데이터베이스를 사용하는 것이 최선인지 의문을 갖게 해요.
관계형 데이터베이스가 의도적으로 비정규화되는 두 번째 이유는 보존이 필요한 비즈니스 문서 구조 때문이에요. 즉, 시간이 지나면 변경될 수 있는 많은 외부 테이블을 참조하는 포함(감싸는) 테이블이 있지만, 포함 문서를 역사 속의 스냅샷으로 보존해야 해요. 일반적인 예는 인보이스(청구서)예요. 이미 고객과 제품 테이블이 있는데, 그 테이블을 참조하는 인보이스를 만들면 될 것 같다고 생각할 수 있어요. 하지만 실제로는 이렇게 해서는 절대 안 돼요. 고객이나 가격 정보가 변경되면 인보이스 날짜 당시의 인보이스 문서 무결성을 잃게 되어, 감사, 보고서, 법률을 위반하고 다른 문제를 일으킬 수 있어요.
관계형 세계에서 비정규화는 Codd의 정규형을 위반하므로 피하려고 해요. 하지만 Cassandra에서 비정규화는, 음, 완전히 정상적인 것이에요. 데이터 모델이 단순하다면 필수는 아니에요. 하지만 두려워하지 마세요.
역사적으로 Cassandra의 비정규화는 이 문서에 설명된 기법을 사용해 여러 테이블을 설계하고 관리하는 것을 요구했어요. 3.0 릴리스부터 Cassandra는 materialized views라는 기능을 제공하며, 이를 통해 기본 테이블 설계를 기반으로 비정규화된 데이터 뷰를 여러 개 만들 수 있어요. Cassandra는 서버에서 구체화된 뷰를 관리하며, 뷰를 테이블과 동기화 상태로 유지하는 작업을 포함해요.
쿼리 우선 설계 (Query-first design)
간단히 말해 관계형 모델링은 개념적 도메인에서 시작해 도메인의 명사(nouns)를 테이블에 나타내는 것을 의미해요. 그런 다음 관계를 모델링하기 위해 기본 키와 외래 키를 할당해요. 다대다 관계가 있으면 그 키만 나타내는 조인 테이블을 만들어요. 조인 테이블은 실제 세계에는 존재하지 않으며, 관계형 모델이 동작하는 방식의 필연적인 부작용이에요. 모든 테이블을 배치한 후에는 키가 정의한 관계를 사용해 이질적인 데이터를 모으는 쿼리를 작성하기 시작할 수 있어요. 관계형 세계에서 쿼리는 매우 부차적이에요. 테이블을 적절히 모델링하기만 하면 원하는 데이터를 항상 얻을 수 있다고 가정해요. 여러 복잡한 서브쿼리나 조인 문을 사용해야 해도 보통 사실이에요.
대조적으로 Cassandra에서는 데이터 모델부터 시작하지 않고 쿼리 모델부터 시작해요. 데이터를 먼저 모델링하고 그 다음 쿼리를 작성하는 대신, Cassandra에서는 쿼리를 모델링하고 그 주변에 데이터를 구성해요. 애플리케이션이 사용할 가장 일반적인 쿼리 경로를 생각하고, 그것들을 지원하는 데 필요한 테이블을 만들어요.
비판가들은 쿼리를 먼저 설계하는 것이 애플리케이션 설계는 물론 데이터베이스 모델링도 지나치게 제약한다고 제안해 왔어요. 하지만 애플리케이션의 쿼리에 대해 깊이 생각해야 한다고 기대하는 것은 완전히 합리적이에요. 관계형 도메인에 대해 깊이 생각하는 것처럼 말이에요. 틀릴 수도 있고, 그러면 어느 세계에서든 문제가 생겨요. 또는 쿼리 요구가 시간이 지남에 따라 바뀌어 데이터셋을 갱신해야 할 수도 있어요. 하지만 이것은 RDBMS에서 잘못된 테이블을 정의하거나 추가 테이블이 필요해지는 것과 다르지 않아요.
최적 저장을 위한 설계 (Designing for optimal storage)
관계형 데이터베이스에서는 테이블이 디스크에 어떻게 저장되는지가 사용자에게 투명한 경우가 많고, RDBMS가 디스크에 테이블을 어떻게 저장할지에 기반한 데이터 모델링 권장 사항을 듣는 일은 드물어요. 하지만 그것은 Cassandra에서 중요한 고려 사항이에요. Cassandra 테이블은 각각 디스크의 별도 파일에 저장되므로, 관련 컬럼을 같은 테이블에 함께 정의하는 것이 중요해요.
Cassandra에서 데이터 모델을 만들기 시작할 때 보게 될 핵심 목표는 주어진 쿼리를 충족하기 위해 검색해야 하는 파티션 수를 최소화하는 것이에요. 파티션은 노드 간에 분할되지 않는 저장 단위이므로, 단일 파티션을 검색하는 쿼리는 일반적으로 최상의 성능을 내요.
정렬은 설계 결정 (Sorting is a design decision)
RDBMS에서는 쿼리에서 ORDER BY를 사용해 레코드가 반환되는 순서를 쉽게 변경할 수 있어요. 기본 정렬 순서는 구성할 수 없으며, 기본적으로 레코드는 쓰여진 순서로 반환돼요. 순서를 변경하려면 쿼리를 수정하기만 하면 되고, 어떤 컬럼 목록으로도 정렬할 수 있어요.
하지만 Cassandra에서는 정렬이 다르게 취급돼요. 그것은 설계 결정이에요. 쿼리에서 사용 가능한 정렬 순서는 고정되어 있으며, CREATE TABLE 명령에서 제공하는 클러스터링 컬럼 선택에 전적으로 결정돼요. CQL SELECT 문은 ORDER BY 의미를 지원하지만, 클러스터링 컬럼이 지정한 순서로만 지원돼요.
이 자료는 Cassandra, The Definitive Guide에서 각색한 내용이에요. O'Reilly Media, Inc. 발행. Copyright © 2020 Jeff Carpenter, Eben Hewitt. All rights reserved. 허가를 받아 사용했어요.