만약 컬렉션이 한 개의 노드에서 담당하기에 너무 크다면, 다수의 샤드 를 생성하여 여기에 부분적으로 저장할 수 있다.
샤드는 컬렉션의 논리적 분할단위 이며, 컬렉션으로부터 Document의 부분을 포함하며, 그러므로 컬렉션의 모든 Document는 정확히 한 샤드를 가지고 있다. 컬렉션의 각 Document를 포함하고 있는 샤드는 해당 컬렉션의 전반적인 '샤딩'정책에 의존적이다. 예를 들어, 당신은 각각 문서가 "Country" 필드단위로 샤드되어 동일한 country 가 co-located 된 컬렉션을 가질지도 모른다. 별개의 컬렉션은 그것의 샤드를 결정하기 위해 uniqueKey의 "해시"를 사용한다.
SolrCloud 이전에는, Solr은 여러 샤드에 걸쳐 실행되는 한 질의를 허용하는 방식의 분산 검색을 지원했기에, 질의는 전체 Solr 인덱스를 대상으로 실행되었고 어떠한 Document도 검색 결과에서 누락되지 않았다. 그러므로 인덱스를 샤드 전반에 쪼개는 것이 SolrCloud의 유일한 개념은 아니다. 분산 처리에 있어 개선이 필요한 여러 문제를 SolrCloud로 개선하고자 한다
1. 인덱스를 샤드로 쪼개어 넣는 작업이 다소 수동적
2. 분산 인덱싱에 대한 지원, 즉 특정 샤드로 특정 document를 전송하는, 이 없음. Solr은 Document를 전송할 어떠한 샤드를 특정해서 알아낼 수 없다
3. 부하분산 또는 대체작동에 대한 지원이 없다. 그러므로 만약 극 대다수의 질의를 받게 된다면 해당 질의를 보낼 곳을 알아내야 하고, 만약 한 샤드가 죽는다면 그것은 곧 데이터의 소실이다.
SolrCloud는 위의 모든 문제를 해결했다. 인덱스 처리와 질의 에 대한 자동 분산을 지원하고, 주키퍼로 부하분산 및 대체작동을 지원한다. 덧붙여, 모든 샤드는 추가적인 견고성 확보를 위해 다수의 레플리카를 가질 수 있다.
SolrCloud는 Master 또는 Slave가 없다. 대신에, 모든 샤드는 적어도 하나의 물리적 레플리카 를 가지며, 그 중 하나는 리더가 된다. 리더는 자동적으로 선출되며, 최초에는 first-come-first-served 에 기반하며, 이후에는 주키퍼의 프로세스에 기반한다.
만약 리더가 죽게되면, 다른 레플리카 중 하나가 새 리더로 선출된다.
Document가 인덱싱 작업을 위해 Solr 노드에 전송될 때, 시스템은 먼저 어떤 샤드가 이 Document를 소유할 것인지 결정하고, 어떤 노드가 해당 샤드의 리더인지 찾는다. Document는 인덱싱을 위해 현재 리더에게 전송되고, 리더는 다른 모든 레플리카에 갱신사항을 전달한다.
도큐먼트 라우팅
Solr는 컬렉션을 생성할 때 router.name 파라미터를 설정함으로써 라우터 기능을 사용할 수 있게 한다. 만약 "compositeID"(기본값) 라우터를 사용한다면, Document ID에 접두어를 붙여 Document와 함께 전송할 수 있다. 이 때 쓰이는 접두어는 Solr가 인덱싱을 위해 Document를 보낼 샤드를 결정하는데 쓰일 해시값을 계산하는데 사용된다. 접두어는 사용자가 원하는 어떠한 문구라도 가능하지만(샤드 이름은 빼고), 반드시 일관성이 있어야 Solr 또한 일관성 있게 동작한다. 예를 들면, 만약 "고객"에 대해 Document를 co-locate하길 원한다면, 고객이름 또는 ID를 접두어로 사용할 수 있다. 만약 고객이 "IBM"이고 Document ID가 "12345"인 Document라면 Document ID 필드에 이와 같은 접두어를 사용할 수 있다.: "IBM!12345". 느낌표 부호가 중요한데, 이는 Document가 어떤 샤드에 직접적인지 결정하는데 사용된다.
질의시에는 특정 샤드에 직접적으로 질의하기 위해 질의문에 _route_ 파라미터를 통해 접두어를 포함한다. (i.e., q=solr@_route_=IBM!). 일부 상황에서는 이것이 모든 샤드에 질의할 때 발생하는 네트워크 지연을 극복하기 때문에 질의 성능을 개선할 것이다.
compositeId 라우터는 2단계 라우팅을 포함하는 접두어를 지원한다. 예를 들면, 첫번째는 지역, 두번째는 고객 으로 구성되어 있는 접두어 라우팅: "USA!IBM!12345"
고객 "IBM"이 다량의 Document를 보유하고 있고 사용자가 이를 다수의 샤드에 퍼뜨리길 원할 수도 있다. 이 경우는 이 문법이 가능하다.: "shard_key/num!document_id"
/num 은 해시 합성에 사용할 비트의 갯수를 뜻한다.
그러므로 "IBM/3!12345"는 3개의 비트를 샤드 키로부터 사용하고 29개의 비트는 유일한 doc id로 사용하여, 컬렉션의 1/8 에 해당하는 샤드에 이를 퍼뜨린다. 이와같이, 만약 num value가 2라면 1/4개의 샤드에 document가 퍼진다. 질의시에, 사용자는 특정 샤드에 직접적으로 질의수행을 위해 비트 갯수가 포함된 질의, 즉 _route_ 파라미터가 포함된 질의(i.e., q=solr&_route_=IBM/3!)를 포함해야 한다.
만약 Document가 어떻게 저장되었는지에 대해 영향을 받고싶지 않다면, 사용자는 document ID에 접두어를 특정할 필요가 없다.
만약 "implicit"라우터가 지정된 컬렉션을 생성했다면, document가 속해있는 샤드를 식별하기 위해 각 document의 필드를 사용하고자 router.field 파라미터를 추가적으로 설정할 수 있다. 그러나, 만약 선언된 필드가 document에 누락되었다면, document는 거절될 것이다. 또한, 특정 샤드를 명명하기 위해 _route_ 파라미터를 사용할 수 있다.
샤드 분할
SolrCloud에서 컬렉션을 생성할 때, 최초 사용될 샤드의 갯수를 결정한다. 그러나 미래에 사용자가 필요할 샤드의 갯수를 미리 알기란 어려운 일이고, 특히 요구사항이 순간순간 변화될 때, 그리고 사용자의 선택이 틀렸음을 나중에 알게되었을 때 새로운 코어를 생성하고 사용자의 데이터를 재-인덱싱하는것을 포함하여 잘못된 부분을 바로잡기 위한 비용은 비쌀 수 있다.
샤드 분할을 위한 기능은 컬렉션 API에 있다. 이는 현재 샤드를 두 개로 쪼개는 것을 허용한다. 현재 존재하고 있는 샤드는 있는 그대로 남게 되므로, 분할동작은 새 샤드로 두 개의 복사본을 효율적으로 만든다. 사용자가 준비만 되어있다면 이 동작 이전의 샤드는 나중에 삭제할 수 있다.
SolrCloud의 클라이언트 어플리케이션으로부터 커밋을 무시
SolrCloud 모드를 사용하는 대부분의 경우에, 인덱싱을 하는 클라이언트 어플리케이션은 명시적 커밋 요청을 전송하지 않아야 한다. 대신, 검색 요청에서 최신 변경사항을 보이도록 하기 위해 openSearcher=false 로 자동 커밋과 자동 소프트커밋을 설정해야 한다. 이는 자동 커밋이 클러스터의 일반 일정에 반영되어 있음을 보장한다. 명시적 커밋을 보내지 않아야 하는 클라이언트 어플리케이션에 정책을 강제설정하기 위해, 사용자는 모든 클라이언트 어플리케이션에 SolrCloud의 인덱스 데이터를 최신화해야 한다. 그러나, 이는 언제나 실행 가능한 부분은 아니므로, Solr은 IgnoreCommitOptimizeUpdateProcessorFactory를 제공한다. 이는 명시적 커밋을 무시(하고/하거나) 사용자의 클라이언트 어플리케이션 코드의 수정을 요구하지 않고 요청을 최적화한다.
Solr 노드가 검색요청을 수신하면, 그 요청은 검색의 대상이 되는 컬렉션의 일부인 몇몇의 샤드의 레플리카로 라우트된다. 선택된 레플리카는 집합기의 역할을 수행할 것이다: 컬렉션의 모든 샤드 대상으로 레플리카를 임의 선택하기 위한 내부 요청 생성, 응답 조정, 필요에 따라 이후의 내부 요청 발행 (예를 들어, Facet 값을 정제하기 위해, 또는 저장된 필드의 추가적인 요청), 클라이언트를 위한 최종 응답 생성
샤드의 질의담당 제한
SolrCloud 사용의 장점 중 하나는 여러 샤드에 분산된 매우 큰 크기의 컬렉션을 대상으로 질의를 할 수 있는 능력이지만, 몇몇의 경우 사용자는 단지 샤드의 일부로부터의 결과에만 관심이 있다는 걸 알게된다. 모든 데이터를 대상으로 질의를 할 것인지, 또는 부분을 대상으로 할 것인지 선택 가능하다.
컬렉션의 모든 샤드 대상의 질의는 친숙하게 보인다;
http:
//localhost:8983/solr/gettingstarted/select?q=*:*
반면에, 만약 사용자가 단지 한 샤드를 대상으로 질의를 하기만을 원한다면, 아래와 같이 논리적 ID를 사용해 샤드를 특정할 수 있다.
http:
//localhost:8983/solr/gettingstarted/select?q=*:*&shards=shard1
만약 사용자가 다수의 샤드 ID를 대상으로 질의를 하고 싶다면, 아래와 같이 그룹핑을 해서 특정할 수 있다.
http:
//localhost:8983/solr/gettingstarted/select?q=*:*&shards=shard1,shard2
위의 두 예시에서, 샤드ID는 해당 샤드의 임의의 레플리카를 선택할 것이다.
아래와 같이 샤드ID의 특정 레플리카를 지정할 수도 있다.
http:
//localhost:8983/solr/gettingstarted/select?q=*:*&shards=localhost:7574/solr/gettingstarted,localhost:8983/solr/gettingstarted
또는, Pipe 기호(|) 를 이용하여 단일 샤드의 몇몇 레플리카를 특정할 수도 있다. (부하분산을 목적으로)
http:
//localhost:8983/solr/gettingstarted/select?q=*:*&shards=localhost:7574/solr/gettingstarted|localhost:7500/solr/gettingstarted
그리고 당연하게도, 레플리카들의 리스트(Pipe로 구분된)로 정의된 샤드 리스트(쉼표로 구분된)를 특정할 수도 있다. 이 예시에서는, 두 개의 샤드가 질의되는데, 첫 번째 샤드는 shard1의 임의의 레플리카에서 수행되고, 두 번째 샤드는 pipe로 구분된 리스트로부터 선택된 임의의 레플리카에서 수행된다 :
http:
//localhost:8983/solr/gettingstarted/select?q=*:*&shards=shard1,localhost:7574/solr/gettingstarted|localhost:7500/solr/gettingstarted
ShaerdHandlerFactory 설정
사용자는 Solr의 분산 질의에 있어 동시성과 스레드 풀링의 차원을 직접적으로 설정할 수 있다. 이는 더욱 정제된 제어를 허용하고 사용자 특정 요구사항을 충족할 수 있게 한다. 기본값은 지연을 초과하는 스루풋의 처리에 찬성한다.
표준 핸들러를 설정하기 위해서, solrconfig.xml에 이와 같이 설정한다:
<
requestHandler
name
=
"standard"
class
=
"solr.SearchHandler"
default
=
"true"
>
<!-- other params go here -->
<
shardHandler
class
=
"HttpShardHandlerFactory"
>
<
int
name
=
"socketTimeOut"
>1000</
int
>
<
int
name
=
"connTimeOut"
>5000</
int
>
</
shardHandler
>
</
requestHandler
>
statsCache 설정 (분산 IDF)
Document와 기간 통계는 관련성을 계산하기 위해 필요하다. Solr은 document 지수를 연산하기 위해 네 가지 implementations를 제공한다.
- LocalStatsCache
- ExactStatsCache
- ExactSharedStatsCache
- LRUStatsCache
solrconfig.xml에 <statsCache> 설정을 통해 implementation를 선택할 수 있다. 예를 들어, 아래는 Solr가 ExactStatsCach를 사용하도록 설정한 것이다.
<
statsCache
class
=
"org.apache.solr.search.stats.ExactStatsCache"
/>
분산 데드락 회피
각각의 샤드는 최상위 질의요청을 제공한 후에 차상위 요청을 만들어 다른 모든 샤드들에 전달한다. HTTP 요청을 처리할 스레드 최대 갯수는 최상위 클라이언트와 다른 샤드들로부터의 요청을 담당할 갯수보다 많아야 한다. 만약 이 부분이 만족되지 않는다면 분산 데드락을 야기할 수 있다.
예를 들어, 두 개의 샤드의 경우 데드락이 발생될 수 있는데, 각 샤드는 오직 하나의 스레드만이 HTTP요청을 처리한다. 이 두 스레드는 동시적으로 최상위 요청을 수신할 수 있고, 서로를 향해 하위요청을 만들 수 있다. 추가적인 요청의 처리를 담당할 스레드가 더 이상 남아있지 않기 때문에, 들어오는 요청들은 다른 미결중인 요청이 완료될 때 까지 막혀있을 것이지만, 하위 요청의 결과를 기다리고 있기 때문에 미결중인 요청 또한 완료되지 않을 것이다. Solr가 충분한 갯수의 스레드를 다룰 수 있도록 설정한다면 사용자는 이와 같은 데드락을 피할 수 있을 것이다.
지역 샤드 선호
Solr은 사용자가, 가능하다면 분산 질의가 샤드의 로컬 레플리카를 선호토록 지시하는 preferLocalShards라는 선택적 boolean 파라미터를 전달 할 수 있게 한다. 만약 질의에 preferLocalShards=true 가 포함된다면, 질의 제어기는 질의 수행을 위해 전 클러스터에 걸쳐 무작위로 선택된 레플리카가 아닌 로컬 레플리카를 찾아 사용토록 설정할 수 있다. 이 설정은 질의가 document 당 매우 많은 필드를 요구하거나 매우 큰 필드를 요구할 때 유용하다. 지역적인 처리가 가능하다면 매우 많은 양의 데이터가 네트워크를 통해 이동하는 것을 피할 수 있기 때문이다. 덧붙여, 이 기능은 성능상 문제가 있는 레플리카로부터 유래되는 문제상황을 최소화 하는데 유용할 수 있다. 건강하지 않은 레플리카가 다른 건강한 레플리카에 영향을 줄 가능성을 줄이기 때문이다.
마지막으로, 컬렉션의 샤드 갯수가 많아지는 만큼 이 기능의 가치가 줄어들 수 있는데, 질의 처리기가 대부분의 샤드를 위해 질의를 non-local 레플리카에서 지시할 것이기 때문이다. 다른말로, 이 기능은 작은 수의 샤드와 많은 수의 레플리카를 가진 컬렉션을 향해 처리를 지시하도록 최적화된 질의에 매우 유용하다. 또한, 이 옵션은 Solr의 CloudSolrClient가 동작하는 것처럼, 사용자가 컬렉션의 레플리카를 호스트 하고 있는 모든 노드를 대상으로 요청을 로드 밸런싱 할 때에만 사용되어야 한다. 만약 로드 밸런싱이 되어있지 않다면, 이 기능은 클러스터 전반에 균등하게 분산되지 않아 클러스터의 핫스팟을 야기할 수 있다.