solr1.4 replication分发知识

2019-03-27 01:18|来源: 网路

 solr1.4中引入ReplicationHandler代替外部脚本来复制索引数据,ReplicationHandler使得复制索引数据更自动化。对于使用者来说,只要简单的配置好,就可以一劳永逸的享受solr的复制功能了。下面介绍其使用相关内容。

在solrconfig.xml中添加配置:

<requestHandler name="/replication" class="solr.ReplicationHandler" >

</requestHandler>

 

不配置它的属性是因为想用手动来触发分发索引,这样可以更有灵活性。主机跟子机同样的配置。

下面是:

solr的ReplicationHandler提供了一系列http命令(参数command),支持的可选值如下:
1)indexversion:slave从master获取最新的索引点信息。 http://master_host:port/solr/replication?command=indexversion
2)filecontent:slave从master下载指定文件的内容。
3)filelist:slave从master获取指定indexversion的索引文件列表(及需要复制的配置文件)。http://host:port/solr/replication?command=filelist&indexversion=<index-version-number>
4)backup:备份索引。如果担心索引有损坏的可能性,可以定期备份索引。http://master_host:port/solr/replication?command=backup

 

参数location:可以指定备份路径。

试了下  &location=bk    找了一下生成的备份的文件的位置,竟然在%tomcat-hom%/bin下生成。

看了代码才知道可以指定绝对路径:比如 &location=/diska/solrSearch/solr_vid_album/data/bk

备份的索引就在/diska/solrSearch/solr_vid_album/data/bk 生成一个镜像备份snapshot.20110129015833目录。


刚刚再看了一下代码,发现有一个参数可以设置备份的映像个数,参数名为:numberToKeep,每次索引机器都可以在定时备份索引

  private void doSnapShoot(SolrParams params, SolrQueryResponse rsp, SolrQueryRequest req) {
    try {
      int numberToKeep = params.getInt(NUMBER_BACKUPS_TO_KEEP, Integer.MAX_VALUE);
      IndexDeletionPolicyWrapper delPolicy = core.getDeletionPolicy();
      IndexCommit indexCommit = delPolicy.getLatestCommit();
      
      if(indexCommit == null) {
        indexCommit = req.getSearcher().getReader().getIndexCommit();
      }
      
      // small race here before the commit point is saved
      new SnapShooter(core, params.get("location")).createSnapAsync(indexCommit, numberToKeep, this);
      
    } catch (Exception e) {
      LOG.warn("Exception during creating a snapshot", e);
      rsp.add("exception", e);
    }
  }


删除旧的映像的代码也贴一下:


  private void deleteOldBackups(int numberToKeep) {
    File[] files = new File(snapDir).listFiles();
    List<OldBackupDirectory> dirs = new ArrayList<OldBackupDirectory>();
    for(File f : files) {
      OldBackupDirectory obd = new OldBackupDirectory(f);
      if(obd.dir != null) {
        dirs.add(obd);
      }
    }
    Collections.sort(dirs);
    int i=1;
    for(OldBackupDirectory dir : dirs) {
      if( i > numberToKeep-1 ) {
        SnapPuller.delTree(dir.dir);
      }
    }   
  }

主要有一个映像类

OldBackupDirectory 
保存相关的信息,实现了一个比较器,以便于排序,删除,主要以时间戳比较

  private class OldBackupDirectory implements Comparable<OldBackupDirectory>{
    File dir;
    Date timestamp;
    final Pattern dirNamePattern = Pattern.compile("^snapshot[.](.*)$");
    
    OldBackupDirectory(File dir) {
      if(dir.isDirectory()) {
        Matcher m = dirNamePattern.matcher(dir.getName());
        if(m.find()) {
          try {
            this.dir = dir;
            this.timestamp = new SimpleDateFormat(DATE_FMT).parse(m.group(1));
          } catch(Exception e) {
            this.dir = null;
            this.timestamp = null;
          }
        }
      }
    }
    public int compareTo(OldBackupDirectory that) {
      return that.timestamp.compareTo(this.timestamp);
    }
  }



 

 


5)fetchindex:手动复制数据,和slave自动复制相当。http://slave_host:port/solr/replication?command=fetchindex

可以再指定主机的URL   &masterUrl=http://172.16.200.1:8080/solr/replication

 

