SolrCloud详解

SolrCloud的基本概念

1、Cluster集群:Cluster是一组Solr节点,逻辑上作为一个单元进行管理,整个集群必须使用同一套schema和SolrConfig。

2、Node节点:一个运行Solr的JVM实例。

3、Collection:在SolrCloud集群中逻辑意义上的完整的索引,常常被划分为一个或多个Shard,这些Shard使用相同的Config Set,如果Shard数超过一个,那么索引方案就是分布式索引。SolrCloud允许客户端用户通过Collection名称引用它,这样用户不需要关心分布式检索时需要使用的和Shard相关参数。

4、Core: 也就是Solr Core,一个Solr中包含一个或者多个Solr Core,每个Solr Core可以独立提供索引和查询功能,Solr Core的提出是为了增加管理灵活性和共用资源。SolrCloud中使用的配置是在Zookeeper中的,而传统的Solr Core的配置文件是在磁盘上的配置目录中。

5、Config Set: Solr Core提供服务必须的一组配置文件,每个Config Set有一个名字。最小需要包括solrconfig.xml和schema.xml,除此之外,依据这两个文件的配置内容,可能还需要包含其它文件,如中文索引需要的词库文件。Config Set存储在Zookeeper中,可以重新上传或者使用upconfig命令进行更新,可使用Solr的启动参数bootstrap_confdir进行初始化或更新。

6、Shard分片: Collection的逻辑分片。每个Shard被分成一个或者多个replicas,通过选举确定哪个是Leader。

7、Replica: Shard的一个拷贝。每个Replica存在于Solr的一个Core中。换句话说一个SolrCore对应着一个Replica,如一个命名为“test”的collection以numShards=1创建,并且指定replicationFactor为2,这会产生2个replicas,也就是对应会有2个Core,分别存储在不同的机器或者Solr实例上,其中一个会被命名为test_shard1_replica1,另一个命名为test_shard1_replica2,它们中的一个会被选举为Leader。

8、 Leader: 赢得选举的Shard replicas,每个Shard有多个Replicas,这几个Replicas需要选举来确定一个Leader。选举可以发生在任何时间,但是通常他们仅在某个Solr实例发生故障时才会触发。当进行索引操作时,SolrCloud会将索引操作请求传到此Shard对应的leader,leader再分发它们到全部Shard的replicas。

9、Zookeeper: Zookeeper提供分布式锁功能,这对SolrCloud是必须的,主要负责处理Leader的选举。Solr可以以内嵌的Zookeeper运行,也可以使用独立的Zookeeper,并且Solr官方建议最好有3个以上的主机。

SolrCloud中完整索引(Collection)的逻辑

在SolrCloud模式下Collection是访问Cluster的入口,这个入口有什么用呢?比如说集群里面有好多台机器,那么访问这个集群通过哪个地址呢,必须有一个接口地址,Collection就是这个接口地址。可见Collection是一个逻辑存在的东西,因此是可以跨Node的,在任意节点上都可以访问Collection。Shard其实也是逻辑存在的,因此Shard也是可以跨Node的; 1个Shard下面可以包含0个或者多个Replication,但1个Shard下面能且只能包含一个Leader如果Shard下面的Leader挂掉了,会从Replication里面再选举一个Leader。

SolrCloud索引操作的基本架构图

  图中所示为拥有4个Solr节点的集群,索引分布在两个Shard里面,每个Shard包含两个Solr节点,一个是Leader节点,一个是Replica节点,此外集群中有一个负责维护集群状态信息的Overseer节点,它是一个总控制器。集群的所有状态信息都放在Zookeeper集群中统一维护。从图中还可以看到,任何一个节点都可以接收索引创建或者更新的请求,然后再将这个请求转发到索引文档所应该属于的那个Shard的Leader节点,Leader节点更新结束完成后,最后将版本号和文档转发给同属于一个Shard的replicas节点。

