(WIP) MySQL Partition(Sharding)
개요
해마다 트래픽이 증가하고, 환경이 복잡해지며 그에 따른 데이터 요구사항도 빠르게 증가하고 있다.
MySQL 확장은 주로 데이터의 상태 저장 특성 때문에 다른 유형의 서버와 매우 다르다.
단순히 애플리케이션 서버처럼 서버의 대수를 높인다고 부하가 해결되지 않는다.
MySQL과 같은 State 한 서버의 경우 성능 향상을 위해 Scaling 을 고려할 수 밖에 없다.
Scaling
스케일링 :
트래픽(부하)을 처리하기 위해 지원하는 시스템의 기능이자 방법이다.
시스템의 확장성이 좋은지 나쁜지에 대한 기준은 비용과 단순성으로 측정할 수 있다.
대표적으로 '스케일 아웃' 과 '스케일 업 방식'이 있다.
시스템의 용량 :
주어진 시간내에 작업을 수행할 수 있는 양이다.
용량은 한정되어 있으며, 보통은 수용 가능한 성능을 제공하면서 달성할 수 있는 처리량으로 정의한다.
데이터의 양 :
애플리케이션이 축적할 수 있는 엄청난 양의 데이터는 가장 일반적인 스케일링 문제중 하나다.
데이터를 절대 삭제하지 않는 오늘날의 많은 웹 애플리케이션에서 특히 문제가 되며
특히, 데이터를 스캔하는 과정에서 많은 양의 데이터로 인해 찾는 속도가 느려 성능이 나빠질 수 밖에 없다.
사용자 수 :
사용자 수가 적더라도, 사용자와 연관된 데이터들이 많다면 데이터의 양은 빠르게 증가할 수 있다.
사용자 활동 :
사용자 활동은 모든 기능에 대해서 골고루 이루어지지 않는다.
예를 들면 특정 기능이 활성화 되어 사용자가 몰리며 부하를 크게 증가시킬 수 있다.
Read VS Write Bound WorkLoad 바운드 워크로드
DataBase 아키텍처 확장을 고려할 때 가장 먼저 고려할 사항은 아래 2가지다.
- 읽기 바운드 워크로드를 확장한다.
- 쓰기 바운드 워크로드를 확장한다.
워크로드 :
시간 경과에 따른 작업량이며, 데이터베이스를 기준으로 초당 쿼리수(QPS)로 볼 수 있다.
단, CPU 코어 개수, 디스크의 처리량 제한, 네트워크 처리량 등 모든 유형의 쿼리와 지연 시간이 혼합된 것이다.
읽기 바운드 워크로드
데이터베이스의 부하 척도를 판단할 수 있는 기준은 CPU와 IOPS 이다.
- CPU 사용률이 높다는 것은 서버가 쿼리를 처리하는데 모든 시간을 소비하고 있음을 의미합니다.
- IOPS 처치량이 많다는 것은 디스크를 매우 자주 읽거나 디스크에서 많은 행을 읽고 있음을 의미한다.
인덱스를 추가하고 쿼리를 튜닝하고 캐시를 사용함으로써 어느정도 부하를 감소시킬 수는 있다.
다만, 이러한 작업을 했음에도 여전히 많은 부하가 발생하게 된다면 읽기 바운드 워크로드 작업이 여전히 많다는 것이다.
이 같은 문제를 해결하기 위해 우리는 Query Offloding 기법을 생각할 수 있다.
Query offloading
쓰기 작업과 읽기 작업을 나누어 부하를 분산시키는 방법이다.
MySQL 의 경우 Replication 을 사용하면 대게 Active-StandBy 토폴로지를 사용한다.
여기서 StandBy 상태인 Replica Server 들에 읽기 쿼리 수행 작업을 할당해주면
StandBy 상태인 Replica Server 들의 수 만큼 Source Server의 읽기 부하가 감소된다.
쓰기 바운드 워크로드
데이터베이스에 쌓인 데이터가 많아질 수록 쓰기 바운드 워크로드는 증가한다.
DataBase 의 성능은 탐색할 범위에 따라 크게 좌우되는데 많은 데이터가 있다면 당연히 탐색하는 범위가 넓어지고
인덱스를 사용해도 인덱스의 워킹셋이 실질적인 물리 메모리가 크다면 쿼리 처리가 상당히 느려질 것이다.
파티션
하나의 테이블이 너무 커서 인덱스 크기가 물리적인 메모리보다 훨씬 크거나
데이터 특성상 주기적인 삭제 작업이 필요한 경우 등이 있을 경우 파티션을 사용한다.
- 단일 INSERT와 단일 또는 범위 SELECT의 빠른 처리
- UPDATE, DELETE 쿼리도 대상을 찾기 위해 SELECT 를 사용하고 인덱스를 사용하게 된다.
- 즉, UPDATE, DELETE 쿼리 또한 인덱스의 크기에 영향을 받게 된다.
- 인덱스의 크기가 물리적으로 MySQL이 사용하는 메모리 공간보다 크다면 그 영향은 더욱 심각해진다.
- 인덱스의 워킹셋이 실질적인 물리 메모리보다 크다면 쿼리 처리가 상당히 느려질 것이다.
- 데이터의 물리적인 저장소를 분리
- 데이터/인덱스 파일이 파일 시스템에서 차지하는 공간이 크다면 그만큼 백업이나 관리작업이 어려워진다.
- 파티션을 통해 파일의 크기를 조절하거나
파티션별 파일들이 저장될 위치나 디스크를 구분해서 지정해 해결하는 것도 가능하다. - 하지만, MySQL 에서는 테이블의 파티션 단위로 인덱스를 생성하거나
파티션별로 다른 인덱스를 가지는 형태는 지원하지 않는다.
- 이력 데이터의 효율적인 관리
- 대부분의 애플리케이션은 로그 이력 데이터를 가지고 있다.
단기간에 대량으로 누적됨과 동시에 일정 기간이 지나면 쓸모가 없어진다. - 로그 데이터는 결국 시간이 지나면 별도로 아카이빙하거나 백업한 후 삭제해버리는게 일반적이다.
특히 다른 데이터에 비해 라이프 사이클이 상당히 짧은 것이 특징이다. - 로그 테이블에서 불필요해진 데이터를 백업하거나 삭제하는 작업은
일반 테이블에서는 상당히 고부하의 작업에 속한다. - 하지만 로그 테이블을 파티션 레이블로 관리한다면
불필요한 데이터 삭제 작업은 단순히 파티션을 추가하거나 삭제하는 방식으로 간단하고 빠르게 해결할 수 있다. - 대량의 데이터가 저장된 로그 테이블을 기간단위로 삭제한다면
MySQL 서버에 전체적으로 미치는 부하뿐만 아니라 로그 테이블 자체의 동시성에도 영향을 미칠 수 있다. - 하지만 파티션을 이용하면 이러한 문제를 해결할 수 있다.
- 대부분의 애플리케이션은 로그 이력 데이터를 가지고 있다.
MySQL 파티션의 내부 처리
파티션 테이블의 레코드 INSERT
INSERT 쿼리가 실행되면, 파티션 키를 기반으로 파티션 표현식을 평가하고
그 결과를 이용해 레코드가 저장될 적절한 파티션을 결정한다.
이후 해당 파티션 테이블에 데이터가 저장된다.
파티션 테이블의 UPDATE
UPDATE 쿼리를 실행하면, 변경 대상 레코드가 어느 파티션에 저장되어있는지 찾아야한다.
UPDATE의 WHERE 조건에 파티션 키 컬럼이 있다면
그 값을 이용해 레코드가 저장된 파티션에서 빠르게 대상 레코드를 검색할 수 있다.
하지만, WHERE 조건에 파티션 키 칼럼의 조건이 명시 되지 않았다면
MySQL 서버는 변경 대상 레코드를 찾기 위해 테이블의 모든 파티션을 검색해야한다.
그리고 실제 레코드를 변경하는 작업의 절차는 UPDATE 쿼리가 어떤 컬럼의 값을 변경하느냐에 따라 큰 차이가 생긴다.
- 파티션 키 이외의 컬럼만 변경될 때는
파티션이 적용되지 않은 일반 테이블과 마찬가지로 컬럼값만 변경한다. - 파티션 키 컬럼이 변경된다면, 레코드가 저장된 파티션에서 해당 레코드를 삭제한다.
그리고 변경되는 파티션 키 칼럼의 표현식을 평가하고,
그 결과를 이용해 레코드를 이동시킬 새로운 파티션을 결정해서 레코드를 새로 지정한다.
파티션 테이블의 검색
SQL 이 수행되기 위해 파티션 테이블을 검색할 때 성능에 크게 영향을 미치는 조건은 다음과 같다.
- WHERE 절의 조건으로 검색해야할 파티션을 선택할 수 있는가?
- WHERE 절의 조건이 인덱스를 효율적으로 사용(인덱스 레인지 스캔)할 수 있는가?
두번째는 일반 테이블에도 사용되는 이야기다.
파티션 테이블에서는 첫번째 선택사항의 결과에 의해
두번째 선택사항의 작업 내용이 달라질 수 있다.
파티션 선택 기능 + 인덱스 효율적 사용 가능
- 두 선택사항이 모두 사용 가능할 때 쿼리가 가장 효율적으로 처리될 수 있다.
이때는 파티션의 개수와 관계없이 검색을 위해 꼭 필요한 파티션의 인덱스만 레인지 스캔한다.
파티션 선택 불가 + 인덱스 효율적 사용 가능
- WHERE 일치하는 레코드가 저장된 파티션을 걸러낼 수 없기에
우선 테이블의 모든 파티션을 대상으로 검색해야한다.
하지만 각 파티션에 대해서는 인덱스 레인지 스캔을 사용할 수 있기에
최종적으로 테이블에 존재하는 모든 파티션의 개수만큼
인덱스 레인지 스캔을 수행해서 검색하게된다.
이 작업은 파티션 개수만큼의 테이블에 대해 인덱스 레인지 스캔을 한다음
결과를 병합해서 가져오는 것과 같다.
파티션 선택 가능 + 인덱스 효율적 사용 불가
- 검색하려는 레코드가 저장된 파티션을 선별할 수 있기에
파티션 개수와 관계없이 검색을 위해 필요한 파티션만 읽으면 된다.
하지만 인덱스는 이용할수 없기에 대상 파티션에 대해 풀 테이블 스캔을 한다.
이는 각 파티션의 레코드 건수가 많다면 상당히 느리게 처리될 것이다.
파티션 선택 불가 + 인덱스 효율적 사용 불가
- WHERE 조건에 일치하는 파티션을 선택할수가 없기에
테이블의 모든 파티션을 검색해야한다.
하지만 각 파티션을 검색하는 작업 자체도 인덱스 레인지 스캔을 사용할 수 없기에
풀 테이블 스캔을 수행해야한다.
3, 4번 상황의 경우 최대한 피하는 것이 좋으며
2번째 상황 또한 파티션 테이블의 데이터량이 많아지면 느려지므로 주의하자.
파티션 테이블의 인덱스 스캔과 정렬
MySQL의 파티션 테이블에서 인덱스는 전부 로컬 인덱스에 해당한다.
파티션을 나누면, 모든 인덱스는 파티션 단위로 생성되며
파티션과 관계없이 테이블 전체 단위로 글로벌하게 하나의 통합된 인덱스는 지원하지 않는다.
파티션되지 않은 테이블에서는 인덱스를 순서대로 읽으면
그 컬럼으로 정렬된 결과를 바로 얻을 수 있지만, 파티션된 테이블에서는 그렇지 않다.
그렇다면 인덱스 레인지 스캔을 수행하는 쿼리가 여러개의 파티션을 읽어야할 때
그 결과는 인덱스 컬럼으로 정렬이 될지 시험해보자.
두 파티션의 데이터를 합친후 정렬할 것이라 예상했지만 실제로는 표시되지는 않다.
실제 MySQL 서버는 여러 파티션에 대해 인덱스 스캔을 수행할 때
각 파티션으로부터 조건에 일치하는 레코드를 정렬 순서대로 읽으면서
우선순위 큐에 임시로 저장한다.
그리고 우선순위 큐에서 다시 필요한 순서대로 데이터를 가져가는 것이다.
이는 각 파티션에서 읽은 데이터가 이미 정렬돼 있는 상태라서 가능한 방법이다.
결론적으로 파티션 테이블에서 인덱스 스캔을 통해 레코드를 읽을 때
MySQL 서버가 별도의 정렬 작업을 수행하지 않는다.
하지만 일반 테이블의 인덱스 스캔처럼 결과를 바로 반환하는 것이 아니라
내부적으로 큐 처리가 한번 피룡한 것이다
Merge Sort 라고 표시한 부분이 바로 우선순위 큐 처리 작업을 의미한다.
파티션 프루닝
옵티마이저에 의해, 전체 파티션중에 일부 파티션에 접근하지 않아도 되는지 판단이 필요하다.
이렇게 최적화 단계에서 필요한 파티션만 골라내고
불필요한 것들은 실행 계획에서 배제하는 것을 파티션 프루닝이라고 한다.
이러한 파티션 프루닝 정보는 실행계획을 확인해보면 옵티마이저가 어떤 파티션만 접근하는지 알 수 있다.
EXPLAIN 명령의 결과에 보이는 partition 컬럼을 살펴보면 쿼리가 어떤 파티션만 조회하는지 확인할 수 있다.
주의사항
파티션의 제약 사항
우선 MySQL 서버의 파티션 기능의 제약 사항을 이해하려면 먼저 용어 몇 가지를 이해해야한다.
PARTITION BY RANGE 절은 테이블이 레인지 파티션을 사용한다는 것을 의미한다.
이제 MySQL 서버의 파티션이 가지는 제약 사항들을 살펴보자
- 스토어드 루틴이나 UDF, 사용자 변수 등을 파티션 표현식에 사용할 수 없다.
- 파티션 표현식은 일반적으로 컬럼 그 자체 또는 MySQL 내장 함수를 사용할 수 있는데
여기서 일부 함수들은 파티션 생성은 가능하지만 파티션 프루닝을 지원하지 않을 수 있다. - 프라이머리키를 포함해서 테이블의 모든 유니크 인덱스는 파티션 키 컬럼을 포함해야한다.
- 파티션된 테이블의 인덱스는 모두 로컬 인덱스며,
동일 테이블에 소속된 모든 파티션은 같은 구조의 인덱스만 가질 수 있다.
또한 파티션 개별로 인덱스를 변경하거나 추가할 수 없다. - 동일 테이블에 속한 모든 파티션은 동일 스토리지 엔진만 가질 수 있다.
- 파티션 생성 이후 MySQL 서버의 sql_mode 시스템 변수 변경은
데이터 파티션의 일관성을 깨뜨릴 수 있다. - 파티션 테이블에서는 외래키를 사용할 수 없다.
- 파티션 테이블은 전문 검색 인덱스 생성이나 전문 검색 쿼리를 사용할 수 없다.
- 공간 데이터를 저장하는 컬럼 타입(POINT, GEOMETRY, ...) 은
파티션 테이블에서 사용할 수 없다. - 임시 테이블(Temporary table)은 파티션 기능 사용할 수 없다.
일반적으로 파티션 테이블 생성할 때 가장 크게 영향을 미치는 제약사항은
모든 유니크 인덱스에 파티션 키 컬럼이 포함되어야 한다는 것이다.
(파티션 키로 사용되는 컬럼은 반드시 프라이머리키의 일부로 참여해야한다.)
한가지 더 주의할 사항은 MySQL 서버에서 파티션 표현식에는
기본적인 산술 연산자를 사용할수 있으며 내장 함수도 사용할 수 있다.
내장 함수들을 파티션 표현식에 사용할 수 있다고해서
이 내장 함수들이 모두 파티션 프루닝 기능을 지원하는 것은 아니다.
파티션 테이블을 처음 설계할 때는 파티션 프루닝 기능이 정상적으로 작동하는지 확인한 후
응용 프로그램에 적용하는 것을 권장한다.
파티션 사용시 주의사항
파티션의 목적은 작업의 범위를 좁히는 것이다.
유니크 인덱스는 중복 레코드에 대해 체크 자업 때문에 범위가 좁혀지지 않는다.
또한 MySQL 파티션은 일반 테이블과 같이 별도로 파일로 관리되는데
이와 관련하여 MySQL 서버가 조작할 수 있는 파일의 개수와 연관된 제약도 있다.
파티션과 유니크 키(프라이머리 키 포함)
종류와 관계없이 테이블에 유니크 인덱스가 있으면 파티션 키는
모든 유니크 인덱스의 일부 또는 모든 컬럼을 포함해야한다.
유니크 키에 대해서 파티션 키가 제대로 설정됐는지 간단히 체크하려면 각 유니크 키에 대해서
값이 주어졌을 때 해당 레코드가 어느 파티션에 저장돼 있는지 계산할 수 있어야 한다.
파티션과 open_files_limit 시스템 변수
MySQL 에서는 일반적으로 테이블을 파일 단위로 관리하기 때문에
MySQL 서버에서 동시에 오픈된 파일의 개수가 상당히 많아질 수 있다.
이를 제한하기 위해 open_files_limit 시스템 변수에
동시에 오픈할 수 있는 적절한 파일의 개수를 설정할 수 있다.
파티션 되지 않은 일반 테이블은 테이블 1개당 오픈된 파일의 개수가 2~3개 수준이지만
파티션 테이블에서는 파티션의 개수 * 2~3 개가 된다.
파티션을 많이 사용하는 경우에는 open_files_limit 시스템 변수를 적절히 높은 값으로 설정하자.
샤딩으로 쓰기 스케일링
최적화된 쿼리와, 대기열에 있는 쓰기 트래픽 증가를 관리할 수 없는 경우 샤딩이 다음 옵션이다.
샤딩은 더 많은 소스 호스트에서 동시에 더 많은 쓰기를 실행할 수 있도록 데이터를
각기 다른 더 작은 데이터베이스 클러스터로 분할하는 것을 의미한다.
샤딩 또는 파티셔닝에는 기능적 파티셔닌ㅇ과 데이터 샤딩이라는 2가지 유형이 있다.
기능적 파티셔닝 또는 업무 분담은
다른 노드를 다른 작업에 할당하는 것을 의미합니다.
사용자 레코드를 한 클러스터에 배치하고 청구서를 다른 클러스터에 두는 예를 들 수 있습니다.
이 접근 방식을 사용하면 각 클러스터를 독립적으로 확장할 수 있습니다.
사용자 등록의 급증은 사용자 클러스터에 부담을 줄 수 있다.
별도의 시스템을 사용하면 청구 클러스터의 로드가 줄어 고객에게 청구가 가능합니다.
반대로 청구 주기가 매월 1일면 사용자 등록에 영향을 미치지 않는다는 것을 알고 실행할 수 있습니다.
데이터 샤딩
오늘날 초대형 MySQL 애플리케이션을 확장하기 위한 가장 보편적이고
성공적인 접근방식 데이터를 더 작은 조각 또는 샤드로 분할하고 다른 노드에 저장함으로써 데이터를 샤딩합니다.
대부분의 애플리케이션은 샤딩이 필요한 데이터만 샤딩합니다.
대규모 애플리케이션에는 다르게 분할할 수 있는 여러 논리적 데이터 세트가 있을 수 있습니다.
다른 서버 세트에 저장할 수 있지만, 꼭 그럴 필요는 없습니다.
액세스 방법에 따라 동일한 데이터를 다양한 방법으로 샤딩할 수도 있습니다.
'필요한 부분만 샤딩'할 계획을 세울때는 주의해야합니다.
이 개념에는 빠르게 증가하는 데이터뿐만 아니라, 논리적으로 이에 속하고 동시에 정기적으로 쿼리될 데이터도 포함되어야합니다.
user_id를 기반으로 샤딩하는 경우 대부분의 쿼리에서 그 user_id 에 조인하는 다른 작은 테이블이 있다면 대부분의 애플리케이션 쿼리를 유지할 수 있도록 이러한 모든 테이블을 함께 샤딩하는 것이 좋습니다.
한번에 하나의 샤드에 대해 수행하고 데이터베이스 간 조인을 방지하기 때문입니다.
분할 방식 선택
샤딩의 가장 중요한 과제는 데이터를 찾고 검색하는 것입니다.
데이터를 찾는 방법은 샤딩 방법에 따라 다릅니다.
이를 수행하는 방법에는 여러가지가 있으며 각각의 편차가 있습니다.
목표는 가장 중요하고 번번한 쿼리가 가능한 적은 수의 샤드를 사용하게 하는 것입니다. (확장성의 원칙 중 하나는 노드 간 혼선을 방지하는 것입니다.) 이 프로세스에서 가장 중요한 부분을 데이터에 대한 파티셔닝 키를 선택하는 것입니다. 파티셔닝 키는 각 샤드에 어떤 행을 넣을지 결정합니다. 객체의 파티션 키를 알고 있다면 2가지 질문에 답할 수 있습니다.
- 이 데이터를 어디에 저장해야합니까?
- 가져올 데이터를 어디서 찾을 수 있습니까?
파티셔닝 키를 선택하고 사용하는 다양한 방법은 나중에 보여드리고 지금은 예를 들어보겠습니다. MySQL의 NDB 클러스터와 마찬가지로 각 테이블의 기본키 해시를 사용하여 모든 샤드에 겹쳐 데이터를 분할한다고 가정합시다.
이는 매우 간단한 접근 방식이지만 원하는 데이터에 대해 모든 샤드를 자주 확인해야하기에 확장성이 좋지 않다. (각 파티션마다 키 생성 전략이 별도로 있으면 어느 파티션 선택할지 모름)
기본키로 분할되기에 모든 샤드에 고르게 분산되어있을 수 있다. 기본키 해시를 사용하면 데이터를 저장할 위치를 쉽게 알 수 있지만 필요한 데이터와 기본키를 알고 있는지 여부에 따라 가져오기가 더 어려워 질 수 있다.
쿼리는 항상 하나의 샤드에 국한되어야한다. 데이터를 수평으로 샤딩할 때는 항상 샤드간에 쿼리를 수행하지 않아도 된다.
여러 샤드에 걸쳐 데이터를 결합하면 애플리케이션 계층이 복잡해지고 처음부터 데이터를 샤딩하는 이점이 사라집니다. 샤드 데이터 세트로 겪을 수 있는 최악의 경우는 원하는 데이터가 어디에 저장되는지 몰라서 샤드를 모두 스캔하는 경우다.
좋은 파티셔닝 키는 일반적으로 데이터베이스에서 매우 중요한 엔티티의 기본키입니다. 이 키는 샤딩 단위를 결정합니다. 예를 들어 사용자 ID 또는 클라이언트 ID 로 데이터를 분할할 경우, 샤딩 단위는 사용자 또는 클라이언트입니다.
시작하는 좋은 방법은 엔티티-관계 다이어그램 또는 모든 엔티티와 해당 관계를 표시하는 도구를 사용하여 데이터 모델을 도식화하는 것입니다. 관련 엔티티가 서로 가까이 있도록 다이어그램을 배치하십시오 이러한 다이어그램을 시각적으로 검사하고 놓쳤을지 모르는 파티셔닝 키 후보를 찾을 수 있습니다. 그러나 다이어그램만 보지 말고 애플리케이션의 쿼리도 고려하십시오 두 엔티티가 어떤식으로든 연관되어 있더라도 관계에 거의 또는 전혀 조인하지 않으면 관계를 끊고 샤딩으 구현할 수 있다.
엔티티 관계 그래프의 연결 정도에 따라 일부 데이터 모델은 다른 모델보다 샤딩하기 쉽습니다.
샤딩 하기 쉬운 구조의 데이터 모델은 대부분 단 하나의 연결만 있는 노드로 구성되고 연결이 많은 하위 그래프를 가지고 있으며 하위 그래프 간의 연결을 비교적 쉽게 잘라낼 수 있기에 샤딩하기가 쉽습니다.
반면 그물망 처럼 묶인 데이터 모델은 그런 하위 그래프가 없기 때문에 샤딩이 어렵습니다.
파티셔닝 키를 선택할 때는 샤드 간 쿼리를 최대한 피하면서 불균형하게 큰 데이터 청크 때문에 문제가 없을 정도로 샤드를 작게 만드는 것을 선택하십시오 샤드는 가능한 균일하고 작은 것이 좋으며, 그렇지 않은 경우 서로 다른 수의 샤드를 그룹으로 묶어 쉽게 균형을 맞출 수 있을 만큼 충분히 작아야합니다.
다중 파티셔닝 키
복잡한 데이터 모델은 데이터 샤딩을 더욱 어렵게 만듭니다. 많은 애플리케이션에는 특히 데이터에 둘 이상의 중요한 차원이 있는 경우 둘 이상의 파티셔닝 키를 가집니다.
다시 말해서, 애프릴케이션은 다양한 각도에서 데이터를 효율적이고 일관적으로 볼 필요가 있습니다. 이말은 곧 시스템내에 일부 데이터를 적어도 두번 저장해야 할 수도 있음을 의미합니다. 단일 샤드만 처리하기 위해 2가지 유형의 쿼리가 모두 필요한 경우 두가지 방식으로 샤딩해야합니다. (게시글의 댓글을 보고 싶다 == 사용자 + 게시글 기준이 필요)
여러개의 파티셔닝 키가 필요하다고 해서 완전히 중복된 데이터 저장소 2개를 설계해야하는 것은 아닙니다. 사용자 데이터용으로 하나의 샤드 데이터 저장소를 구축하고 책 데이터용으로 다른 하나를 구축할 수 있습니다. 댓글에는 사용자 ID와 게시물 ID가 모두 있으므로 샤드간의 경계를 넘습니다.
댓글을 완전히 복제하는 대신, 사용자 데이터와 함께 댓글을 저장할 수 있습니다. 이것은 두 데이터 저장소에 모두 액세스하지 않고도 책의 댓글에 대한 대부분의 보기를 렌더링하기에 충분할 수 있으며 전체 댓글 텍스트를 표시해야하는 경우 사용자 데이터 저장소에 검색할 수 있습니다.
샤드간의 쿼리
대부분의 샤드 애플리케이션에는 여러 샤드로부터 데이터를 수집하거나 결합해야하는 쿼리가 최소 몇개가 있을겁니다.
애플리케이션에서 단일 쿼리로 간주하는 것을 분할하고 샤드당 하나씩 여러개의 쿼리를 병렬로 분할하고 실행해야하므로 이러한 쿼리가 제대로 작동하도록 하는 것이 데이터 샤딩을 구현하는데 있어 가장 어려운 부분입니다.
우수한 데이터베이스 추상화 계층은 이러한 문제를 완화하는데 도움이 될 수 있지만, 이러한 쿼리는 샤드 내 쿼리보다 훨씬 느리고 비용이 많이 들기에 일반적으로 보다 적극적인 캐싱도 필요하다.
교차 샤드 쿼리가 표준치가 아닌 특이치를 검색하는 상황이면 선택한 샤딩 방식이 적합하다는 것을 알 수 있다. 쿼리를 가능한 한 단수하게 만들고 하나의 샤드에 포함되게 해야합니다. 일부 교차 샤드 집계가 필요하나 경우 해당 부분을 애플리케이션 로직의 일부로 구성하는 것이 좋습니다.
교챠 샤드 쿼리는 요약 테이블의 이점도 얻을 수 있습니다. 모든 샤드를 순회하고 완료되면 각 샤드에 중복으로 결과를 저장하여 구축할 수 있습니다. 각 샤드의 데이터를 복제하는 것이 너무 낭비인 경우 요약 테이블을 다른 데이터 저장소에 통합하여 한번만 저장할 수 있습니다.
샤딩되지 않은 데이터는 종종 전역 노드에 존재하며 로드로부터 보호하기 위해 많은 양의 캐싱을 사용합니다.
일부 애플리케이션은 일관된 데이터 배포가 중요하거나 적절한 파티셔닝 키가 없을 때 기본적으로 랜덤 샤딩을 이용합니다. 분산 검색 애플리케이션이 좋은 예입니다. 이 경우 교차 샤드 쿼리 및 집계는 예외가 아니라 일반적입니다.
샤드에 대한 쿼리 또한 샤딩을 이용햇을 때 어려운 사항중 하나입니다. 데이터 일관성을 유지하는 것도 어렵습니다. 외래키는 샤드에서 작동하지 않으므로 일반적인 솔루션은 애플리케이션에서 필요에 따라 참조 무결설을 확인하거나 샤드 내에서 외래키를 사용합니다.
샤드내의 내부 일관성이 가장 중요할 수 있기 때문입니다. XA 트랜잭션을 사용하는 것이 가능하지만, 오버헤드 대문에 실제로는 흔하지 않습니다.
간헐적으로 실행되는 정리 프로세스를 설계할 수도 있습니다. 주기적으로 실행되고 샤드 전체에서 데이터가 일관되는지 확인하는 검사기 스크립트를 작성할 수 있습니다.
샤딩과 파티셔닝 모두 지원하는 가장 인기 있는 오픈소스 2가지를 소개할 예정