반응형

Sharding의 정의

  • 같은 테이블 스키마를 가진 데이터를 다수의 데이터베이스에 분산하여 저장하는 방법
  • Application Level 에서도 가능 (RDBMS 에서 Sharding) 하지만, Databas Level에서도 가능 (ex-MongoDB / Redis 등)
  • Horizontal Partitioning (수평파티션) 이라고도 함

 

Sharding  적용

  • 프로그래밍, 운영적인 복잡도는 증가하고 높아지는 것을 뜻함
  • 가능하면 Sharding을 피하거나 지연시킬 수 있는 방법
    • Sacle Up
      • 하드스펙을 올리는 것
    • Read 부하가 크면
      • Replication 을 두어 Read 분산 처리
    • Table의 특정 컬럼만 사용 빈도수가 높다면
      • Vertically Partition(수직 파티션)을 진행
      • Data를 Hot, Warm, Cold data로 분리하여 처리
        • Memory DB를 활용
        • 테이블의 데이터 건수를 줄이는 것

Sharding 방식

  • Range Sharding (range based sharding)
    • key (shard key) 값에 따라서 range 를 나눠서 데이터 를 분배하는 방식
    • 비교적 간단하게 sharding이 가능
    • 증설에 재정렬 비용이 들지 않음
    • Shard key 의 선택이 중요
      • Shard key에 따라 일부 데이터가 몰릴 수 있음(hotspots)
      • 트래픽이 저조한 DB가 존재
  • Hash Sharding (hash based sharding)
    • key를 받아 해당 데이터를 hash 함수 결과로 분배
    • Hotspot를 방지하고 균등하게 분배
    • 재분배를 해야하는 경우(삭제 또는 추가) 전체 데이터를 다시 hash value를 이용하여 분배 (Migration 에 어려움)
  • Directory Based Sharding
    • shard내 어떤 데이터가 존재하는 지 추적할 수 있는 lookup table이 존재
    • range based sharding 과 비슷하지만, 특별한 기준에 의해 shard를 나눈 것이라, 동적으로 shard를 구성 가능
      • range나 hash 모두 정적인 반면, 해당 sharding은 유연성 있게 임의로 나누는 것이라 유연성을 갖춤
    • 쿼리 할 때 모두 lookup table 를 참조하기 때문에 lookup table이 문제를 일으킬 소지를 보유
      • lookup table이 hot spot 가능성
      • lookup table이 손상되면 문제 발생

 

MongoDB  Sharding

 

  • 분산 처리를 통한 효율성 향상이 가장 큰 목적이므로 3대 이상의 샤드 서버로 구축을 권장(최소 2대)
  • 싱글 노드 운영보다 최소 20~30% 추가 메모리 요구 (MongoS와 OpLog, Balancer 프로세스가 사용하게 될 추가 메모리 고려)
  • 샤드 시스템에 구축되는 config 서버는 최소 3대 이상 활성화를 권장.
    • Config 서버는 샤드 시스템 구축과 관련된 메타 데이터를 저장 관리하며 빠른 검색을 위한 인덱스 정보를 저장, 관리하고 있기 때문
    • 샤드 서버와는 별도의 서버에 구축이 원칙
    • Config 서버는 샤드 서버보다 저사양의 시스템으로 구축 가능

Config Server

 

  • Config 서버는 샤딩 시스템의 필수 구조
  • 최소 1대가 요구되며 장애로 인해 서비스가 중지되는 것을 피하기 위해 추가로 Config 서버 설정이 필요.(HA 구성 필요-PSS-필수)
  • Config 서버는 각 샤드 서버에 데이터들이 어떻게 분산 저장되어 있는지에 대한 Meta Data가 저장 (Shard 정보를 저장 관리)
    • Shard Meta 정보
      • MongoS가 처리하는 Chunk 단위로 된 chunk 리스트와 chunk들을의 range 정보를 보유
    • 분산 Lock
      • MongoS들 간의 config 서버와의 데이터 통신 동기화를 위해 도입
      • 샤딩을 수행할 연산들에 대해 분산 락을 사용
        • 여러개의 mongos가 동시에 동일한 chunk에 대한 작업을 시도하는 등의 이슈를 방지하기 위함
        • 작업을 수행하기 전 config server의 locks collection 의  lock을 획득 후에만 작업 가능