SolrCloud的工作模式

  SolrCloud中包含有多个Solr Instance,而每个Solr Instance中包含有多个Solr Core,Solr Core对应着一个可访问的Solr索引资源,每个Solr Core对应着一个Replica或者Leader,这样,当Solr Client通过Collection访问Solr集群的时候,便可通过Shard分片找到对应的Replica即SolrCore,从而就可以访问索引文档了。

  在SolrCloud模式下,同一个集群里所有Core的配置是统一的,Core有leader和replication两种角色,每个Core一定属于一个Shard,Core在Shard中扮演Leader还是replication由Solr内部Zookeeper自动协调。

访问SolrCloud的过程:Solr Client向Zookeeper咨询Collection的地址,Zookeeper返回存活的节点地址供访问,插入数据的时候由SolrCloud内部协调数据分发(内部使用一致性哈希)。

SolrCloud创建索引和更新索引

索引存储细节:

当Solr客户端发送add/update请求给CloudSolrServer,CloudSolrServer会连接至Zookeeper获取当前SolrCloud的集群状态,并会在/clusterstate.json 和/live_nodes中注册watcher,便于监视Zookeeper和SolrCloud,这样做的好处有以下两点:

1、CloudSolrServer获取到SolrCloud的状态后,它可直接将document发往SolrCloud的leader,从而降低网络转发消耗。

2、注册watcher有利于建索引时候的负载均衡,比如如果有个节点leader下线了,那么CloudSolrServer会立马得知,那它就会停止往已下线的leader发送document。

此外,CloudSolrServer 在发送document时候需要知道发往哪个shard?对于建好的SolrCloud集群,每一个shard都会有一个Hash区间,当Document进行update的时候,SolrCloud就会计算这个Document的Hash值,然后根据该值和shard的hash区间来判断这个document应该发往哪个shard,Solr使用documentroute组件来进行document的分发。目前Solr有两个DocRouter类的子类CompositeIdRouter(Solr默认采用的)类和ImplicitDocRouter类,当然我们也可以通过继承DocRouter来定制化我们的document route组件。

举例来说当Solr Shard建立时候,Solr会给每一个shard分配32bit的hash值的区间,比如SolrCloud有两个shard分别为A,B,那么A的hash值区间就为80000000-ffffffff,B的hash值区间为0-7fffffff。默认的CompositeIdRouter hash策略会根据document ID计算出唯一的Hash值,并判断该值在哪个shard的hash区间内。

SolrCloud对于Hash值的获取提出了以下两个要求:

1、hash计算速度必须快,因为hash计算是分布式建索引的第一步。

2、 hash值必须能均匀的分布于每一个shard,如果有一个shard的document数量大于另一个shard,那么在查询的时候前一个shard所花的时间就会大于后一个,SolrCloud的查询是先分后汇总的过程,也就是说最后每一个shard查询完毕才算完毕,所以SolrCloud的查询速度是由最慢的shard的查询速度决定的。

基于以上两点,SolrCloud采用了MurmurHash 算法以提高hash计算速度和hash值的均匀分布。

更新索引:

1、 Leader接受到update请求后,先将update信息存放到本地的update log,同时Leader还会给document分配新的version,对于已存在的document,如果新的版本高就会抛弃旧版本,最后发送至replica。

2、一旦document经过验证以及加入version后,就会并行的被转发至所有上线的replica。SolrCloud并不会关注那些已经下线的replica,因为当他们上线时候会有recovery进程对他们进行恢复。如果转发的replica处于recovering状态,那么这个replica就会把update放入updatetransaction 日志。

3、当leader接受到所有的replica的反馈成功后,它才会反馈客户端成功。只要shard中有一个replica是active的,Solr就会继续接受update请求。这一策略其实是牺牲了一致性换取了写入的有效性。这其中有一个重要参数:leaderVoteWait参数,它表示当只有一个replica时候,这个replica会进入recovering状态并持续一段时间等待leader的重新上线。如果在这段时间内leader没有上线,那么他就会转成leader,其中可能会有一些document丢失。当然可以使用majority quorum来避免这个情况,这跟Zookeeper的leader选举策略一样,比如当多数的replica下线了,那么客户端的write就会失败。

