题记:本来计划的SolrCloud的Recovery策略的文章是3篇的,但是没想到Recovery的内容蛮多的,前面三章分别介绍了Recovery的原理和总体流程,PeerSync策略,Replication策略。本章主要介绍我在实际生产环境中碰到的recovery的几个问题,以及前面漏下的几个点。
一. 日志中多次出现"Stopping recovery for zkNodeName= ..."
我在公司的生产环境中总是会看到连续多次出现 " WARN : Stopping recovery for zkNodeName= ..." 或者 "INFO : Starting recovery process. core=..." 这样的日志(由于公司的东西无法拿出了,所以只能意会下日志了)。
这种现象的原因是因为:前文讲到过出现Recovery的原因之一是Leader转发update request到replica后没有接收到replica的表示成功的返回,那么这是Leader会发送RequestRecovery request给replia,命令它进行recovery。这是一次转发失败的过程。而每当Solr出现Leader转发update失败时候往往不会只出现一次,所以Leader会发送多次RequestRecovery request给replia。
Relica的Recovery过程起始于DefaultSolrCoreState类的doRecovery()函数,在进行doRecovery()时候Replica会取消之前的Recovery。所以出现上述现象的根本原因就在于cancelRecovery上。需要指出的是DefaultSolrCoreState类的doRecovery()函数不但在RequestRecovery请求后会被调用,在leader 选举失败的时候也会被掉用。
1 @Override 2 public void cancelRecovery() { 3 synchronized (recoveryLock) { 4 if (recoveryStrat != null && recoveryRunning) { 5 recoveryStrat.close(); 6 while (true) { 7 try { 8 recoveryStrat.join(); 9 } catch (InterruptedException e) { 10 // not interruptible - keep waiting 11 continue; 12 } 13 break; 14 } 15 16 recoveryRunning = false; 17 recoveryLock.notifyAll(); 18 } 19 } 20 }
1 @Override 2 public void close() { 3 close = true; 4 try { 5 prevSendPreRecoveryHttpUriRequest.abort(); 6 } catch (NullPointerException e) { 7 // okay 8 } 9 log.warn("Stopping recovery for zkNodeName=" + coreZkNodeName + "core=" + coreName); 10 }
二. Recovery过程中的rollback
之前有@从前 网友给我留言说出现了"持续向solrcloud提交数据的同时调用了optimize 方法。导致索引文件同步失败,就一直无法recovery。"的现象。造成这个现象的原因大致由以下两点:
- optimize的操作的本质是Merge策略中的forceMerge,默认情况下一旦触发了forceMerge,那么Solr会把所有的Segment合并成一个Segment。可以想象下,几十甚至几百GB的数据合成一个Segment,这样的符合会有多大?而且这还不算,一旦触发了forceMerge,如果有实时数据进来,那么它会把新进来的数据也merge进去,也就是说会一直merge进去根本不会停下来。关于forceMerge的具体情况,将在接下来介绍Merge的文章中详述。
- Replication策略介绍的时候提到,如果isFullCopyNeeded为false,那么Solr就会调用closeIndexWriter.
1 if (!isFullCopyNeeded) { 2 // rollback - and do it before we download any files 3 // so we donx27t remove files we thought we didnx27t need 4 // to download later 5 solrCore.getUpdateHandler().getSolrCoreState() 6 .closeIndexWriter(core, true); 7 }
我们很容会忽视closeIndexWriter传入的true参数,如果传入的为true,表示Solr关闭IndexWriter时候会进行回滚rollback,它的作用就是将IndexWriter退回到上次commit之后的状态,清空上次commit之后的所有add进来的数据。
1 if (indexWriter != null) { 2 if (!rollback) { 3 try { 4 log.info("Closing old IndexWriter... core=" + coreName); 5 indexWriter.close(); 6 } catch (Exception e) { 7 SolrException.log(log, "Error closing old IndexWriter. core=" 8 + coreName, e); 9 } 10 } else { 11 try { 12 log.info("Rollback old IndexWriter... core=" + coreName); 13 indexWriter.rollback(); 14 } catch (Exception e) { 15 SolrException.log(log, "Error rolling back old IndexWriter. core=" 16 + coreName, e); 17 } 18 } 19 }
那么问题就出在rollback中,Lucene的IndexWriter在进行回滚的时候会尝试去关闭正在进行的mergePolicy和mergeScheduler,如果发现还有segment正在进行那么它会一直等待,所以当optimize(forceMerge)进行时且有实时数据进来,那么Recovery就会一直停在那里直到超时。
1 ...阅读原文
2. Solr4.8.0源码分析(13)之LuceneCore的索引修复 (R:1801)[2016-01-19]
3. Solr4.8.0源码分析(1)之Solr的Servlet (R:1366)[2015-12-25]
4. Solr4.8.0源码分析(4)之Eclipse Solr调试环境搭建 (R:1388)[2015-12-31]
5. Solr4.8.0源码分析(6)之非排序查询 (R:1278)[2016-01-06]
6. Solr4.8.0源码分析(9)之Lucene的索引文件(2) (R:1465)[2016-01-11]
7. Solr4.8.0源码分析(23)之SolrCloud的Recovery策略(四) (R:1450)[2016-02-12]
8. Solr4.8.0源码分析(7)之Solr SPI (R:1531)[2016-01-08]
9. Solr4.8.0源码分析(19)之缓存机制(二) (R:1493)[2016-01-30]
10. Solr4.8.0源码分析(12)之Lucene的索引文件(5) (R:1419)[2016-01-16]
11. Solr4.8.0源码分析(2)之Solr的启动(一) (R:1593)[2015-12-24]
12. Solr4.8.0源码分析(17)之SolrCloud索引深入(4) (R:1390)[2016-01-27]
13. Solr4.8.0源码分析(5)之查询流程分析总述 (R:1553)[2016-01-03]
14. Solr4.8.0源码分析(24)之SolrCloud的Recovery策略(五) (R:1401)[2016-02-12]
15. Solr4.8.0源码分析(15) 之 SolrCloud索引深入(2) (R:1322)[2016-01-23]