6)disablepoll:停止slave的复制。http://slave_host:port/solr/replication?command=disablepoll
7)enablepoll:开启slave的复制。http://slave_host:port/solr/replication?command=enablepoll
8)abortfetch:终止slave上正在进行的下载文件过程。http://slave_host:port/solr/replication?command=abortfetch
9)commits:show当前仍旧保留的IndexCommit信息。
10)details:show slave当前的复制细节信息。http://slave_host:port/solr/replication?command=details
11)enablereplication:启动master对所有slave的复制功能 http://master_host:port/solr/replication?command=enablereplication
12)disablereplication:关闭master对所有slave的复制功能 http://master_host:port/solr/replication?command=disablereplication

 

 

比如主机为:http://172.16.200.1:8080/solr/

子机为:http://172.16.200.2:8080/solr/

 

手动复制数据,触发分发请求:

 

http://172.16.200.2:8080/solr/replication?command=fetchindex&masterUrl=http://172.16.200.1:8080/solr/replication

可以写成一个脚本,在系统的定时任务上定时运行脚本。

测试过1G的索引,速度很快,几乎不用担心服务中止。

 

以下引自http://www.kafka0102.com/2010/07/249.html

1、master的工作

对于ReplicationHandler的复制功能来说,核心的问题确定是在一个时间点要复制哪些文件,这就用上了lucene的IndexDeletionPolicy的特性。lucene在初始化时,会调用IndexDeletionPolicy.onInit(List<? extends IndexCommit> commits)方法;lucene在commit(触发的时机也可以是optimize、close,solr在commit时实际上就是close了indexwriter)时,会调用IndexDeletionPolicy.onCommit(List<? extends IndexCommit> commits)。IndexCommit对象中保存了该次提交关联的文件列表等信息,这使得solr中的复制过程中,slave可以从master得到文件列表后跟本地文件做比较,跳过不变的文件,下载新文件,并删除无用的文件。IndexDeletionPolicy的两个针对commits的函数,会对当前存在的commits列表做些处理,比如lucene默认的KeepOnlyLastCommitDeletionPolicy会只保留最新的IndexCommit,对那些过时的IndexCommit执行delete操作以将无用的文件删掉。solr中,SolrDeletionPolicy默认也是保留最新一个IndexCommit,但可以设置maxCommitAge、maxCommitsToKeep、maxOptimizedCommitsToKeep来保留更多的IndexCommit。但solr真正使用的IndexDeletionPolicy实现是IndexDeletionPolicyWrapper,它是SolrDeletionPolicy的wrap。在slave从master复制文件的过程中,要保证当前正在复制的IndexCommit点不能被删除,这就用到了IndexDeletionPolicyWrapper中的void setReserveDuration(Long indexVersion, long reserveTime)方法,该方法会在master向slave响应indexversion、filelist命令前、以及每向slave传送5M的索引文件内容时调用,而默认的reserveTime时间是10s,如果慢速网络传输5M数据需要10秒以上,就需要调整该值了。

ReplicationHandler复制文件没有采用rsync,而是使用http,它在读一个文件内容传输到slave时,默认是按照1M大小分段输出内容到slave(http chunked?),并且默认是对每段内容做了checksum,保证传输的内容的正确性。上面提到的setReserveDuration点,主要就是它在packetsWritten % 5 == 0次数后触发一次修改。

ReplicationHandler还可以备份索引文件。由于lucene的索引文件只是追加新文件而不会修改已有文件,所以只要针对一个IndexCommit点做备份,其过程还是很简单的。

2、slave的工作

slave启动时会创建SnapPuller对象,SnapPuller会启动一个线程定时的(pollInterval间隔)从master复制数据(fetchLatestIndex方法)。对于一次复制过程,slave和master交互处理细节如下:
1、slave首先向master询问最新的索引版本号(indexversion命令),slave检查得到的latestVersion、latestGeneration有效后,和本地的IndexCommit的getVersion()、getGeneration()比较,如果不相等,则需要往下进行,否则等待下一次调度。



  boolean isFullCopyNeeded = commit.getGeneration() >= latestGeneration;
      File tmpIndexDir = createTempindexDir(core);

     if (isIndexStale())//如果本地有文件跟master同名但大小不一样则认为从索引被破坏,需要完全下载
        isFullCopyNeeded = true;
      successfulInstall = false;