4、索引的commit有两种,一种是softcommit,即在内存中生成segment,document是可见的(可查询到)但是没写入磁盘,断电后数据会丢失。另一种是hardcommit,直接将数据写入磁盘且数据可见。

SolrCloud索引的检索

1、用户的一个查询,可以发送到含有该Collection的任意Solr的Server,Solr内部处理的逻辑会转到一个Replica。

2、此Replica会基于查询索引的方式,启动分布式查询,基于索引的Shard的个数,把查询转为多个子查询,并把每个子查询定位到对应Shard的任意一个Replica。

3、每个子查询返回查询结果。

4、最初的Replica合并子查询,并把最终结果返回给用户。

SolrShard Splitting的具体过程

一般情况下,增加Shard和Replica的数量能提升SolrCloud的查询性能和容灾能力,但是我们仍然得根据实际的document的数量,document的大小,以及建索引的并发,查询复杂度,以及索引的增长率来统筹考虑Shard和Replica的数量。Solr依赖Zookeeper实现集群的管理,在Zookeeper中有一个Znode 是/clusterstate.json ,它存储了当前时刻下整个集群的状态。同时在一个集群中有且只会存在一个overseer,如果当前的overseer fail了那么SolrCloud就会选出新的一个overseer,就跟shard leader选取类似。

Shard分割的具体过程(old shard split为newShard1和newShard2)可以描述为:

a、在一个Shard的文档到达阈值,或者接收到用户的API命令,Solr将启动Shard的分裂过程。

b、此时,原有的Shard仍然会提供服务,Solr将会提取原有Shard并按路由规则,转到新的Shard做索引。同时,新加入的文档:

1.2.用户可以把文档提交给任意一个Replica,并转交给Leader。

3.Leader把文档路由给原有Shard的每个Replica,各自做索引。

III.V. 同时,会把文档路由给新的Shard的Leader

IV.VI.新Shard的Leader会路由文档到自己的Replica,各自做索引,在原有文档重新索引完成,系统会把分发文档路由切到对应的新的Leader上,原有Shard关闭。Shard只是一个逻辑概念,所以Shard的Splitting只是将原有Shard的Replica均匀的分不到更多的Shard的更多的Solr节点上去。

搭建

搭建solr集群所需要的服务器为:192.168.121.12和192.168.121.14。

1、搭建zookeeper集群步骤:需要三台zookeeper、分别是zk1、zk2、zk3,对应的端口分别为192.168.121.12:2181:192.168.121.12:2182:192.168.121.14:2181。

2、搭建solrcloud集群步骤:

  1)搭建四个单机版的solr服务对应的jetty,端口分别是192.168.121.12:8080、192.168.121.12:8081、192.168.121.14:8080、192.168.121.14:8081

  2)设置jetty的启动参数,在每个jetty目录下的bin/jetty.sh,添加以下-DzkHost参数内容内容:

  

  3)将solr配置文件上传到zookeeper中,进行统一管理,进入到/root/soft/solr-4.10.3/example/scripts/cloud-scripts目录中执行zkcli.sh命令./zkcli.sh   -zkhost       192.168.121.12:2181,192.168.121.12:2182,192.168.121.14:2181   -cmd   upconfig   -confdir   /usr/local/solrcloud/solrhome/collection/conf   -confname   myconf(每个ip地址之间用逗号分隔)

  collection为自己创建的solr集合

  myconf为上传到zookeeper的集合配置文件的名称

3、启动所有的solr服务、启动所有的zookeeper服务。

4、访问部署的solr集群中任意的端口服务。

  

5、创建索引集合指令:

