http://blog.maxindelicato.com/2008/12/scalability-strategies-primer-database-sharding.html
위의 링크의 문서를 간략하게 정리한 자료입니다.
확장성 전략 입문서 : 데이터베이스 샤딩
데이터 베이스 확장성
기본 용어 정리
- 성능 – 한 시스템에서 트랜젝션 하나를 서비스할 수 있게 하는 스피드
- 확장성 – 전체 시스템의 부하증가시에도 서비스의 품질을 유지시키는 시스템의 기능
- 수직 확장 – 시스템을 수직으로 확장하는 것은 각각의 컴포넌트의 자원을 늘려서 확장성을 증가시키는 것
- 수평 확장 – 수평으로 확장하는 것은 각각의 컴포넌트들을 늘려서 확장성을 증가시키는 것
- 수직 데이터셋 파티셔닝 – 데이터 세트(테이블)을 테이블의 컬럼 단위로 쪼개는것
- 수평 데이터셋 파티셔닝 – 데이터 세트(테이블)을 테이블의 행단위로 쪼개는 것
- 시스템 – 소프트웨어와 하드웨어가 상호 연결된 컴포넌트들의 집합
- 샤드 – 데이터베이스의 인스턴스 한개. 전체 데이터 세트의 부분집합. 각각의 샤드에 있는 데이터는 유니크하지만, 같은 스키마를 공유한다.
당신의 데이터를 파티셔닝하라
왜 우리는 데이터를 파티셔닝 해야하는가? 그리고 그것은 우리 어플리케이션의 확장성에 어떻게 도움이 되나?
가능한한 최고 사양의 머신을 구해서 수직적으로 확장을 할 수는 있겠지만, 결국에는 벽에 부딪힌 자신을 발견할 것이다. 하드웨어를 수직적으로 확장하는 것은 한계가 있다.
알고리즘적 색인(Algorithmic Lookups)
날짜 시간 파티셔닝
파티셔닝 날짜와 시간으로 데이터를 파티셔닝 하는 가장 단순한 알고리즘적 방법
Row Count 파티셔닝
날짜와 시간 파티셔닝의 또다른 대안은 Row Count파티셔닝이다. Row Count파티셔닝은 날짜 시간 파티셔닝과 기본적인 개념을 같이한다.
“우리의 시스템에는 하나의 DB서버가 있다. 그 데이터베이스는 지금 사용량 초과이고 가끔 실행되는 오랫동안 실행되는 쿼리로 인해 장애가 발생직전의 상태이다. 데이터셋의 거의 가득 차오른 용량이 주된 이슈이다. 좀 더 상세히 말하자면, 거의 2천 5백만개의 송장이 5년이란 기간동안 더해졌다.”
위의 주어진 시나리오대로라면, 우리는 아래와 같이 말할 수 있다.
- 낮은 볼륨의 쓰기/읽기
- 리소스 집중적인 읽기 – 오래 실행되는 쿼리는 CPU와 Disk 리소스를 집중적으로 사용한다.
- 큰 용량의 데이터 – 큰 볼륨의 데이터를 다루고 있다.
- 데이터의 균등한 분배 – 데이터 셋이 크지만 5년동안 거의 1년에 500만개가 증가했다. 어느정도 증가할지는 장담할 수 없다.
더 이상 예측 가능한 데이터셋을 다루어야 한다고 고민할 필요는 없다. 역사적인 관례로 보면 – 장담할 수 있는건 아니지만 – 데이터셋은 1년에 500만개정도가 계속해서 증가했다. 보통의 서버는 하나의 테이블에 500만개의 읽기 처리를 별다른 노력없이 할 수 있다. 그러므로 우리는 간단히 2천5백만개의 레코드를 분리된 각각의 5개의 데이터 샤드로 재배치 할 수 있다.
몇년이 지난후, 우리는 또다른 샤드를 우리 시스템에 추가 할 수도 있다. 우리는 균등한 데이터의 분배에 대한 유지 관리를 걱정하지 않아도 된다.
새로운 데이터를 어디에 넣을지 결정하는 것은 간단하다. 간단히 우리의 어플리케이션에 설정을 할수 있고 우리의 샤드는 현재 활성상태의 샤드가 무엇인지 찾아낸다. 액티브 샤드가 최대 레코드의 수용치에 이르렀을 때에, 어플리케이션에는 간단히 준비된 다음 샤드에 데이터를 넣도록 설정을 추가하거나 변경하면 된다.
이제 유연성면에서는 약간 어려워지지만, 작년의 데이터가 어디있는지 결정하기 위해 럭셔리 해질 필요는 없는데, 일년전의 데이터를 쿼리할 경우에 모든 샤드를 검색하면 된다. 이건 생각보다 나쁘진 않다. 우리 시스템은 이미 병렬화(parallelized)되었기 때문이다. 이제 하나의 서버에서 찾고자 하는 데이터가 어디 있는지 찾는 대신에 각각의 서버에 찾고자 하는 쿼리를 날리고 결과들을 합치면 된다.
마스터 인덱스 색인
마스터 인덱스 색인을 사용하는 것은 알고리즘적 색인을 사용하는 것과 거의 비슷하게 간단하지만, 특정 데이터 셋이 연속적으로 정렬되어 있지 않은 경우에 필요하다.
도메인 파티셔닝
좀 더 복잡한 파티셔닝 방법중 도메인 파티셔닝은 최고의 유연성을 준다.
파티셔닝의 규모를 결정하는 것은 해당 데이터 베이스의 스키마의 성격에 강하게 의존한다.
특정 사이즈로 파티셔닝이 쉽도록 잘 정리된 스키마는 아래의 성질들이 있다.
- 스키마들의 테이블이 상호 관계가 없이 자연적으로 원칙적으로 격리되어 있다. 예를 들면, 대부분의 테이블은 유저테이블과 관계가 있다. 그리고 포함된 열들은 특히 단순한 한명의 유저의 데이터이다.
- 스키마의 가장 중요한 테이블은 많은 조인을 필요로 하지 않아야 한다. 예를 들면, 유저테이블을 파티셔닝하고자 할 때, 여러유저에 대한 쿼리가 각각의 다른 샤드에 대한 조인이 필요 없어야 한다.
- 위성테이블의 수, 포함된 데이터, 충분히 작은 사이즈의 1단 계층의 테이블, 그리고 계속 작은 사이즈를 유지하는것, 그리고 성능의 병목지점이 되지 않는것.
도메인 파티셔닝은 두개의 메인 서버타입으로 구성된다. 하나의 인덱스 샤드 그리고 하나이상의 도메인 샤드.
인덱스 샤드는 검색 메커니즘을 서비스한다. 최소로, 데이터셋의 파티셔닝된 규모와 그 도메인 샤드의 위치를 포함한다. 처음에 인덱스 샤드에 쿼리를 날려서 어느 도메인 서버에 데이터가 있는 지 얻어오고 도메인 샤드에 대한 다른 컨넥션이 저장된 데이터를 요청한다.
인덱스샤드에 과부하가 걸리는 거 아니냐고 궁금해 할 수 있을 것 같은데, 약간의 과부하가 걸리긴 하지만, 전체시스템에 영향을 자주, 많이 줄 정도는 아니다. 인덱스 샤드의 데이터는 검색하는 데이터에 비해 작은 용량이기 때문에 데이터 전체를 메모리에 올리기도 한다.
“우리의 시스템에는 하나의 DB서버가 있다. 그 데이터베이스는 지금 사용량 초과이고 가끔 실행되는 오랫동안 실행되는 쿼리로 인해 장애가 발생직전의 상태이다. 데이터셋의 거의 가득 차오른 용량이 주된 이슈이다. 좀 더 자세히 말하면, 거의 500만개의 비선형적인 자료를 지난 2년동안 쌓아왔다. 각가의 유저는 user테이블, user_profile테이블, user_blog테이블, user_blog_engty테이블로 되어있다. user_profile 테이블의 각각의 열은 user테이블과 관련되어 있다. 각각의 user_blog테이블의 열은 user테이블과 관련되어 있다. 각각의 user_blog_entry테이블의 열은 user_blog의 열과 관련이 있다.”
위의 시나리오를 보고 파티셔닝 규모를 정해보자. 직접적이든 간접적이든 user라는 하난의 엔트리에 모든 데이터가 관련이 있는것을 알고 있다. 그리고 시스템이 매우 커지는 주된 요인이 유저가 추가되는 것이라는 것을 알고 있다. 유저는 유저테이블에 저장되는 것을 알고 있기 때문에 우리는 확신을 가지고 유저 테이블을 여러개의 샤드로 파티셔닝함으로써 부하를 분산시킬 수 있다고 말 할 수 있다.
** 인덱스 샤드 스키마 정의하기 **
우리가 파티셔닝 해야되는 건 user라는 건 알았는데, 각각의 user를 검색을 위해, 유저에 대한 유니크한 식별자가 필요하다. 이건 간단하게 userId 키 컬럼을 조합함으로써 만들 수 있다.userId키를 가지고 있는 것은 중요하지만, 검색에 매우 유용하지많은 않다. 대부분의 유저들은 username으로 검색을 하기때문에 그들의 username으로 인증하는 것은 중요하다. 유저의 인증에는 username과 password둘다 필요하다.
인덱스 샤드를 위한 명백하게 가장 필요한 데이터들은 아래의 컬럼이 필요하다.
- shardId – 샤드를 위한 식별자
- connectionString – 연결된 샤들에 대한 컨넥션 스트링
- status – 샤드의 상태를 나타내는데 사용
- createdDate – 데이터가 해당 샤드에 추가된 시간
- userId – 유저에 대한 유일한 식별자
- username
- password
** 핫스팟과 샤드의 재조정 다루기 **
불가피하게도 확장성 전략에는 데이터가 하나의 서버에 몰리는 것에 의해 발생하는 성능 저하를 방지하기 위해 재분배가 필요하다.