2、slave向master请求之前得到的indexversion下的文件列表(filelist命令,包括索引文件和可选的配置文件)。如果文件列表为空,则返回等待下一次调度。否则,就需要检查哪些文件需要被下载过来。这里做的判断有:1)如果本地的commit.getGeneration() >= latestGeneration,说明本地索引文件被破坏(比如对slave不小心提交了修改索引的命令),需要完全将master的文件复制过来。2)逐个检查文件列表中的文件是否在本地存在,不存在就下载下来。


  private void downloadIndexFiles(boolean downloadCompleteIndex, File tmpIdxDir, long latestVersion) throws Exception {
    for (Map<String, Object> file : filesToDownload) {
      File localIndexFile = new File(solrCore.getIndexDir(), (String) file.get(NAME));
      //文件不存在或者需要完全下载的时候才会下载
      if (!localIndexFile.exists() || downloadCompleteIndex) {
        fileFetcher = new FileFetcher(tmpIdxDir, file, (String) file.get(NAME), false, latestVersion);
        currentFile = file;
        fileFetcher.fetchFile();
        filesDownloaded.add(new HashMap<String, Object>(file));
      } else {
        LOG.info("Skipping download for " + localIndexFile);
      }
    }
  }




3、对于下载文件内容,对应命令是filecontent。下载的文件显然需要放到临时目录中,这个临时目录和已有的索引目录(默认名字index)在同一数据目录下,只是命名为index.<时间戳>。下载完毕后,copy数据有两种情况:1)如果是完全下载,则不需要将临时目录中的文件copy到已有目录中,而是修改数据目录中的index.properties,标识索引目录为新生成的临时目录,而旧索引目录并不会被删除,可以手工删掉,当然,通常是不应该出现slave的Generation大于master的异常情况。2)通常就是把临时索引目录的文件copy到旧索引目录,copy时要把segments_N放到最后copy,避免copy中途出现异常造成数据被毁。


 private boolean copyAFile(File tmpIdxDir, File indexDir, String fname, List<String> copiedfiles) {
    File indexFileInTmpDir = new File(tmpIdxDir, fname);
    File indexFileInIndex = new File(indexDir, fname);
    boolean success = indexFileInTmpDir.renameTo(indexFileInIndex);
    if(!success){
      try {
        LOG.error("Unable to move index file from: " + indexFileInTmpDir
              + " to: " + indexFileInIndex + "Trying to do a copy");
        FileUtils.copyFile(indexFileInTmpDir,indexFileInIndex);
        success = true;
      } catch (IOException e) {
        LOG.error("Unable to copy index file from: " + indexFileInTmpDir
              + " to: " + indexFileInIndex , e);
      }
    }
    if (!success) {
      for (String f : copiedfiles) {
        File indexFile = new File(indexDir, f);
        if (indexFile.exists())
          indexFile.delete();
      }
      delTree(tmpIdxDir);
      return false;
    }
    return true;
  }





4、当新索引和可选的配置文件copy完毕之后,slave会对solrcore的UpdateHandler做commit操作,这会close掉indexwriter并强制重启新的indexsearcher提供服务。同时,如果solrcore的UpdateHandler是DirectUpdateHandler2(不应该不是),会强制调用handler.forceOpenWriter()来删除旧的无用的索引文件,并调用replicationHandler.refreshCommitpoint()来更新slave的indexCommitPoint。








5、如果索引复制失败,slave会向数据目录下的replication.properties输出复制失败的信息。

 

 

 

 

 

 

 

 

 

 

 

 

 


转自:http://blog.csdn.net/duck_genuine/article/details/6165314

相关问答