http://192.168.121.12:8080/solr/admin/collections?action=CREATE&name=AddressBean&numShards=2&replicationFactor=2&collection.configName=AddressBean

  action:表示执行的动作,是创建还是删除。

  name:创建的集合名称。

  numShards:分片的个数。

  replicationFactor:每个分片有多少个副本集。

  collection.configName:引用的集合配置文件的名称。

6、删除索引集合指令:

http://192.168.121.12:8080/solr/admin/collections?action=DELETE&name=haixin

实例

package solrtest;import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.SolrQuery.ORDER;
import org.apache.solr.client.solrj.impl.CloudSolrClient;
import org.apache.solr.client.solrj.impl.CloudSolrClient.Builder;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.response.UpdateResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrInputDocument;
import org.aspectj.lang.annotation.Before;import net.sf.json.JSONArray;
import net.sf.json.JSONObject;public class SolrCloudService {// zookeeper地址  private static final String zkHostString ="192.168.121.12:2181,192.168.121.12:2182,192.168.121.14:2181";  // collection默认名称,比如我的solr服务器上的collection是collection2_shard1_replica1,就是去掉“_shard1_replica1”的名称  private static String defaultCollection ="AddressBean"; // cloudSolrServer实际  private static CloudSolrClient cloudSolrClient;  final static int  zkClientTimeout = 30000;    final static int zkConnectTimeout = 30000;    // 测试方法之前构造 CloudSolrServer  public static void main(String[] args) throws IOException, Exception{cloudSolrClient = new CloudSolrClient.Builder().withZkHost(zkHostString).build();cloudSolrClient.setDefaultCollection(defaultCollection); cloudSolrClient.setZkClientTimeout(zkClientTimeout);    cloudSolrClient.setZkConnectTimeout(zkConnectTimeout);cloudSolrClient.connect(); JSONObject jsonObject = new JSONObject();jsonObject.put("id", UUID.randomUUID().toString());jsonObject.put("userName", "凌晨零点");//jsonObject.put("realName", "尤忠民");//jsonObject.put("department", "青岛大学网络中心技术管理二部");jsonObject.put("createTime", new Date());//jsonObject.put("jobTitle", "项目经理");jsonObject.put("gender", "女");jsonObject.put("address", "青岛市崂山区青岛大学五号楼五楼");SolrCloudService.addIndexs(jsonObject);}  // 向solrCloud上创建单个索引  public static void addIndexs(JSONObject jsonObject) throws SolrServerException,  IOException {  SolrInputDocument doc = new SolrInputDocument();doc.addField("id", jsonObject.getString("id"));doc.addField("userName", jsonObject.getString("userName"));//doc.addField("realName", jsonObject.getString("realName"));//doc.addField("department", jsonObject.getString("department"));doc.addField("createTime", new Date());//doc.addField("jobTitle", jsonObject.getString("jobTitle")); doc.addField("gender", jsonObject.getString("gender"));doc.addField("address", jsonObject.getString("address")); System.out.println(jsonObject.toString());cloudSolrClient.add(doc);  cloudSolrClient.commit();  }  //向solrCloud上创建多个个索引  public void addIndexs(JSONArray jsonArray) throws Exception, IOException {List<SolrInputDocument> docs = new ArrayList<SolrInputDocument>();for(int i=0;i<jsonArray.size();i++){JSONObject jsonObject = jsonArray.getJSONObject(i);SolrInputDocument doc = new SolrInputDocument();doc.addField("name", jsonObject.getString("name"));doc.addField("id", UUID.randomUUID().toString());doc.addField("phoneType", jsonObject.getString("phoneType"));doc.addField("price", jsonObject.getString("price"));docs.add(doc);}cloudSolrClient.add(docs);// 提交cloudSolrClient.commit();}//向solrCloud上创建多个对象的索引  public void addIndexs(List<Phone> phoneList) throws Exception, IOException {cloudSolrClient.addBeans(phoneList);cloudSolrClient.commit();}//向solrCloud上创建一个对象的索引  public void addIndex(Phone phone) throws Exception, IOException {// 创建一个文档//添加的Field 必须是在schema.xml 中配置了,不然就报错// 创建文档2cloudSolrClient.addBean(phone);// 提交cloudSolrClient.commit();}//通过指定的id删除solrCloud上的索引public void delete(List<String> ids) throws Exception {cloudSolrClient.deleteById(ids);cloudSolrClient.commit(); //提交}//通过指定的域值删除数据public void deleteByField(String fieldName,String fieldValue) throws Exception {String query = fieldName + ":" + fieldValue;cloudSolrClient.deleteByQuery(query);cloudSolrClient.commit(); //提交}//通过指定的域值更新数据public void updateByField(String id,String fieldName,String fieldValue) throws Exception {SolrInputDocument doc = new SolrInputDocument();doc.addField("id", id);doc.addField(fieldName, fieldValue);cloudSolrClient.add(doc);cloudSolrClient.commit(); //提交}//查询索引public void query(String fieldName,String fieldValue, Integer page, Integer rows,String fieldSort) throws Exception {//添加查询SolrQuery solrQuery = new SolrQuery();//查询//代表Solr控制界面Query中的p字段 ,查询字符串,这个是必须的。//如果查询所有*:* ,根据指定字段查询(Name:张三 AND Address:北京)//solrQuery.set(fieldName, fieldValue);solrQuery.setQuery(fieldName+":"+fieldValue);//代表fq字段 ,(filter query)过虑查询,作用:在q查询符合结果中同时是fq查询符合的,//例如:q=Name:张三&fq=CreateDate:[20081001 TO 20091031],找关键字mm,并且CreateDate是20081001
//          solrQuery.addFilterQuery("itemName:yby");//指定返回那些字段内容,用逗号或空格分隔多个。solrQuery.addField("id,phoneType,price");//按照指定的字段排序solrQuery.setSort(fieldSort, ORDER.asc);// 设置分页 start=0就是从0开始,,rows=5当前返回5条记录,第二页就是变化start这个值为5就可以了。solrQuery.setStart((Math.max(page, 1) - 1) * rows);solrQuery.setRows(rows);// 设置高亮solrQuery.setHighlight(true); // 开启高亮组件solrQuery.addHighlightField("id");// 高亮字段solrQuery.setHighlightSimplePre("<em>");// 标记,高亮关键字前缀solrQuery.setHighlightSimplePost("</em>");// 后缀//获取查询结果QueryResponse response = cloudSolrClient.query(solrQuery);//获取查询到的文档SolrDocumentList docs = response.getResults();//查询到的条数long cnt = docs.getNumFound();System.out.println("查询到的条数\t"+cnt);//获取查询结果for(SolrDocument doc :docs) {String id = doc.get("id").toString();System.out.printf("%s\r\n",id);String phoneType = doc.get("phoneType").toString();System.out.printf("%s\r\n",phoneType);}}
}

  

