Nodes, Cores, Cluster and Leaders
Nodes and Cores
在SolrCloud中,一个node就是一个JVM运行Solr的实例,通常称之为server。每个Solrcore都可以被当作一个node。任何一个node都可以包含一个Solr的实例和多样化的数据在其中。
Solr core中存储了基于一篇文章中发现的文本内容和字段的索引。一个单独的Solr实例可以包含多个core,这些core基于本地的标准彼此间分离。这些core针对不同的用户(在美国的用户或者在加拿大的用户)提供不同的搜索方式,提供私密关注点(某些用户不能访问某些文档),提供内容毫不相关或者很难整合到一起的文档(鞋子的数据和DVD的数据)。
当你在SolrCloud模式下启动一个新的core时,这个core会自动注册到ZooKeeper当中。这一过程包括创建一个临时node(如果Solr实例停止这个临时node就会消失),注册core和如何连接core(例如Solr的URL,core名称等)的相关信息。客户端和集群中的nodes可以通过这些信息来决定为了执行请求需要访问哪些信息。
新的Solr cores可以通过CoreAdmin来创建和关联collection。新增的云相关参数将在Parameter Reference页面介绍。使用CREATE操作的对象:
l collection:core所属于的collection。默认是core的名称
l shard:shardid代表core
l collection.<param>=<value>:如果一个新的collection被创建就会设置一组<param>=<value>属性。例如collection.configName=<configname>用来指出新的collection的config。
例如:
curl ‘http://localhost:8983/solr/admin/cores?action=CREATE&name=mycore&collection=my_collection&shard=shard2‘
Clusters
一个cluster是被ZooKeeper管理的一组Solr nodes单独集合。当你拥有一个cluster,在任何时候你都可以向集群发请求,如果这个请求是公认的,你可以确保这个请求会被作为一个单元来管理和持久化,也就是你不会丢失数据。做完操作马上就可以看到状态的更新,集群可以被扩张或者收缩。
Creating a Cluster
只要在Zookeeper上注册的Solr实例超过一个,集群就被创建了。
Resing a Cluster
集群包含一个可设置shard数量的参数。当你启动solr的时候,通过传递系统参数numShards来设置新集群的shard数量。无论任何一个Solr node都必须在第一次启动的时候传递numShards参数,用来自动配置shard应该属于哪个实例的一部分。一旦你启动Solr node的数量超过numShards,nodes将会为每个shard创建replicas,均匀的分布在node上只要他们属于同一个collection。
想要在你的collection中添加更多的core,仅仅启动新的core。你可以在任何时候这样做,新的core会在激活之前与当前在shard中的replicas同步。
如果你采用手动的给一个core赋予一个shard id,你同样可以绕过numShards。
Shard的数量决定了你的索引数据有多么碎片化,所以在你初始化集群的设置之后你不能更改索引的shard数量。
不过,你有机会将你的索引分离到多个shard中去,即使你只有一台服务器。你可以在将来扩张到多台服务器上面。完成这个操作只要遵循以下几点:
1. 在一台物理服务器上用多个core来设置你的collection。每个shard都是那个shard下的leader。
2. 当你准备好了,你可以通过启动每台新服务器上所属于那些shard新的replica来移动那些shard到新的服务器上。
3. 删除原来服务器上的shard。ZooKeeper将会把replica升级为那个shard的leader。
Leaders and Replicas
leader的概念跟solr replication功能中的master很相近。Leader负责确保replicas跟存储在leader中的信息保持一致。
然后使用SolrCloud,你就不仅仅拥有一个master和一到多个slave,反而你很可能进行分布式查询和多服务器之间索引的通讯。如果你已经设置Solr的numShards=2,例如你的所有分别在两个shard上。这种情况下,两个shard都将被视为leader。如果你在初始化两个之后启动了更多的node,那这些node就自动的被当做这些leader的replica。
Replica归属于shard是为了他们在第一次加入到集群时保持启动状态。这是由round-robin方式完成的,除非手动地将带有shardId参数的新node归属给一个shard在启动期间。这个参数通常作为系统的属性,-DshardId=1,新的node需要附上shard的ID值。
在后期重启的时候,每个node加载到node第一次启动时分配给他的shard中去(不管这个分配是手动的还是自动的)。如果一开始被分配为leader的node不可获取了,一个replica可以变成leader。
思考:
l Node A伴随引导参数启动,指向一个独立的ZooKeeper,numShards设置为2.
l Node B启动并指向独立的ZooKeeper
Node A和Node B都是shard,在启动Node A时定义满足2个shard插槽。如果我们查看Solr控制台,我们会发现两个node都包含leader(用一个实边的白圆表示)。
l Node C启动并指向独立的ZooKeeper
Node C将会自动成为Node A的一个replica,因为我们没有指定他属于任何一个其他的shard,而且他也不能够成为一个新的shard因为我们只定义了两个shard而且这两个都被占用了。
l Node D启动并指向独立的ZooKeeper
Node D将会自动成为Node B的一个replica,道理与Node C相同。
进行重启,假设Node C在Node A之前重启会发生什么?Node C会成为leader,Node A成为了NodeC的replica。
Shards and IndexingData in SolrCloud
当你的数据存储在一个node上数量过大,你可以分离这些数据通过创建一或多个shard来存储在section中。每一个都是逻辑索引的一部分,或者是core,包含了所有node中section的索引。
Shard是一种以一定数量的server或是node来分离core的方法。例如,假设你有一个shard包含各种状态的数据,或是不同种类的,将要被独立的检索,但通常是结合的。
在SolrCloud之前,Solr提供分布式检索,允许从多个shard执行一个查询,所以执行的这个查询与solr全部索引对立而且查询结果中不会丢失documents。所以通过shard分离core并不是Solrcloud独有的概念。然而,分布式方法伴随的一些问题SolrCloud很有必要进行改进:
1. 将core分离到shard中是手动的
2. 不支持分布式索引,那就意味着你需要明确的发送document到特殊的shard中;Sole不能指出document发送到了自己哪一个shard。
3. 没有负载均衡或者容错,所以如果你想大规模的查询,你需要指出请求发送到哪而且如果一个shard崩溃了就结束了。
SolrCloud解决了这些问题。支持分布式的索引和分布式自动查询,ZooKeeper提供容错和负载均衡。另外,每个shard都可以有多个replica用来增加健壮性。
在SolrCloud中没有master和slaves。代替他们的是leaders和replicas。Leaders是自动选举的,以first-come-first-served为基本原则,基于ZooKeeper处理描述在http://zookeeper.apache.org/doc/trunk/recipes.html#sc_leaderElection
如果leader停掉了,他的一个replica就会被自动选举为新的leader。每个node都启动后,他会被分配到拥有最少replica的shard中。如果情况都一样,那就会被分配到shard ID最小的那个shard中。
当一个document发送到服务器用于索引时,系统会先判断这台服务器是一个replica还是leader。
l 如果服务器是replica,这个document会转发给leader进行处理。
l 如果服务器是leader,SolrCloud决定document应该访问哪个shard,然后将document转发给那个shard的leader,在这个shard生成这个document的索引,然后标记该索引并转发给其他的replica。
Document Routing
当创建你的collection时,Solr具备通过指定router.name参数的collection来实现router的能力。如果你使用“compositeId”router,你可以发送带document ID前缀的document,这将用于计算hash,Solr以此决定document发往哪个shard生成索引。这个前缀可以是任意的(不要是shard的名称),但必须始终如一这样solr性能才稳定。例如,你想要给客户同步数据,你可以使用客户名或ID作为前缀。例如,如果你的客户是IBM,document的ID是“12345”,你最好在document的id值中加入前缀:“IBM!12345”。“!”是一个边界,区分前缀用来决定哪个shard来管理这个document。
那么在查询的时候,在你的查询语句中通过_route_参数(也就是,q=solr&_route_=IBM!)加入前缀来管理查询到指定的shard。在某些情况下,这样做会增强查询的性能,因为从所有shard查询时网络的潜在因素。
提示:_route_参数代替shard.keys,shard.keys在Solr以后发布的版本中弃用。
这个compositeId支持2级前缀。例如:第一个是地区前缀,然后是客户前缀:“USA!IBM!12345”
另一种使用场景是如果IBM这个客户有大量的文档,你想要分布他们到多个shard中去。这种用法的语法是:“shard_key/num!document_id”,这个num就是在复合hash使用shrad的key的bit的数量。
因此“IBM/3!12345”将会在shard key中占用3bit,在唯一的doc id中占29bit,在collection中传播租户超过shard的1/8。例如num值为2就会跨1/4的shard传播该document。在查询的时候,直接到指定的shard中用_route_参数查询,将bit的num一起包含在前缀中(也就是q=solr&_route_=IBM/3!)。
如果你不想影响document如何存储,就不用在document ID中指定前缀。
如果你创建了一个collection而且定义在创建的时候定义了“implicit”route,你可以添加定义一个router.field参数,通过各个document的这个field来确定document属于哪个shard。如果在document中丢失这个field指定,document将会被拒绝。你同样可以使用_route_参数来命名一个指定的shard。
Shard Splitting
当你在SolrCloud中创建一个collection,你要决定shard的初始化个数。但是很难提前知道你所需要的shard个数,特别是当组织需求发生改变,这成本会很高当事后发现你的选择是错误的,涉及创建新的core而且重新生成所有数据的索引。
Collection API提供拆分shard的能力。目前允许拆分一个shard为两片。现有的shard保持不变,所以拆分操作实际上将两个切片的数据作为两个新的shard。当你准备好了易后你可以删除老的shard。
更多关于拆分shard的内容在Collection API这一章节。https://cwiki.apache.org/confluence/display/solr/Collections+API
Ignoring Commits fromClient Application in SolrCloud
多数情况下我们在SolrCloud模式下运行,客户端应用不能直接发送提交索引数据的请求。当然,你可以通过配置openSearcher=false和soft-commits自动提交使最近更新在搜索请求中显示。这可以确保schedule集群中的提交定期发生。确保客户端应用不会发送直接提交的方案,你可以更新所有客户端应用的solr索引数据到SolrCloud中。然而这种方法并不是一直都可行,因此solr提供IgnoreCommitOptimizeUpdateProcessorFactory,可以允许你不用重构客户端应用的代码来忽略来自客户端应用的直接提交或者优化的请求。想要激活这个请求处理器,你需要在solrconfig.xml中添加一下配置:
<updateRequestProcessorChainname="ignore-commit-from-client" default="true">
<processorclass="solr.IgnoreCommitOptimizeUpdateProcessorFactory">
<intname="statusCode">200</int>
</processor>
<processorclass="solr.LogUpdateProcessorFactory" />
<processorclass="solr.DistributedUpdateProcessorFactory" />
<processorclass="solr.RunUpdateProcessorFactory" />
</updateRequestProcessorChain>
在上面的例子中,处理器会返回给客户端200但是会忽略commit/optimize请求。注意你的SolrCloud同样需要接入隐式的处理器,因为这个定制的chain会覆盖默认的chain。
在下面的这个例子当中,处理器会返回一个403 code异常的定制的错误信息:
<updateRequestProcessorChainname="ignore-commit-from-client" default="true">
<processorclass="solr.IgnoreCommitOptimizeUpdateProcessorFactory">
<intname="statusCode">403</int>
<str name="responseMessage">Thoushall not issue a commit!</str>
</processor>
<processor class="solr.LogUpdateProcessorFactory"/>
<processorclass="solr.DistributedUpdateProcessorFactory" />
<processorclass="solr.RunUpdateProcessorFactory" />
</updateRequestProcessorChain>
最后,你可以通过以下配置来忽略优化使提交通过:
<updateRequestProcessorChain name="ignore-optimize-only-from-client-403">
<processorclass="solr.IgnoreCommitOptimizeUpdateProcessorFactory">
<str name="responseMessage">Thoushall not issue an optimize, but commits are OK!</str>
<boolname="ignoreOptimizeOnly">true</bool>
</processor>
<processorclass="solr.RunUpdateProcessorFactory" />
</updateRequestProcessorChain>
Distributed Requests
Limiting Which Shardsare Queried
SolrCloud的一大优点就是可以在各个包含或不包含你要找的数据的shrad间进行分布式查询。你可以选择查询全部的数据或者只是部分数据。
从所有shard查询collection看起来很熟悉,好像SolrCloud甚至没有发挥作用:
http://localhost:8983/solr/gettingstarted/select?q=*:*
你只想从一个shard查询,你可以指定通过那个shard的逻辑ID来指定shard:
http://localhost:8983/solr/gettingstarted/select?q=*:*&shards=shard1
如果你想查询一组shard ids,你可以同时指定他们:
http://localhost:8983/solr/gettingstarted/select?q=*:*&shards=shard1,shard2
上面的两个例子,shard Ids会随机选取相应shard下的replica。
以下两者任选其一,你可以明确的指定shard中你希望使用的replica:
http://localhost:8983/solr/gettingstarted/select?q=*:*&shards=localhost:7574/solr/gettingstarted,localhost:8983/solr/gettingstarted
或者,你可以从一个单独的shard中指定一个replica的集合通过使用符号“|”(为了达到负载均衡的目的):
http://localhost:8983/solr/gettingstarted/select?q=*:*&shards=localhost:7574/solr/gettingstarted|localhost:7500/solr/gettingstarted
当然,你可以通过“,”来指定一个shard集合,集合中成员又可以是通过“|”来指定的多个shard。例如这个例子当中需要2个shard,第一个是从shard1从随机选取的replica,第二个是通过“|”明确划分的集合:
http://localhost:8983/solr/gettingstarted/select?q=*:*&shards=shard1,localhost:7574/solr/gettingstarted|localhost:7500/solr/gettingstarted
Configuring theShardHandlerFactory
在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>
Configuring statsCache(Distributed IDF)
为了计算关联度,需要文档和长期统计。Solr提供四种模式来进行文档的统计计算:
LocalStatsCache: 这只使用本地术语和文档统计来计算相关性。为了从各个shard统一分布的术语,这种配置的效果很好。
如果没有配置<statsCache>,默认为这项。
ExactStatsCache: 此实现使用全局值(跨collection)为文档频率。
ExactSharedStatsCache: 功能与ExactStatsCache很像,但在同一条件下,对于后续的请求来说,全局数据是可重用的。
LRUStatsCache:通过LRU缓存全局统计,在请求之间共享。
通过在solrconfig.xml中配置<statsCache>来实现。例如下面这行Solr使用ExactStatsCache实现:
<statsCache class="org.apache.solr.search.stats.ExactStatsCache"/>
Avoiding DistributedDeadlock
每个碎片是顶级的查询请求进行子要求其他所有的碎片。应注意确保服务的HTTP请求的线程的最大数量是大于从顶级客户和其他碎片的请求的数量。如果不是这种情况,则配置可能会导致分布式死锁。
例如,死锁可能在两个碎片的情况下发生的,每一个与只是一个单一的线程服务的HTTP请求。两个线程可以同时接收一个顶层请求,并将请求分为彼此。因为没有更多的剩余的线程来服务请求,传入请求将被阻塞,直到其他的等待请求被完成,但他们不会完成,因为他们等待的子请求。通过确保Solr配置足够数量的线程来处理,就可以避免死锁,这样。
Prefer Local Shards
Solr允许你通过一个可选的布尔型参数命名preferLocalShards表明分布式查询,当一个本地shard可用时,倾向于这个shard的replica。换句话说,如果查询包括preferLocalShards= true,然后查询控制器将本地replica执行查询而不是选择随机从整个集群的查询服务。这是有用的,当一个查询请求多个字段或大的字段被返回,因为它避免了在网络上移动大量的数据时,它是在本地。此外,此功能可以是有用的,用于最大限度地减少的影响的问题的副本与退化的性能,因为它降低的可能性,退化的副本将被击中的其他健康的副本。
最后,它表明这一特征的价值减少集合中增加shard数因为查询控制器将直接查询到大部分的非本地replica的shard。换句话说,这一特征对与一个小数量的shard和许多replica集合的查询优化是非常有用的。另外,如果你要求从collection所有nodes的replica中进行负载均衡的查询只能用这个选项,如Solr的CloudSolrClient会做。如果不负载平衡,这个功能可以在集群中引入一个热点,因为查询将不均匀分布在整个集群中。
https://cwiki.apache.org/confluence/display/solr/SolrCloud
http://wiki.apache.org/solr/FrontPage
未完待续...