ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [데이터 중심 애플리케이션] 파티셔닝(분산 데이터)
    Software Development/Database 2021. 10. 31. 16:12

    파티션이라는 용어는 몽고DB, 엘라스틱서치, 솔라의 샤드(Shard), HBase에서는 리전(Region), 빅테이블에서는 태블릿(tablet), 카산드라와 리악에서는 브이노드(vnode), 카우치베이스에서는 브이버켓(vbucket)이라고 부른다. 

     

    데이터 파티셔닝을 원하는 주된 이유는 확장성이다. 대용량 데이터셋이 여러 디스크에 분산될 수 있고 질의 부하는 여러 프로세서에 분산될 수 있다.

     

    파티셔닝과 복제

    보통 복제와 파티셔닝을 함께 적용하여 각 파티션의 복사본을 여러 노드에 저장한다. 각 레코드는 정확히 한 파티션에 속하더라도 이를 여러 다른 노드에 저장해서 내결함성을 보장할 수 있다.

     

    한 노드에 여러 파티션을 저장할 수도 있다. 

    키-값 데이터 파티셔닝

    대량의 데이터를 파티셔닝할 경우, 어떤 레코드를 어느 노드에 저장할지 어떻게 결정해야 하나?

     

    파티셔닝의 목적은 데이터 질의 부하를 노드 사이에 고르게 분산시키는 것이다. 모든 노드가 동일한 분량을 담당한다고 가정할 때 10대의 노드를 사용하면 한 대를 사용할 때보다 이론상으로 10배의 데이터를 저장하고 10배의 읽기, 쓰기 요청을 처리할 수 있다.

     

    파티셔닝이 고르게 이뤄지지 않아 다른 파티션보다 데이터가 많거나 질의를 많이 받는 파티션이 있다면 쏠렸다(skewed)고 말한다. 쏠림이 있으면 파티셔닝의 효과가 매우 떨어진다. 극단적인 경우 모든 부하가 한 파티션에 몰려 10개 중 9개 노드는 유휴 상태에 있고 요청을 받는 노드 하나가 병목이 될 수 있다.

    불균형하게 부하가 높은 파티션을 핫스팟이라고 한다.

     

    핫스팟을 회피하는 가장 단순한 방법은 레코드를 할당할 노드를 무작위로 선택하는 것이다. 그러면 데이터가 노드를 사이에 매우 고르게 분산되지만 커다란 단점이 있다. 어떤 레코드를 읽으려고 할 때 해당 레코드가 어느 노드에 저장됐는지 알 수 없으므로 모든 노드에서 병렬적으로 질의를 실행해야 한다.

    키 범위 기준 파티셔닝

    종이 백과사전 처럼 각 파티션에 연속된 범위의 키를 할당.

    범위들 아이의 경계를 알면 어떤 키가 어느 파티션에 속하는지 쉽게 찾을 수 있다.

    어떤 파티션이 어느 노드에 할당됐는지 알면 적절한 노드로 여청을 직접 보낼 수 있다.

    키 범위 크기가 반드시 동일할 필요는 없다.

    각 파티션 내에서는 키를 정렬된 순서로 저장할 수 있다(SS 테이블 LSM 트리) 이렇게 하면 범위 스캔이 쉽고, 연쇄된 색인으로 간주해서 질의 하나로 관련 레코드 여러 개를 읽어오는 데 사용할 수 있다.

    그러나 핫스팟을 유발할 수 있다. 핫스팟을 유발하지 않도록 설계를 고려해야 한다.

    키의 해시값 기준 파티셔닝

    해시 함수를 사용. 암호학적으로 강할 필요는 없다. MD5로도 충분.

     

    이 기법은 키를 파티션 사이에 균일하게 분산시키는 데 좋다. 파티션 경계는 크기가 동일하도록 나눌 수도 있고  무작위에 가깝게 선택할 수도 있다.

     

    그러나 파티셔닝에 키의 해시값을 사용해서 파티셔닝하면 키 범위 파티셔닝의 좋은 속성을 잃어 버린다. 바로 질의를 효율적으로 실행할 수 있는 능력이다. 전에는 인접했던 키들이 이제는 모든 파티션에 흩어져서 정렬 순서가 유지되지 않는다. 몽고DB에서는 해시 기반 샤딩 모드를 활성화하면 범위 질의가 모든 파티션에 전송돼야 한다. 리악, 카우치베이스, 볼드모트도 기본키에 대한 범위 질의가 되지 않는다.

     

    카산드라는 두 가지 파티셔닝 전략 사이에서 타협한다. 카산드라에서 테이블을 선언할 때 여러 칼럼을 포함하는 복합 기본키를 지정할 수 있다. 키의 첫 부분에만 해싱을 적용해 파티션 결정에 사용하고 남은 칼럼은 카산드라의 SS테이블에서 데이터를 정렬하는 연쇄된 색인으로 사용한다. 따라서 복합 키의 첫 번쨰 칼럼에 대해서는 값 범위로 검색하는 질의를 쓸 수 없지만 첫 번째 칼럼에 고정된 값을 지정하면 키의 다른 칼럼에 대해서는 범위 스캔을 효율적으로 실행할 수 있다.

     

    연쇄된 색인을 사용하면 일대다 관계를 표현하는 우아한 데이터 모델을 만들 수 있다. 예를 들어 소셜 미디어 사이트에서 사용자 한 명이 수정한 문서 여러 개를 올릴 수도 있다. 수정한 문서의 기본키를 (user_id, update_timestamp)로 선택하면 특정한 사용자가 어던 시간 구간에서 수정한 모든 문서를 타임스탬프 순으로 정렬해서 읽어올 수 있다. 

    쏠린 작업부하와 핫스팟 완화

    키를 해싱해서 파티션을 정하면 핫스팟을 줄이는 데 도움이 된다. 그렇지만 핫스팟을 완벽히 제거할 수는 없다. 항상 동일한 키를 읽고 쓰는 극단적인 상황에서는 모든 요청이 동일한 파티션에 쏠리게 된다.

     

    예를 들어 소셜 미디어 사이트에서 수백만 명의 팔로워를 거느린 유명인이 뭔가를 하면 후폭풍이 발생할 수 있다. 유명인이 실행한 작업 때문에 동일한 키에 막대한 양의 데이터를 기록해야 할 수도 있다. 

     

    현대 데이터 시스템은 대부분 크게 쏠린 작업부하를 자동으로 보정하지 못하므로 애플리케이션에서 쏠림을 완화해야 한다. 예를 들어 요청이 매우 많이 쏠리는 키를 발견했을 때 간단한 해결책은 각 키의 시작이나 끝에 임의의 숫자를 붙이는 것이다. 임의의 10진수 두 개만 붙이더라도 한 키에 대한 쓰기 작업이 100개의 다른 키로 균등하게 분산되고 그 키들은 다른 파티션으로 분산될 수 있다.

     

    그러나 다른 키에 쪼개서 쓰면 읽기를 실행할 때 추가적인 작업이 필요해진다. 100개의 키에 해당하는 데이터를 읽어서 조합해야하기 때문이다. 추가적으로 저장해야 할 정보도 있다. 이 기법은 요청이 몰리는 소수의 키에만 적용하는 게 적합하다. 

     

    미래에는 데이터 시스템이 쏠린 작업부하를 자동으로 감지해서 보정할 수 있겠지만 아직은 애플리케이션에 대한 트레이드오프를 꼼꼼히 따져볼 필요가 있다.

    파티셔닝과 보조 색인

    지금까지 설명한 파티셔닝 방식은 키-값 데이터 모델에 의존한다. 보조 색인이 연관되면 상황은 복잡해진다. 보조 색인은 보통 레코드를 유일하게 식별하는 용도가 아니라 특정한 값이 발생한 항목을 검색하는 수단이다. 

     

    보조 색인은 관계형 데이터베이스의 핵심 요소이며 문서 데이터베이스에서도 흔하다. 많은 키-값 저장소에서는 구현 복잡도가 추가되는 것을 피하려고 보조 색인을 지원하지 않지만 보조 색인은 데이터 모델링에 매우 유용하므로 일부 저장소에서는 이를 추가하기 시작했다. 그리고 보조 색인은 솔라나 엘라스틱서치 같은 검색 서버에게는 존재의 이유다.

     

    보조 색인은 파티션에 깔끔하게 대응되지 않는 문제점이 있다. 보조 색인이 있는 데이터베이스를 파티셔닝하는 데 널리 쓰이는 두 가지 방법이 있다. 문서 기반 파티셔닝과 용어 기반 파티셔닝이다.

    문서 기준 보조 색인 파티셔닝

    중고차를 판매하는 웹사이트를 운영한다고 하자. 각 항목에는 문서ID라고 부르는 고유 ID가 있고 데이터베이스를 문서 ID 기준으로 파티셔닝한다(ID 0부터 499까지는 파티션 0, ID 500부터 999까지는 파티션 1에 할당하는 식).

     

    사용자들이 차를 검색할 때 색상과 제조사로 필터링할 수 있게 하려면 color와 make에 보조 색인을 만들어야 한다. 색인을 선언했다면 데이터베이스가 자동으로 색인 생성을 할 수 있다. 예를 들어 빨간색 자동차가 데이터베이스에 추가되면 데이터베이스 파티션은 자동으로 그것은 color:red 색인 항목에 해당하는 문서 ID 목록에 추가.

     

    이런 색인 방법을 사용하면 각 파티션이 완전히 독립적으로 동작한다. 각 파티션은 자신의 보조 색인을 유지하며 그 파티션에 속하는 문서만 담당. 다른 파티션에 어떤 데이터가 저장되는지 신경 쓰지 않는다. 추가, 삭제, 갱신 등의 쓰기 작업을 실행할 때는 쓰려고 하는 문서 ID를 포함하는 파티션만 다루면 된다. 문서 파티셔닝 색인은 지역 색인(local index)이라고도 한다.

     

    빨간색 자동차는 모든 파티션에 저장되어 있을 수 있기 때문에 모든 파티션으로 질의를 보내서 결과를 모아야 한다(스캐터/개더(scatter/getter) 방식이라고 한다). 보조 색인을 써서 읽는 질의는 큰 비용이 들 수 있다. 꼬리 지연 시간 증폭이 발생하기 쉽다. 그럼에도 보조 색인을 문서 기준으로 파티셔닝하는 경우가 많다. 몽고DB, 리악, 카산드라, 엘라스틱서치, 솔라, 볼트DB는 모두 문서 기준으로 파티셔닝된 보조 색인을 사용한다. 데이터베이스 벤더들은 대부분 보조 색인 질의가 단일 파티션에서만 실행되도록 파티셔닝 방식을 설계하기를 권장하지만 항상 가능하지는 않다. 특히 단일 질의에서 여러 보조 색인을 사용할 때 그렇다.

    용어 기준 보조 색인 파티셔닝

    각 파티션이 자신만의 보조 색인을 갖게 하는 대신, 모든 파티션의 데이터를 담당하는 전역 색인을 만들 수도 있다. 그러나 한 노드에만 색인을 저장할 수는 없다. 해당 노드가 병목이 되어 파티셔닝의 목정을 해치기 때문이다. 전역 색인도 파티셔닝해야 하지만 기본키 색인과는 다른 식으로 할 수 있다.

     

    모든 파티션에 있는 빨간색 자동차 정보는 색인에서 color:red 항목에 저장되지만 색깔 색인은 a부터 r까지의 글자로 시작하는 색깔은 파티션 0에, s부터 z까지의 글자로 시작하는 색깔은 파티션 1에 저장되도록 파티셔닝된다. 

     

    찾고자 하는 용어에 따라 색인의 파티션이 결정되므로 이런 식의 색인을 용어 기준으로 파티셔닝됐다고 한다. 용어라는 이름은 전문 색인에서 나왔는데 용어란 문서에 등장하는 모든 단어를 말한다.

     

    이전처럼 색인을 파티셔닝할 때 용어 자체를 쓸 수도 있고 용어의 해시값을 사용할 수도 있다. 용어 차제로 파티셔닝하면 범위 스캔에 유용한 반명 용어의 해시값을 사용해 파티셔닝하면 부하가 좀 더 고르게 분산된다.

     

    문서 파티셔닝 색인에 비해 전역 색인이 갖는 이점은 읽기가 효율적이라는 것이다. 그렇지만 쓰기가 느리고 복잡하다는 단점이 있다. 단일 문서를 쓸 때 해당 색인의 여러 파티션에 영향을 줄 수 있기 때문이다. 

     

    이상적인 세상이라면 색인은 항상 최신의 상태에 있고 데이터베이스에 기록된 모든 문서는 바로 색인에 반영돼야 한다. 하지만 용어 파티셔닝 색인을 사용할 때 그렇게 하려면 쓰기에 영향받는 모든 파티션에 걸친 분산 트랜잭션을 실행해야 하는데, 모든 데이터베이스에서 분산 트랜잭션을 지원하지 않는다.

     

    현실에서는 전역 보조 색인은 대개 비동기로 갱신된다. 아마존 다이나모DB는 정상적인상황에서는 전역 보조 색인을 갱신하는 데 1초도 안 걸리지만 인프라에 결함이 생기면 반영 지연 시간이 더 길어질 수도 있다.

    파티션 재균형화

    시간이 지나면 데이터베이스에 변화가 생긴다.

    • 질의 처리향이 증가해서 늘어난 부하를 처리하기 위해 CPU를 더 추가하고 싶다.
    • 데이터셋 크기가 증가해서 데이터셋 저장에 사용할 디스크와 램을 추가하고 싶다.
    • 장비에 장애가 발생해서 그 방지가 담당하던 역할을 다른 장비가 넘겨받아야 한다.

    클러스터에서 한 노드가 담당하던 부하를 다른 노드로 옮기는 과정을 rebalancing이라고 한다.

    어떤 파티셔닝 방식을 쓰는지에 무관하게 rebalancing이 실행도리 때 보통 만족시킬 것으로 기대되는 최소 요구사항이 있다.

     

    • 재균형화 후, 부사가 클러스터 내에 있는 노드들 사이에 균등하게 분배돼야 한다.
    • 재균형화 도중에도 데이터베이스는 읽기 쓰기 요청을 받아들어야 한다.
    • 재균형화가 빨리 실행되고 네트워크와 디스크 I/O 부하를 최소화할 수 있도록 노드들 사이에 데이터가 필요 이상으로 옮겨져셔는 안된다.

    재균형화 전략

    쓰면 안되는 방법: 해시값에 모든 N 연산을 실행

    모드 N 방식의 문제는 노드 개수 N이 바뀌면 대부분의 키가 노드 사이에 옮겨져야 한다는 점이다. 데이터를 필요 이상으로 이동하지 않는 방법이 필요하다.

    파티션 개수 고정

    파티션을 노드 대수보다 많이 만들고 각 노드에 여러 파티션을 할당. 노드 10대로 구성된 클러스터에서 실행되는 데이터베이스는 처음부터 파티션을 1000개로 쪼개서 각 노드마다 약 100개의 파티션을 할당할 수 있다.

     

    이론성으로 클러스터에 성능이 다른 하드웨어가 섞여 있는 것을 고려할 수도 있다. 이 방식을 사용할 때는 보통 데이터베이스가 처음 구축될 때 파티션 개수가 고정되고 이후에 변하지 않는다. 파티션 개수가 고정되면 운영이 단순해지므로 고정 파티션을 사용하는 데이터베이스는 파티션 분할을 지원하지 않는 경우가 많다. 

    동적 파티셔닝

    키 범위 파티셔닝을 사용하는 데이터베이스는 파티션 경계와 개수가 고정돼 있느 게 매우 불편하다. 파티션 경계를 잘못 지정하면 모든 데이터가 한 파티션에 저장되고 나머지 파티션은 텅 빌 수도 있다. 

    HBase나 리싱크DB처럼 키 범위 파티셔닝을 사용하는 데이터베이스에서는 파티션을 동적으로 만든다. 파티션 크기가 설정된 값을 넘어서면 파티션을 두 개로 쪼개 각각에 우너래 파티션의 절반 정도의 데이터가 포함되게 한다. 

     

    파티션 개수가 고정된 경우와 마찬가지로 각 파티션은 노드 하나에 할당되고 각 노드는 여러 파티션을 담당할 수 있다.큰 파티션이 쪼개진 후 부하의 균형을 맞추기 위해 분할된 파티션 중 하나가 다른 노드로 이동될 수 있다. HBase의 경우 기분 분산 파일 시스템인 HDFS를 통해 파티션 파일이 전송된다.

     

    동적 파티셔닝은 파티션 개수가 전체 데이터 용령에 맞춰 조정된다는 이점이 있다. 그러나 빈 데이터페비으는 파티션 경계를 어디로 정해야 하는지에 고나한 사전 정보가 없으므로 시작할 때는 파티션이 하나라는 함정이 있다. HBase와 몽고DB에서는 빈 데이터베이스에 초기 파티션 집합을 설정할 수 있게 한다(사전 분할(pre-splitting)이라 부른다).

    노드 비례 파티셔닝

    노드당 파티션 개수를 고정한다. 새 노드가 클러스터에 추가되면 고정된 개수의 파티션을 무작위로 선택해 분할하고 각 분할된 파티션의 절반을 그대로 두고 다른 절반은 새 노드에 할당한다. 

    운영: 자동 재균형화와 수동 재균형화

    자동 재균형화는 일상적인 유지보수에 손이 덜 가므로 편리할 수 있다. 하지만 예측하기 어렵기도 하다. 자동화는 자동 장애 감지와 조합되면 위험해질 수도 있다. 일시적으로 노드 한대에 과부하가 걸려 일시적으로 요청에 대한 응답이 느려지면, 다른 노드들은 과부하 걸린 노드가 죽었다고 간주하고 재균형화를 하려고 한다. 그러면 과부하 걸린 느도와 다른 노드들, 그리고 네트워크에 부하를 더해서 상호아이 악화되고 연쇄 장애가 발생할 수 있다.

    요청 라우팅

    파티션이 재균형화되면서 노드에 할당되는 파티션이 바뀐다. 이때 클라이언트에서 요청을 보낼 때 어느 노드로 접속해야 하는지 어떻게 알 수 있을까?

     

    이 문제는 데이터베이스에 국한되지 않는 더욱 일반적인 문제인 서비스 찾기(service discovery)의 일종이다. 네트워크를 통해 접속되는 소프트웨어라면 어떤 것이든지, 특히 고가용성을 지향하는 소프트웨어라면 모두 이 문제가 있다. 

     

    상위 수준에서 보면 이 문제는 몇 가지 다른 접근법이 있다.

    • 클라이언트가 아무 노드에 접속한 후(라운드 로빈 로드 밸런서를 통해서) 만약 해당 노드에 요청을 적용할 파티션이 있다면 거기서 요청을 직접 처리하고 없다면 올바른 노드로 전달해서 응답을 받고 클라이언트에게 응답을 전달
    • 클라이언ㅇ트의 모든 요청을 라우팅 계층으로 먼저 보낸다. 라우팅 계층 자체에서는 어무 요청도 처리하지 않고 파티션 인지 로드 밸런서로만 동작
    • 클라이언트가 파티셔닝 방법과 파티션이 어떤 노드에 할당됐는지 알고 있게 한다.

    핵심 문제는 라우팅 결정을 내리는 구성요소가 노드에 할당된 파티션의 변경사항을 어떻게 아느냐다. 이 문제는 참여하는 모든 곳에서 정보가 일치해야 하므로 다루기 어렵다. 그렇지 않으면 요청이 잘못된 노드로 전송되고 제대로 처리되지 못한다. 

     

    많은 분산 데이터 시스템은 클러스터 메타데이터를 추적하기 위해 주키퍼 같은 별도 코디네이션 서비르를 사용한다. 각 노드는 주키퍼에 자신을 등록하고 주키퍼는 파티션과 노드 사이의 신뢰성 있는 할당 정보를 관리한다. 라우팅 게층이나 파티션 인지 클라이언트 같은 다른 구성요소들은 주키퍼에 있는 정보를 구독할 수 있다. 

     

    HBase, 솔라, 카프카도 파티션 할당을 추적하는 데 주키퍼를 사용한다. 몽고DB도 아키텍처는 비슷하지만 자체적인 설정서버 구현에 의존하고 몽고스 데몬을 라우팅 게층으로 사용한다. 

     

    카산드라와 리악은 다른 방법을 쓴다. 가십 프로토콜을 사용해서 클러스터 상태 변화를 노드 사이에 퍼트린다. 아무 노드나 요청을 받을 수 있고 욫어을 받은 노드는 요청을 처리할 파티션을 갖고 있는 올바른 노드로 요청을 전달해준다. 복잡하지만 코디네이션 서비스에 의존하지 않는다.

    병렬 질의 실행

    지금까지는 단일 키를 읽거나 쓰는 매우 간단한 질의에 대해서만 설명했다. 대부분읜 NoSQL 분산 데이터스토어에서 지우너되는 접근 수준이다.

     

    그러나 분석용으로 자주 사용되는 대규모 병렬 처리 관계형 데이터베이스 제품은 훨씬 더 복합한 종류의 질의를 지원한다. 조인, 필터링, 그룹화, 집계 연산을 몇 개 포함한다. 

     

    데이터 웨어하우스 질의 고속 병렬 실행은 더 전문적인 주제이며 분석 업무가 비즈니스적으로 중요해짐에 따라 관심이 많아지고 있다.

     

    댓글

Designed by Tistory.