repl_conf:PRIMARY> db.locks.find() 
{ "_id" : "config", "state" : 0, "process" : "ConfigServer", "ts" : ObjectId("5d6b7f15a9f5ecd49052a36f"), "when" : ISODate("2019-09-01T08:19:33.165Z"), "who" : "ConfigServer:conn164", "why" : "createCollection" } 
{ "_id" : "config.system.sessions", "state" : 0, "process" : "ConfigServer", "ts" : ObjectId("5d6b7f15a9f5ecd49052a376"), "when" : ISODate("2019-09-01T08:19:33.172Z"), "who" : "ConfigServer:conn164", "why" : "createCollection" } 
{ "_id" : "testdb", "state" : 0, "process" : "ConfigServer", "ts" : ObjectId("5d62889d3ed72a6b6729a5ca"), "when" : ISODate("2019-08-25T13:09:49.728Z"), "who" : "ConfigServer:conn24", "why" : "shardCollection" } 
{ "_id" : "testdb.testCollection2", "state" : 0, "process" : "ConfigServer", "ts" : ObjectId("5d62889d3ed72a6b6729a5d1"), "when" : ISODate("2019-08-25T13:09:49.738Z"), "who" : "ConfigServer:conn24", "why" : "shardCollection" } 
{ "_id" : "test.testCollection2", "state" : 0, "process" : "ConfigServer", "ts" : ObjectId("5d6288ab3ed72a6b6729a65a"), "when" : ISODate("2019-08-25T13:10:03.834Z"), "who" : "ConfigServer:conn24", "why" : "shardCollection" }

 

      • Lock 역할
        • 밸런서(balancer)의 연산
        • Collection 의 분할(split)
        • Collection 이관(migration)

          • LockPinger : 해당 쓰레드는 30초 주기로 config 서버와 통신
    • 복제 집합 정보 : MongoS가 관리, 접속해야 하는 Mongo Shard 정보
  • MongoS가 데이터를 쓰고/읽기 작업을 수행할 때 Config 서버는 MongoS를 통해 데이터를 동기화-수집 진행

 

MongoS

 

  • 데이터를 Shard 서버로 분배해 주는 프로세스 (Router-Balancer)
    • Data를 분산하다 보면 작업의 일관성을 위하여 Lock을 사용
    • 이때 Chunk Size를 적절하게 설계하지 못하면 Migration 때문에 성능 저하 현상이 발생 가능성
    • DB 사용량이 적은 시간대 Balancer를 동작시키고 그 외 시간에는 끄는 방법도 성능 향상의 방법
  • 하나 이상의 프로세스가 활성화  가능(여러대의 MongoS를 운영 가능)
  • Application Server에서 실행 가능 (Application에서 직접적으로 접속하는 주체이며,독립적인 서버로 동작 가능하며,  Application 서버 내에서도 API 형태로 실행 가능)
    • MongoS를 Application Server 서버 local 에 설치하는 것을 추천
      (application server 가 별도의 라우터를 네트워크 공유 안하고, Local에서 직접 접근하기 때문에 효율성 증가. 별도의 서버를 구축 하지 않아서 서버 비용 절감. 단, 관리 포인트로 인한 문제점도 존재)

  • Config 서버로부터 Meta-Data를 Caching
    • read, write 작업시 해당 샤드를 찾을 수 있도록 캐쉬할 수 있는 기능을 제공
root@7d536b10b886:/# mongos --configdb config-replica-set/mongo1:27019,mongo2:27019 --bind_ip 0.0.0.0

 

  • MongoS가 실행될 때 Config 서버를 등록
    • Config 서버와 연결되면 샤딩 정책을 포함한 메타 정보를 연결된 모든 Config 서버에 전송
  • MongoS는 Config Server 와 연결하게 되는데, Config Server가 여러 대인 경우 여러 대 중 하나라도 연결이 안되면 MongoS 는 연결 실패로 MongoS가 실행되지 않음
  • MongoS 내에서는 데이터를 저장하지 않으며, 다른 MongoS간 연결이 없기 때문에 데이터 동기화를 위해 Config 서버를 이용
  • MongoS 의 쿼리 클러스터 라우팅 방법
    1. 쿼리를 보내야 하는 샤드 리스트를 결정
    2. 대상되는 샤드에 커서를 설정
    3. Target Shard에 보낸 결과를 받아 데이터를 병합하고 해당 결과를 Client로 Return
    4. Mongo3.6에서는, 집계 쿼리의 경우 각 Shard에서 작업하는 것이 아닌, Mongos에서 결과를 받아 merge 후 작업하여 리턴하는 형태로 변경
    5. MongoS에서 Pipline을 운영할 수 없는 2가지 경우
      1. 분할 파이프라인의 병합 부분에 Primary shard 에서 동작해야하는 부분이 포함되어 있는 경우
        • $lookup 집계가 실행중인 Shard Collection 과 동일한 Database 내에 있는 unshared collection과 진행 된다면, 병합은 Primary shard에서 실행해야 함
      2. 분할 파이프라인의 $group과 같은 Temporary data를 Disk에 기록해야 하는 경우가 포함된 경우, 또한 Client는 allowDiskUse를 True 지정했을 경우 Mongos를 사용할 수 없음
      3. 이런 경우, Merged 파이프라인은 Primary shard에서 하지 않고, 병합 대상인 샤드들 중 무작위로 선택된 샤드에서 실행
      • Shard cluster 에서 Aggregation 쿼리가 어떻게 동작하고 싶은지 알고 싶으면, explain:true 로 설정하여 aggregation을 실행하여 확인 가능
      • mergeType은 병합 단계에서 ("primaryShard", "anyShard", or "mongos") 로 보여주며,  분할 파이프라인은 개별샤드에서 실행된 작업을 리턴.
      • https://docs.mongodb.com/manual/core/sharded-cluster-query-router/ 참고

 

 