转载于:https://www.cnblogs.com/youzhongmin/p/8716850.html

solr搭建分布式搜索引擎相关推荐

  1. 手把手,教你用MaxCompute+OpenSearch搭建分布式搜索引擎

    摘要: 最近,经常有客户咨询如何低成本搭建高性能的海量数据搜索引擎,比如实现公众号检索.影讯检索等等.由于客户的数据在阿里云上,所以希望找到云上解决方案.笔者开始调研一些云上产品,很多人向我推荐了Op ...

  2. ElasticSearch分布式搜索引擎从入门到实战应用(入门篇-基本命令操作)

    ElasticSearch分布式搜索引擎从入门到实战应用(入门篇) 1.入门须知 2.ElasticSearch概述 2.1.ES简介 2.2.应用场景 3.ES和Solr的对比 3.1.ES作用 3 ...

  3. 爬梯:ElasticSearch分布式搜索引擎

    学习资料:狂神说 ElactisSearch 7.6.2 ElasticSearch 分布式搜索引擎 1. 概述 1.1 ELK ELK是ElasticSearch.Logstash.Kibana三大 ...

  4. 技术分享:如何用Solr搭建大数据查询平台

    技术分享:如何用Solr搭建大数据查询平台 0×00 开头照例扯淡 自从各种脱裤门事件开始层出不穷,在下就学乖了,各个地方的密码全都改成不一样的,重要帐号的密码定期更换,生怕被人社出祖宗十八代的我,甚 ...

  5. 1号店11.11:分布式搜索引擎的架构实践

    http://www.uml.org.cn/zjjs/201512011.asp "11.11"是一年一度的电商盛宴,为了准备这个一年内最大规模的促销,1号店各条战线都在紧张有序地 ...

  6. 打造自己的分布式搜索引擎底层架构(非Lucene)

    打造自己的分布式搜索引擎底层架构(非Lucene) 大家知道,搜索引擎技术不仅仅是类似百度首页的应用,还可以衍生出数据分析工具,商务智能工具等许多有卖点的应用,甚至是社会化关系通道的发现. 甚至这些非 ...

  7. 微服务03 分布式搜索引擎 elasticsearch ELK kibana RestAPI 索引库 DSL查询 RestClient 黑马旅游

    分布式搜索引擎01 -- elasticsearch基础 0.学习目标 1.初识elasticsearch 1.1.了解ES 1.1.1.elasticsearch的作用 elasticsearch是 ...

  8. solr搭建和测试 windows版

    solr搭建和测试 windows版 准备:solr启动包(windows) 流程: 简单测试: 整合springboot: solr命令: 准备:solr启动包(windows) 下载链接: htt ...

  9. 10、面试官对于分布式搜索引擎的4个连环炮

    业内目前来说事实上的一个标准,就是分布式搜索引擎一般大家都用elasticsearch,es,solr,但是确实,这两年大家一般都用更加易用的es. lucene 如果你确实真的不连lucene都不知 ...

