1. 역정규화는 성능을 지배한다
- 데이터베이스 개발과 관련된 격언 중에는 "먼저 문제가 될 때까지 정규화를 하고, 그 다음에 제대로 동작할 때까지 역정규화를 해라."라는 말이 있다.
- 말도 안되는 소리이다!
- SQL Server 6.5 또는 그 이전 버전에서는 OLTP 데이터베이스에서 역정규화를 하는 것이 더 나은 성능을 발휘할 수도 있었다.
- 하지만, 요즘 시대에 통하는 말은 아니다.
- OLTP 또는 (보고서 서비스나 BI가 아닌) 운영 데이터베이스의 경우 정규화된 스키마에 적절하게 인덱스만 설정되어 있다면 역정규화된 스키마에 비해 항상 더 좋은 성능을 낼 수 있다.
- 정규화된 디자인에서는 '이중으로 insert 하고 update를 해야 한다'라는 것과 데이터 조회 시 역정규화된 데이터를 피벗팅(pivoting)해야 한다는 단점이 훨씬 더 크다.
- 성능 테스트를 해보지 않았거나 어플리케이션의 프로필에 대해서 잘 모르는 상태에서 역정규화 방식으로 구성해서 발생하는 성능 문제들은 대부분 SQL에 대해 조금만 알면 쉽게 해결할 수 있는 문제들인 경우가 많다.
- 하지만, 비교적 덜 정규화된 데이터베이스들을 만들게 되는 근본적인 원인은 성능 개선을 위한 노력이 부족하기 때문만은 아니다.
- 데이터베이스를 설계하는 사람이나 프로그램을 작성하는 사람들의 기술이 부족한 것이 가장 큰 원인이다.
- 경우에 따라서는 OLTP 데이터베이스라도 역정규화를 하는 것이 더 좋은 성능을 낼 수 있다.
- 예를 들어, 재고 관리를 위한 시스템에서 거래와 관련된 계산을 미리 해놓고 지역별로 재고량을 계산해놓는 경우가 이에 해당한다.
- 이러한 경우에도 읽기/쓰기의 비율은 신중하게 고려해야하며, 정규화와 역정규화 스키마에 대해 동일한 부하를 발생시켜 성능 테스트를 해보기 전까지는 역정규화가 더 좋다라고 단정지어서는 안된다.(그리고 뉴스 그룹을 통해 더 나은 방법이 있는지를 MVP들에게도 물어보기 바란다. MVP 프로그램이 운영되는 목적이 이러한 질문에 대해 도움을 주기 위해서이다.)
- 제 1차 정규화는 매우 중요하다.
- 1차 정규화는 중복된 열을 허용하지 않는다는 것을 의미한다.
- 또한, 속성(attribute)당 하나의 팩트만 있어야 하고, 읽기 쉬운 형태의 데이터로 되어있어야 하며, 중복된 행을 허용하지 않는다는 것을 의미한다.
- 생각보다 1차 정규화를 위반하는 경우가 많이 있다.
- 제품 코드와 제품 코드를 포함하고 있는 스마트 키 형태가 바로 1차 정규화를 위반하는 경우가 많이 있다.
- 제품 코드를 포함하고 있는 스마트 키 형태가 바로 1차 정규화를 위반한 경우라 할 수 있다.
- 바이너리 열(비트 1은 이것, 2는 저것을 의미하는 행태)에 있는 데이터를 포함하는 비트마스크 속성도 마찬가지로 1차 정규화를 위반하는 사례라 할 수 있다.
- 값을 제대로 이해하기 위해서는 바이너리 인코딩 방식을 알아야 하기 때문이다.
- 효율적으로 인덱스를 사용할 수 있기 위해서는 제 1차 정규화를 정확히 지켜야한다는 점을 유의해야 한다.
- 열 내부에 포함된 값의 일부를 사용해야하는 경우에는 인덱스를 제대로 사용하지 못할 수도 있다.(예를 들어 5자리의 문자열로 된 값이 있을 때 처음 2자리의 문자열을 찾을 때는 인덱스를 사용할 수 있지만, 중간의 3자리 문자열로 된 값을 찾을 때에는 인덱스를 제대로 사용하지 못한다.)
2. 키가 중요하다
- 기본 키와 참조 키 제약 조건이 제대로 설정되어 있지 않은 상태로 운영되는 데이터베이스들이 상당히 많이 있다는 것은 아주 놀라운 일이다.
- 테이블에 자동 증가 열(identity column)이나 GUID와 같이 기본 키로 정할 수 있는 충분한 열이 있음에도 불구하고, 기본 키를 설정하지 않는 경우도 많이 있다.
- 이렇게 사용하는 이유는 아마도 어플리케이션에 데이터베이스로 전달되는 데이터에 오류가 없다고 단정하고 있기 때문이라고 생각한다.
- 이런 방식을 사용하는 개발자는 아마도 다른 나라에서 사용되는 어플리케이션 개발 경험이 없는 사람일 것이다.
- 필자(Paul)는 2007년에 러시아로 세 번의 출장을 다녀올 기회가 있었다.
- 델타 항공 티켓 창구에서 체크인을 하는 동안 창구 직원들은 필자의 여권을 확인했다.
- 비행기에 타기 전에도 필자의 티켓과 여권을 확인했다.
- 하지만, 모스크바에 도착한 다음 출입국 관리 사무소의 직원은 필자의 여권과 비자에 문제가 있다고 판단하여, 다시 처음부터 여권을 검사하고 신상 정보를 확인한 다음 러시아 비자도 검사하였다.
- 이와는 달리, 영국을 방문했을 때에는 입국하는 모든 사람들에 대해서 사진촬영만 하였다.
- 미국에서는 지문만 남기면 된다.
- 이러한 나라들 (영국, 미국 등)에서는 데이터베이스의 키 개념을 제대로 이해하고 있기 때문일 수도 있다.
- 필자가 이전에 컨설팅을 했던 (키가 제대로 설정되어 있지 않은) 데이터베이스들은 항상 다음과 같은 두 가지 특징들을 가지고 있었다.
- 하나는 데이터베이스를 담당하는 IT 관리자들은 자신들의 데이터가 아주 정교하고 제대로 되어있다고 생각한다는 점이고,
- 다른 하나는 실제 데이터들은 완전히 엉망이었다는 점이다.
- 키를 사용하지 않는 데이터베이스 개발자들도 있다.
- 이런 개발자가 되지 말기 바란다.
- 기본 키를 설정할 때에는 가능하면 복합 키 가능하면 복합 키(composite key) 형태로 만들지 않도록 한다.
- 기본 키가 복합 키라면 참조 키 역시 복합 키 형태이어야 하며, 이로 인해 여러 열들을 조인 해야 하고 클러스터형 인덱스는 매우 커지게 된다.
- 기술적인 측면에서 보면 잘못된 것은 아니지만 분명 복합 키를 사용함으로써 성능이 크게 저하될 수도 있다.
- 이러한 경우 대리 키(surrogate key)라는 것을 사용해서 해결할 수 있다.
- 대리 키(예를 들어 INT IDENTITY 열로 설정한 경우)는 크기가 작고 빠르며 유용하게 사용될 수 있다.
- 하지만, 대리 키를 사용할 경우에는 해당 열에 Unique 제약 조건을 반드시 설정해주어야 한다.
- Unique 제약 조건은 중복 데이터가 발생해서 데이터가 엉망이 되는 것을 방지하기 위한 목적으로 사용된다.
3. 일반화를 하자
- 소위 "정규화된" 데이터베이스들은 상당히 복잡한 경우가 많다.
- 이로 인해 개발자들은 데이터베이스가 정규화될수록 더 많은 테이블이 존재하고 복잡해진다고 생각하게 된다.
- 심지어는 다음과 같은 말을 하기도 한다.
- "나는 제 3차 정규화만 할래. 그렇지 않으면 테이블들이 너무 많아져."
- 데이터베이스가 복잡해지는 일반적인 원인은 데이터베이스 설계자들이 어떤 형태의 것들을 하나의 엔터티로 그룹화할 것인지를 결정할 때 너무 상세한 수준으로 생각하기 때문이다.
- 이로 인해 테이블의 수는 많아지게 되며, 데이터베이스를 이용해서 개발하는 비용도 많이 들고 힘들어지게 되고 자연스레 정규화가 오명을 쓰게 되는 경우이다.
- 다른 극단적인 예로, 너무 많은 유형의 것들을 거대한 젤리와 같은 하나의 blob 열로 병합해버리는 경우를 들 수 있다.
- 데이터베이스 설계자들에게는 이러한 방식이 간단하고 쉬울 수 있지만, 시간이 지날수록 무결성이 깨지는 문제가 발생하게 된다.
- 데이터베이스 개발자들이 많이 사용하긴 하지만 명확히 이름 붙여지지 않은 기술이 바로 일반화(generalization)라는 것이다.
- 비슷한 유형의 것들을 묶어서 하나의 엔터티로 만들어 데이터베이스를 좀 더 단순하게 하는 것이다.
- 일반화가 잘 된 데이터베이스는 정규화가 더 잘되어 있다.
- 이러한 데이터베이스에서 일반화와 정규화의 차이는 단지 각 엔터티들이 명확하게 구분되는지의 여부에만 있다.
- 일반화가 잘 되어 있으면 개발 시간을 줄일 수 있을뿐더러, 데이터베이스를 더욱 유연하게 만들어주고 데이터베이스의 수명을 연장시켜주기도 한다.
- 극도로 복잡한 구성을 멋진 행태의 디자인으로 만들어주는 데이터베이스 설계 기술이란 엔터티들을 일반화하여 무결성을 보장하도록 정규화되었지만 복잡하지 않으면서 요구사항을 만족하는 데이터베이스를 설계하는것을 말한다.
4. 클래스 <> 테이블
- 객체 지향적인 세계와 데이터베이스 세계 사이에는 기술적인 면 뿐만아니라 문화적인 면에서도 차이가 있다.
- 관계형 데이터베이스에는 상속이라는 개념이 없다.
- .NET 언어를 사용하는 개발자들은 데이터베이스를 단지 하나의 데이터 저장소로만 다루며, 데이터베이스를 관리하는 사람들은 클래스나 상속, 개체 등을 이해할 필요가 없다고 생각한다.
- 대부분의 회사에서 .NET 데이터베이스 개발자와 데이터베이스 설계자의 비율은 50대 1 정도이기 때문에 데이터베이스와 관련된 의견들은 쉽게 무시되거나 묵살된다.
- 또한 관리자들에게는 어플리케이션 프로그램 언어들이 사탕과 같이 달콤하게 보이지만(이로 인해 그들의 직업 수명이 짧아지기도 하지만), DBA들에게는 오직 T-SQL이라는 오래되었으면서도 화려하지 않지만 충직한 언어만 존재한다.
- 이로 인해 관리자들은 데이터베이스 교육보다는 어플리케이션 언어 교육을 더 중요하게 생각하는 경우가 많다.
- 데이터베이스는 공격을 받고 있으며 전쟁에서 밀리고 있다고 볼 수도 있다.
- 다수의 솔루션들이 NHibernate나 Entity Framework와 같은 어플리케이션 계층 개체 관계형 맵퍼(object-relational mapper)를 사용하고 있다.
- 이러한 개체-관계형 맵퍼들은 데이터베이스에 대한 형편 없는 추상 계층을 제공하게 된다.
- 각각의 클래스마다 테이블들을 만들고 모든 개체마다 행을 만들게 된다.
- 하지만, 이러한 방식으로는 클래스들을 정확히 표시할 수는 없다.
- 가장 일반적인 해결책은 슈퍼 타입(super-types)과 서브 타입(sub-types), 테이블들을 조인한 뷰를 통해서 클래스에 있는 모든 개체들을 표현하는 방식으로 데이터베이스 내에 개체들을 모델링 하는 것이다.
- 어떤 개체들은 관계형 데이터베이스의 테이블과 바로 매핑될 수도 있고, 테이블의 여러 값들로 구성할 수도 있다.
- 어떤 클래스들은 여러 테이블들을 이용해서 정의해야 할 수도 있다.
- 이렇게 하기 위해서 필요한 것이 바로 팀워크이다.
- 관계형 데이터베이스 및 객체 지향 프로그램 아키텍트들이 협업해서 이와 같은 중간 계층을 만들어야 한다.
- 이러한 것이 만들어지면 프로세스를 더욱 쉽게 만들 수 있는 도구들도 만들 수 있게 된다.
5. 로직을 데이터로 저장하자
- 일반적으로 데이터베이스에는 업무 로직이 저장되지 않는다고 생각한다.
- 복잡한 업무 로직과 T-SQL 코드를 분리하기 위한 목적이라면 어느 정도 맞는 말이다.
- 하지만 업무 로직은 아주 쉽게 변경되기 때문에 어플리케이션에서 변경이 시작되면 액세스 계층으로 전파되고, 저장 프로시저 계층, 데이터베이스 스키마 계층으로까지 영향을 미치면서 결국에는 데이터까지 영향을 받게 된다.
- 업무 로직이 있어야 할 가장 최적의 장소는 바로 데이터 안이다.
- 업무 로직에서 사용하는 모든 변수 들은 프로그램이 아니라 바로 데이터베이스 내에 저장되어있어야 한다.
- 어플리케이션의 동작은 .NET 코드나 T-SQL의 Case 문이 아닌 데이터에서 읽어 들인 값(로직)을 이용해서 정해져야 한다.
- 데이터 집합(값)을 업무 규칙 데이터 집합(로직)과 동적으로 연결시킬 수 있도록 구성하면 업무 규칙들이 변경되더라도 코드의 변경 없이 바로 처리할 수 있게 된다. 업무 로직을 데이터 방식으로 저장하게 되면 어플리케이션에서는 로직에 대한 자유로움과 정결함을 얻을 수 있다.
'SQL > 실전 SQL Server MVP 53' 카테고리의 다른 글
| 1. 데이터베이스 설계 및 아키텍처 - (1) Louis와 Paul의 관계형 데이터베이스 디자인 10가지 원칙_2 (0) | 2025.02.06 |
|---|