Chunk

  • collection을 여러 조각으로 파티션하고 각 조각을 여러 샤드 서버에 분산해서 저장하는데, 이 데이터 조각을 Chunk라고 함
  • chunk는 각 샤드서버에 균등하게 저장되어야 좋은 성능을 낼 수 있음
  • 균등하게 저장하기 위해 큰 청크를 작은 청크로 Split 하고 청크가 많은 샤드에서는 적은 샤드로 Chunk Migration 을 수행
  • 청크 사이즈는 Default 64Mb 이며 size를  변경도 가능 (또는 100,000 row)
    • Chunk size 변경 시 유의사항
      1. Chunk size를 작게하면 빈번한 마이그레이션이 동작하여 성능은 저하가 발생할 수 있으나, 데이터를 고르게 분배할 수 있는 효과를 볼 수 있다. (추가로 mongos에서 추가 비용이 발생)
      2. 청크 사이즈를 크게하면 마이그레이션 빈도는 줄어들어 네트워크나 mongos 에서 오버헤드 측면에서 효율적.그러나 잠재적으로 분포의 불균형이 발생 가능성
      3. 청크 사이즈는 마이그레이션할 청크 내 document 수와 비례(청크가 클수록 저장되는 document 수가 증가)
      4. 청크 사이즈는 기존 Collection을 분할할 때 최대 컬렉션 크기에 따라 영향. 샤딩 이후 청크 사이즈는 컬렉션 크기에 영향 없음
  • Chunk Split
    • 샤드 내 Chunk의 사이즈가 너무 커지는 것을 막기 위해 split 이 발생
    • 청크가 지정된 청크 사이즈를 초과하거나, 청크내의 문서 수가 마이그레이션할 청크당 최대 문서 수를 초과할 경우 발생
    • 이 때 split는 샤드 키 값을 기준으로 진행
    • Insert 또는 update 시 split 이 발생
    • split 이 발생하면 메타 데이터 변경이 발생하며, 데이터의 균등함을 가져온다
    • Split 을 한다고 해도 청크가 샤드에 고르게 분포되지 않을 수 있음
      • 이때 밸런서가 여러 샤드에 존재하는 청크를 재분배
      • 클러스터 밸런서 참고
  • Chunk Migration
    • 여러 Shard 로 나누어진 청크를 샤드간 균등하게 분배하기 위하여 Migration 을 진행
      1. Balance 프로세스가 moveChunk 명령을 Source 샤드로 명령(Chunk Migration이 필요한 Shard=Source Shard)
      2. Source Shard는 moveChunk 명령으로 이동 시작
        • 이동하는 Chunk는 라우팅 되어 동작하며, 경로에 대한 내역은 Source Shard에 저장(수신에 대한 내역)
      3. Target Shard는 해당 Chunk 관련한 Index를 생성(build)
      4. Target Shard는 Chunk 내의 Document 요청을 하고, 해당 데이터 사본을 수신 시작
      5. Chunk에서 최종(원본) Docuemnt를 수신한 후 Migration 간 변경된 내역이 있는지 확인하기 위하여 다시 한번 Target Shard는 동기화 프로세스를 시작
      6. 완전히 동기화가 완료되면, Config Server에 연결하여 Cluster Meta 데이터를 청크의 새 위치로 업데이트 진행(mongoS가 관여 하지 않을까....의견)
      7. Source Shard가 Meta 정보를 업데이트를 완료하고 Chunk에 열린 커서가 없으면 Source Shard는 Migration 대상을 삭제 진행
    • 샤드에서 여러 청크를 마이그레이션 하기 위해서는 밸런서는 한 번에 하나씩 청크를 마이그레이션 진행
      • 3.4에서부터는 병렬 청크 마이그레이션을 수행 가능. 단, 샤드가 한 번에 최대 하나만 참여하지만, 샤드가 n 개인 여러개의 샤딩된 클러스터의 경우 최대 샤드 n개/2 만큼 동시 Migration을 진행 가능
        • 클러스터 내 Chunk Migration은 한번만 가능했지만, 3.4부터는 Cluster 내 최대 n/2 개의 Chunk Migration은 가능(단 하나의 샤드당 하나의 Chunk Migration만 가능)
    • 하지만, 밸런서는 청크를 이동 후 이동한 청크를 삭제에 대해서(삭제단계)는 기다리지 않고 다음 마이그레이션 진행 (비동기식 청크 마이그레이션 삭제)
    • attemptToBalanceJumboChunks 라는 밸런서 설정을 하면, 마이그레이션 하기 너무 큰 청크는 밸런서가 이동 시키지 않음
    • Migration 조건
      • 수동
        • 대량 Insert 중 데이터를 배포해야 하는 경우 제한적으로 사용
        • 수동 마이그레이션 참조
      • 자동
        • 밸런서 프로세스가 Collection 내 Chunk들이 파편화가 발생하여 고르게 분포 되지 않았다고 판단 될 때 Chunk를 이동 시킴
        • 측정 임계값
          • 하나의 샤드 서번에 8개의 Chunk가 발생하면 다른 서버로 Migration이 발생하는데 20개 미만의 Chunk가 발생하면 평균 2번 정도의 Migration 이 발생
          • Migration이 빈번하게 발생하면 Chunk를 이동하기 위한 작업이 수시로 발생하기 때문에 성능 지연현상을 발생 시킬 수 있음
          • 적절한 빈도의 Migration이 발생되기 위해서는 적절한 Chunk Size를 할당이 필요