更多
  • 虽然我在这里遇到了一个老问题,但我迟到了一点。 答案是Solr Cloud在内部处理复制。 Solr Cloud wiki页面详细解释了这一点。 如果你设置了numShards = 2并添加更多的服务器(这样你总共有四个),分片将被复制到新的服务器 - 确保你的分片位于多个节点上。 直接回答你的问题; SolrCloud为你做了复制设置和逻辑,你应该让它做它自己的事情,而不是在混合中引入“手动”设置复制。 SolrCloud的重点在于隐藏复制和共享逻辑,允许您在可用时简单添加更多服务器。 当然,您可以创建逻 ...
  • 我希望你想要双向复制的主要原因是支持跨数据中心的情况。 也就是说,您希望将查询隔离到特定位置,但要在高延迟链接中保持同步。 如果您不需要这个,只需使用SolrCloud并让它处理复制。 您可以对索引进行分片并获得所需的更新吞吐量。 任何更新都可以转到任何节点,Solr会确保将其写入正确的位置。 如果您真的在考虑数据中心,Solr在6.0中添加了一些全新的数据中心支持,您可以在这里阅读: https : //sematext.com/blog/2016/04/20/solr-6-datacenter-repl ...
  • 根据JIRA( https://issues.apache.org/jira/browse/SOLR-5532 ),这个错误出现在Solr 4.6中 我建议您至少更新Solr 4.6到4.6.1,或者如果可能的话,更新到4.7或甚至更新到4.10。 According to JIRA (https://issues.apache.org/jira/browse/SOLR-5532) this bug was here in Solr 4.6 I will recommend you to update So ...
  • 如果您希望控制复制发生的时间,我建议禁用轮询并使用外部调度程序复制REST API。 我们也使用它来对master进行一些验证,然后让slave复制,这会影响最终用户。 If you are looking to control the time at which replication should happen, I would suggest to disable polling and make use of replication REST API with an external schedul ...
  • 所以我的第一个答案是我的第一个问题;) 最初设置此核心时,使用了像这样的导入查询。 SELECT * FROM [TABLE] 然后在data-import-handler中将字段映射为这样。 当Solr开始将SQL中[ENDTIME](datetime2)列的内容转换为日期时,会将其添加到导入查询中。 CAST(CAST(ENDTIME as datetime2(0)) as varchar(100)) as ENDT ...
  • 首先:1.4是Solr的一个非常老的版本。 我不确定当时是否支持每个字段值(突出显示本身是在Solr 1.3中引入的)。 默认的荧光笔在3.1中已更改。 但是,您应该能够通过为hl.maxAnalyzedChars提供一个较大的值来突出显示字段中的所有hl.maxAnalyzedChars (不确定-1是否会执行您想要的操作)。 尝试的另一个选择应该是具有大的hl.maxAnalyzedChars值和大的hl.fragsize值(对两个字段使用相同的值而不是0)。 如果您仍然无法使其工作,请在更新版本的So ...
  • 您可以使用构面查询而不是构面范围来完成所要求的内容。 尝试这样的事情: facet.query=myDateField:[NOW-11MONTH/MONTH TO NOW-10MONTH/MONTH] facet.query=myDateField:[NOW-10MONTH/MONTH TO NOW-9MONTH/MONTH] facet.query=myDateField:[NOW-9MONTH/MONTH TO NOW-8MONTH/MONTH] ... 等等。 现在你可以完全控制任何一个方面, ...
  • 也许不是一个正确的答案,但问题似乎解决了。 当然,我们必须做一些事情才能实现这一点,但是我们所能想到的就是删除log4j属性文件中的CONSOLE日志记录并删除它创建的11GB日志文件。 猜猜这可能至少可以为其他人提供其他东西来尝试谁有同样的问题。 Maybe not a proper answer but the issue seemed to resolve itself. Of course we must have done something to make this happen, howeve ...
  • 您应该能够在论坛中找到Solr Master Slave Heirarchy Vs SolrCloud的比较,但这些是我对他们的理解/体验,所以要轻易消耗它。 该怎么做以及为什么? 这完全取决于您的业务需求。 我想说如果你的应用程序只是从SOLR中读取数据并且在从SOLR读取数据时需要高可用性,那么简单的一个主机到多个从机层次结构就足够了。 但是,如果您正在寻找SOLR写入的高可用性,那么SolrCloud是一个正确的选择 SolrCloud是更好的解决方案吗? 没有什么比这更好了,维护SolrCloud需 ...
  • 我正在将升级从4.7升级到5.3。 它实际上比你所做的要容易得多。 这是最简单的方法: 1.在同一个盒子上并行安装。 创建集合等... 2.将模式和solrconfig合并。 将新的和旧的并排打开,并将旧的和旧的一起复制到新的中。 有一些差异; 所以这很重要。 确保这个工作。 3.关闭Solr 4,将索引复制到新的Solr中。 4.索引应该没有问题。 但确保这个工作。 5.在新实例上执行特定于环境的任务,例如设置复制等。 Volia! 我在Solr面前有nginx,所以一旦升级完成,我就可以将nginx重定 ...