最近又被soft commit 和 hard commit搞得有点迷糊,其实都怪自己没有早点看源代码。问题出自这段代码,这也是我一开始接触solr时,查到的代码sample(事实证明问题都出在这里,引以为戒,有问题真的不要不求甚解)
if (rs.next()){
while (true) {
NiuniuSolrInputDocument doc = new NiuniuSolrInputDocument(rs);
docs.add(doc);
idx++;
if(idx>=1000){
httpSolrServer.add(docs);
httpSolrServer.commit();
docs.clear();
idx = 0;
}
if (!rs.next())
break;
}
if(idx!=0){
httpSolrServer.add(docs);
httpSolrServer.commit();
}
}
问题
可以看到我每次执行完add方法以后,都会commit一下,认为只有commit过后文档才会真正的提交。如果按照这个思路走,批量建索引等可能问题不算大,但是如果研究solrconfig的autoCommit和autoSoftCommit就会有问题了。
问题来了,如果有autoCommit那我为毛要手动commit?
事实是,每次执行httpSolrServer.add(docs)
方法,就会进行如下工作:
public UpdateResponse add(Iterator<SolrInputDocument> docIterator) throws SolrServerException, IOException {
UpdateRequest req = new UpdateRequest();
req.setDocIterator(docIterator);
return req.process(this);
}
即add方法已经把文档加入到solr中了,只要你的solr设置过autoCommit,那么每隔一段时间或者每当累积的文档到达一定数量,就会进行一次commit。
hard commit AND soft commit
看了manual里的autoCommit和autoSoftCommit的介绍,autoCommit就是自动commit,这里的commit指的是hard commit,里面有几个参数:
maxDocs:add到solr缓冲区的文档数量超过这个值时,自动执行commit把结果写入到磁盘索引文件中
maxTime:最长多久执行一次commit,即如果文档一直没有通过commit沉淀到磁盘上,那么持久性就可能出问题,所以要隔一段时间就执行一次commit
openSearcher:要不要新开一个searcher,如果要新开,那么目前已有的cache就会失效,具体代码可以看SolrIndexSearcher,它里面有几个Cache成员变量,如果你新建立一个searcher那么cache失效也是正常的了,除非你有autoWarming机制。在详细一点,就是,如果不执行softCommit(autoSoftCommit的maxTime设置成-1),同时在hard commit的时候openSearcher=false,那么执行完hard commit之后,磁盘文件虽然改变了,但是searcher没有新开,需要reload或者重启,才能看到索引中的新文档。所以相当于softCommit本身就自带openSearcher,那么,如果autoCommit的openSearcher=false,就一定要做softCommit,或者就索性不autoSoftCommit,而是使用autoCommit + openSearcher=true。
上面提到了hard commit,那么对应的就是softCommit,softCommit和NRTCachingDirectoryFactory息息相关。NRT是Near Realtime的缩写,即近实时的索引方式。
<directoryFactory name="DirectoryFactory"
class="${solr.directoryFactory:solr.NRTCachingDirectoryFactory}"/
solr.NRTCachingDirectoryFactory, the default,
wraps solr.StandardDirectoryFactory and caches small files in memory
for better NRT performance.
solr.RAMDirectoryFactory is memory based, not
persistent, and doesn't work with replication
这个有什么用呢?淘宝的增量商品架构走的就是这种方式,解释一下用它的理由:
- 用户增量添加商品,我们需要让这些增量商品尽早能被检索到
- 按照标准的索引文件的格式,文档想要被检索到就需要改写磁盘上的文件,频繁的改写带来了难以接受的IO开销
- 那么我们需要一种折中的方案,可以先把这部分增量放到内存中,这也是cache的意思,在检索时会去索引文件和内存中都去查询,这样的话就能实时把文档检索到,同时不会太频繁的更新磁盘
- 放在内存而不是磁盘就意味着存在persistence的问题,如果服务crash,内存中的文档就会丢失,这个时候我们就需要autoCommit了。
所以如果我们面临的是heavy index和heavy query的场景,需要在提交后就能立马检索到,就需要对每次增量都进行soft commit,然后设置一个我们能够容忍的时间interval或者数量上限,定时或者定量去执行一次hard commit,这样即使系统crash我们也只会丢失这个时间段的文档,而之前的文档已经都持久化了。而如果能够容忍提交后隔20秒才能搜索到,那么就设置autoSoftCommit参数为20000就好了,这样add到index中的文档每隔20s就会自动softCommit一次,就能被检索到了。
最终方案,我们把UpdateHandler的参数设置成:
<autoCommit>
<maxTime>600000</maxTime>
<openSearcher>false</openSearcher>
</autoCommit>
<autoSoftCommit>
<maxTime>5000</maxTime>
</autoSoftCommit>
然后在增量提交文档时,不需要指定commit或者softCommit,新的文档会根据autoSoftCommit来自动进行软提交,然后会根据autoCommit来自定进行hard commit,这样新添加的文档在5秒之内就可以被检索到,然后每隔10分钟,这些文档就会写到磁盘上。
curl
http://localhost:8080/solr/niuniu_resource/update? -H "Content-Type: text/xml" --data-binary
'
<add>
<doc>
<field name="raw_id">123478</field>
</doc>
</add>