Chunk 수

Chunk Migration 수

1 ~20개 미만

2

21 ~ 80 개 미만

4

80개 이상

8

 

Sharded Cluster Balancer

https://docs.mongodb.com/manual/core/sharding-balancer-administration/#sharding-internals-balancing

  • 각 Shard의 Chunk 수를 모니터링하는 백그라운드 프로세스
  • 샤드의 청크 수가 Migration 임계치 값에 도달 하면 밸런서가 샤드간에 Chunk를 자동으로 Migration하고 샤드 당 동일한 수의 청크를 유지
    • 샤드된 컬렉션의 청크를 모든 샤드 컬렉션에 균등하게 재분배하는 역할(밸러서는 Default로 enable)
    • 샤드간 청크가 균등하게 유지될 때까지 밸런서가 동작 (Migration 는 위의 Chunk Migration 동작을 참고)
  • 밸런싱 절차는 사용자 및 어플리케이션 계층에서 투명하게 동작하지만, Migration이 진행되는 동안에는 부하가 발생
  • 밸런서는 Config server Replica set에서 Primary에서 동작
  • 유지보수를 위해 밸런서를 비활성화도 가능하며 수행 시간을 설정도 가능
  • balancing 작업이 02:00 에 자동으로 수행
#Config 서버에서 동작을 확인 가능

repl_conf:PRIMARY> db.settings.update (

... {_id:"balancer"},

... {$set : {activeWindow: {start: "02:00", stop : "06:00" } } },

... {upsert : true } )

WriteResult({ "nMatched" : 0, "nUpserted" : 1, "nModified" : 0, "_id" : "balanver" })

repl_conf:PRIMARY> db.settings.find()

{ "_id" : "chunksize", "value" : 64 }

{ "_id" : "balancer", "stopped" : false }

{ "_id" : "autosplit", "enabled" : true }

{ "_id" : "balanver", "activeWindow" : { "start" : "02:00", "stop" : "06:00" } }
  • 샤드 추가 및 제거
    • 샤드를 추가하게 되면 새로운 샤드에는 Chunk가 없기 때문에 불균형이 발생
    • 클러스터에서 샤드를 제거하면 상주하는 청크가 클러스터 전체로 재분배가 일어나므로 샤드 추가와 같이 불균형이 발생
    • 샤드를 추가하거나 삭제 모두 마이그레이션 하기 시작하면 시간이 소요
    • 효율적인 방안은 좀 더 조사가 필요

 