最新文章

  1. Pandas把dataframe中的整数数值(integer)转化为时间(日期、时间)信息实战
  2. Bugku——Web——矛盾
  3. CentOS 7配置IP的几种方法。
  4. iOS技术周报-第28期
  5. ArcGIS Engine 10开发环境的一些常见问题(转载)
  6. SQL 数据分析常用语句
  7. Nginx源码分析-启动初始化过程(一)
  8. 当今将Windows应用程序迁移到Windows on Arm的实践
  9. 摩根大通分析:随着灰度资金流动缓慢,比特币能否重回4万美元仍存疑问
  10. c++ 使用gdiplus
  11. php steam 第三方登录,Steam第三方登录
  12. 东北大学《铸造工艺学》结课报告
  13. 快乐数Python解法
  14. 脑科学研究中基于图论的复杂脑网络分析方法
  15. 贝叶斯公式的对数似然函数_最大似然法与似然函数
  16. win10系统如何关闭服务器,win10命令关闭服务器该怎么操作关闭?
  17. 圆上三点求圆心和半径
  18. oracle数据库定期备份
  19. SpringBoot logback-spring配置,再也不用为日志烦恼了。再推荐IDEA一个日志插件Grep Console美美哒展示控制台输出
  20. 张宇1000题高等数学 第三章 一元函数微分学的概念

热门文章

  1. 无线通信领域:技术整合,创造未来
  2. 基于ArcGIS JS API 的点击查询功能
  3. Spring Boot 发邮件和附件,超实用!
  4. 云计算,拼的就是运维
  5. 你真的理解零拷贝吗?
  6. 研发应该懂的binlog知识(下)
  7. 为什么你的问题总是没人回答
  8. 收藏 | 阿里云Redis开发规范
  9. 操作系统:进程调度算法
  10. 37.拷贝控制和资源管理