Sharding System 주의점

  1. 하나의 Shard 서버에 데이터가 집중되고 균등 분산이 안 되는 경우
    • Shard Key로 설정된 필드의 Cardinality가 낮은 경우에 Chunk Size는 반드시 64Mb 단위로 분할되는 것이 아님
      • 때로는 64Mb보다 훨씬 큰 크기의 Chunk 크기로 생성되기도 함
      • Default로 8개 정도의 Chunk가 발생하면 Migration이 발생하기 때문에 다른 서버로 Chunk를 이동하는 횟수는 줄어들게 되고 자연스럽게 하나의 샤드 서버에 만 데이터 집중되는 현상 발생
    • 적절한 Shard Key를 선택하지 못한 경우 발생하는 문제점(Shard Key의 중요성)
    • 혹여나 균등 분산이 안된다고 판단되면 Chunk Size를 줄이는 것을 추천(Migration 빈도수가 높아 져 균등하게 분활은 되나 성능 저하 발생)
  2. 특정 Shard 서버에 IO 트래픽이 증가하는 경우
    • MongoDB의 샤드 서버는 동일한 Shard Key를 가진 데이터들을 같은 샤드 서버에 저장하기 위해 Split 과 Migration 수행
    • Shard 서버의 IO 트래픽이 증가하는 이유는 너무 낮은 Cardinality 를 가진 Field를 설정 때문
    • 데이터가 집중적으로 저장되어 있는 Chunk를 Hot Chunk라고 하며, 특정 서버에 집중되어 있을 때 상대적으로 서버 IO 트래픽이 증가
  3. 샤드 클러스터의 밸런스가 균등하지 않는 경우
    • 데이터를 입력할 때 로드 밸런싱이 적절하게 수행되었지만 사용자의 필요에 따라 특정 서버의 데이터를 삭제 또는 다른 저장 공간으로 이동했다면 Balancer가 깨지게 됨
    • Shard key 의 낮은 Cadinality
    • 하나의 Collection에 대량의 Insert 되는 것 보다 분산 저장되는 속도가 늦는 경우 밸런스 불균형 발생
    • Insert 되는 속도에 비해 Chunk Migration이 빈약한 네트워크 대역폭으로 인해 빠르게 이동되지 못하는 경우 발생
    • 하루 일과 중 Peak 시간에 빈번한 Migration이 발생하게 되면 시스템 자원의 효율성이 떨어지게 되어 성능 지연 발생
      • 피크 시간에 Chunk Migration을 중지하고 유휴 시간에 작업 될 수 있도록 Balance 설정
  4. 과도한 Chunk Migration이 클러스트 동작을 멈추는 경우
    • 빈번한 Chunk Migration이 일시적으로 Cluster 서버 전체의 성능 지연 문제를 유발
    • 빈번한 Migration 회수를 줄일 수 없다면 유휴 시간에 작업 될 수 있도록 Balance 설정
    • 불필요하게 큰 Chunk Size는 네트워크 트래픽을 증가시키고 시스템 자원의 효율성을 저하 시키는 원인이 될 수 있으므로 Chunk size를 줄이는 것 고려
    • Shard 서버의 밸런싱이 적절하지 않다는 것은 Shard 서버의 수가 처리하려는 데이터 발생 양에 비해 부족하기 때문이므로 샤드 서버 대수 증설 고려
  5. 쓰기 성능이 지연되고 빠른 검색이 안 되는 경우
    • 초당 몇 만 건 이상의 데이터들이 동시에 저장되기 위해 Collection의 크기가 중요
    • 하나의 Collection은 여러개의 익스텐트 구조로 생성되는데, 익스텐트 사이즈가 작게 생성되어 있으면 잦은 익스텐트 할당으로 인해 불필요한 대기 시간이 발생
    • 충분한 익스텐트 크기로 생성(createCollection 을 이용하여 Collection을 명시적으로 생성하면서 size를 조정 가능)
    • 빠른 쓰기 성능이 요구되는 경우 Rich Docuemnt 구조로 설계하는 것이 유리  (Data Model Design 참고 /  Embedded Document도 고려)
    • 메모리 부족으로 인한 성능 저하로 메모리 증설도 고려
  • Shard Key 의 중요성,(1~5) Balancer는 유휴 시간대에 작업 추천(3), 적절한 Shard 수(4), 메모리도 체크(5)

참고

반응형

+ Recent posts