网站建设 流程,咸宁网站建设哪家好,linux WordPress上传插件需要ftp,2万元建设网站贵吗本文分两部份#xff0c;第一部份为译#xff1a;是对是对于lucene事务的一篇佳作《Transactional Lucene》的翻译。第二部份为解#xff1a;是本人对一文中提到一些概念在源码层次的一些理解分析#xff0c;参考lucene源码版本为4.10.4。《Transactional Lucene》中还提到…本文分两部份第一部份为译是对是对于lucene事务的一篇佳作《Transactional Lucene》的翻译。第二部份为解是本人对一文中提到一些概念在源码层次的一些理解分析参考lucene源码版本为4.10.4。《Transactional Lucene》中还提到了多commit在实际生产中的一些妙用值得参考。
一、译
很多用户并不了解Lucene API的事务语义及其在搜索应用中的用途。对于初学者应当了解的Lucene ACID特性如下
Atomiciy 原子性 当你在一次IndexWriter session中做操作增加删除文档然后commit要么你的所有的操作修改都是可见的commit成功要么所有的操作修改都不可见commit失败绝不会处于某种中间状态。有些方法有它自身的原子操作如果你调用updateDocument方法其内在实现是先删除后添加文档即使你打开了一个近实时(NRT)reader或者使用另一个线程做commit绝不会出现只有删除而没有添加的情况。与此类似如果使用addDocuments方法添加一组文档对于任何reader而言要么所有的文档可见要么所有文档不可见。(对原文未提到的一点补充通过Indexwriter.getReader获得的Reader是能看到上次commit之后IndexWriter执行至当前的所有变化的在解的部份中将对其进行详细说明。)Consistency 一致性 如果计算机或者OS奔溃或者jvm挂掉或被杀死亦或是电源被拔掉了你的索引都会保持完好。注意像RAM故障cpu位翻转或者文件系统损坏之类的问题还是容易造成索引破坏的。Isolation 隔离性 当IndexWriter正在做更改的时候所有更改都不会对当前搜索该索引的IndexReader可见直到你commit或者打开了一个新的NRT reader。一次只能有一个IndexWriter实例对索引进行更改。Durablity 持久性 一旦commit操作返回所有变更都会被写入到持久化存储。如果计算机或者OS奔溃或者jvm挂掉或被杀死亦或是电源被拔掉了所有的变更都已在索引中保存。
Lucene提供了二阶段提交API: 调用prepareCommit方法完成主要的提交工作应用缓存的delete操作写入被缓存中的文档fsync文件。如果发生故障例如硬盘占满基本可以肯定它会发生在第一阶段prepareCommit阶段。然后调用commit方法完成事务。
当你调用IndexWriter的close方法时会自动调用commit方法。如果你想要丢弃自上次commit以来的所有修改可以调用rollback方法。你甚至可以对一次CREATE进行rollback: 如果你有已经有了一个index你使用OpenMode.CREATE选项打开一个IndexWriter然后再调用rollback,index会保持不变。同样你也可以调用deleteAll方法然后rollback回来。
注意仅在一个新的目录上打开一个IndexWriter并不会产生一个空的commit操作即你无法在这个目录上打开一个IndexReader直到你完成一次commit。
Lucene自身并未实现事务日志但在更高层上可以方便实地现。例发Solr与ElasticSearch就实现了事务日志。
同一索引的多Commit
单个lucene索引可以保存多个commit这是一个强大的特性但往往容易被人们所忽视。每次commit都持有该commit被创建的时间点的过引索视图。 这个特点类似于ZFS和新兴的Btrfs这样的现代文件系统的快照和写克隆功能。事实上Lucene是能够暴露多次commit状态其内在的原理是所有的索引分片和文件只会被写一次正如ZFS和Btrfs上的块。
为了保存多次commit到你的索引中只需要实现你自己的IndexDeletionPolicy并将其传递给IndexWriter。Lucene正是通过这个类才知站定哪些commit是要被删除的IndexWriter会在打开索引和任何一次完成commit的时候调用它。KeepOnlyLastCommitDeletionPolicy是默认的删除机制实现它会删除掉除了最近一次commit以外的所有commit。如果采用NoDeletionPolicy那么每一次commit都会保存。
你可以在commit的时候传送userData (MapString,String)用于记录关于本次commit的一些用户自定义信息对Lucene不透明然后使用IndexReader.listCommits方法获得索引的所有commit信息。一旦你找到了一次commit你可以在其上找开一个IndexReader对commit点上的索引数据进行搜索。
你也可以基于之前的commit打开一个IndexWriter回滚之后的所有变动。这有点类似于rollback方法不同之处在于它允许你在多个commit间rollback而不仅是回滚当前IndexWriter会话中所做的变动。
当你使用OpenMode.CREATE参数打开一个索引的时候老的commit仍会保存。你也可使用OpenMode.CREATE同时还在老的commit上用IndexReader进行搜索。这使得一些有趣的应用情影成为可能例如你可以在不影响当前任何打开的reader前提下在各个commit间做过索引全量重建工作。
组合上这些有意思的事务特征你可以完成一些很酷的工作 利用SnapShotDeletionPolicy或者PersistentSnapshotDeletionPolicy实现热备 these deletion policies make it trivial to take a live backup of the index without blocking ongoing changes with IndexWriter. 备份可以轻易的实现增量只要复制新的文件并去掉删除文件,而你可以减少IO消耗以减轻对于搜索的干扰。 搜索不同版本的目录 也许你在运行一个商业网站却要辗转于不同版本的目录之间。在这种情况下你就可以保存更老的commit允许用户选择搜索哪个版本的目录。 在同一个初始索引上进行多次索引实验也许你想要在同一个大的索引上运行一系列的性能测试实验例如尝试不的RAM缓存大小或者merge因子。要想如此你可以在运行完每次测试之后不要关闭IndexWriter而使用回滚方法快迅地恢得到初始状态以备下次测试。 强制merge所有的片段到单一片段中但依然保存之前的多片段时候的commit。如此你就可以做多个段vs单一段的性能比较实验。 在NFS文件系统上做索引与搜索 由于NFS无法保证当前打开的文件不被删除你必须使用IndexDeletionPolicy来保存每次提交直至所有的reader都不再使用该commit即重新在一个更新的commit上打开。一种简单的解决方案是基本于时间的例如不删除该commit直到创建该commit15分钟之后然后每5分钟重新打开reader一次。不然你在NFS上进行搜索时将会遇到各种可怕的异常。 分布式commit: 如果你有其他的资源需要有lucene索引发生变化的同时提交你就可以使用二阶段提交API。这样的做法很简单但容易在第二阶段发生失败 例如lucene在其第二阶段完成了提交而数据库在第二阶段中发生了一些错误、奔溃或者是断电这时你就可以通过在一个较早的commit上重新打开一个IndexWriter来rollback Lucene的这次commit。 进行实验性的索引更新也许你只想对索引的一个子集做一次re-indexing操作但你又不确定这样的操作是否会成功。这种情况下只要保存老的commit如果操作失败就执行rollback,如果成功则删除老的commit。 基于时间的快照也许你想能够更自由地回滚到1天前一周前一个月之前...的索引状态就保可以根据这些时间点保存这些commit。
注意保存多个版本的commit必然会带来更多的磁盘空间消耗。然而这些消耗往往会比较小因为多个commit往往会共享一些索引片段尤其是那些更大更早的片段。
二、 解
对于Lucene事务的原子性、隔离性以及近时实(NRT)搜索
上文在原子性的描述中提到“要么你的所有的操作修改都是可见的commit成功要么所有的操作修改都不可见commit失败绝不会处于某种中间状态。” 但没有提到通过Indexwriter.getReader获得的Reader是能看到上次commit之后IndexWriter执行到当前的所有变化的。 When you ask for the IndexReader from the IndexWriter, the IndexWriter will be flushed (docs accumulated in RAM will be written to disk) but not committed (fsync files, write new segments file, etc).The returned IndexReader will search over previously committed segments, as well as the new, flushed but not committed segment. Because flushing will likely be processor rather than IO bound, this should be a process that can be attacked with more processor power if found to be too slow. 还有人们常使用openIfChanged方法来实现近实时搜索。我们来看看Lucene DirectoryReader的标准实现StandardDirectoryReader中的oldOpenIfChanged方法的实现。可以发现所谓的doOpenIfChanged内部也是靠优先尝试从IndexWriter获得DirectoryReader来实现的。 Overrideprotected DirectoryReader doOpenIfChanged() throws IOException {return doOpenIfChanged((IndexCommit) null);}Overrideprotected DirectoryReader doOpenIfChanged(final IndexCommit commit) throws IOException {ensureOpen();// If we were obtained by writer.getReader(), re-ask the// writer to get a new reader.if (writer ! null) {return doOpenFromWriter(commit);} else {return doOpenNoWriter(commit);}}现在抛开近实时搜索不使用该新特性那么上文中作者提到的原子性特征是完备的。在不考虑merge的情况下Lucene的每一次commit将内存中积累的索引变更写入到硬盘形成新的索引分段包括文档删除操作。这样的好处在于每一次commit不用去复杂又耗时地修改之前的索引分段只要累加新文件即可。但这样的后果是越来越多的分片存在影响查询效率所以才需要merge机制的存在不断地去合并分段这是另话了。
上文中所提的隔离性就是基于这种机制实现的非NRT的正常reader打开时只能获得当前已commit到磁盘的分段文件信息的索引数据无法得到两次commit的中间状态。 而NRT的reader可以获得打开时IndexWriter所应用的所有变更但也无法感知到之后的IndexWriter所做的索引变化除非重新打开。
至于“一次只能有一个IndexWriter实例对索引进行更改”这是靠Lucene实现的排它锁实现的你将会在你的索引目录下看到write.lock文件的存在。
Lucene commit
Lucene多个commit的保存与删除到底是怎么回事?如何基于commit打开索引? 这一系列的问题还是得需要通过了解lucene如何保存每次commit才能理解。
我们首先看看一个IndexCommit的子类一般都包含哪些信息。一个IndexCommit实例就代表了一次索引commit。对索引内容的任何变更只有在segments_N文件完成写入之后才可见。
public abstract class IndexCommit implements ComparableIndexCommit {/*** Get the segments file (codesegments_N/code) associated * with this commit point.*/public abstract String getSegmentsFileName();/*** Returns all index files referenced by this commit point.*/public abstract CollectionString getFileNames() throws IOException;/*** Returns the {link Directory} for the index.*/public abstract Directory getDirectory();/*** Delete this commit point. This only applies when using* the commit point in the context of IndexWriters* IndexDeletionPolicy.*/public abstract void delete();...
}两个重要的方法getSegmentsFileName与getFileNames。segmentsFileName是与当前commit关联的segments_N文件名。N是一个递增的值每次commit对应的N都会增大。所以如果保存多个commit的话自然会出现多个segments_N文件。segments_N具体格式内容在此不作具体解释总体它记录了lucene索引的所有分段的元数据信息根据segments_N文件可以清楚地知道当前commit的索引内容分布于哪些分段之中。getFileNames返回的是当前commit所引用的所有文件。 如此每次某个commit上打开的IndexReader或IndexWriter就知道应该去加载哪些文件。也正因如此多个commit能够共享一些老的索引分段而不至于每个commit占用太大的存储空间。当涉及到commit删除时由于lucene对索引文件的删除是通过引用计数的方式实现的只要对commit引用的文件调用一次IndexFileDeleter.decRef(Collection files)方法即可。只有引用计数为0的文件才会真正地被删除。
Luncene IndexDeletionPolicy
IndexDeletionPolicy它所能做的是在两个方法时间结点上对IndexCommit做删除。定义如下
public abstract class IndexDeletionPolicy {protected IndexDeletionPolicy() {}public abstract void onInit(List? extends IndexCommit commits) throws IOException;public abstract void onCommit(List? extends IndexCommit commits) throws IOException;
}onInit方法只在IndexWriter初始化时被调用onCommit在每次commit操作的时候被调用。commits列表中包含了当前所有的commit点按从老到新的顺序排列。 默认实现KeepOnlyLastCommitDeletionPolicy删除上一次commit以外的所有commit相关文件减少引用计数。 SnapshotDeletionPolicy是采用wrapper模式对现有IndexDeletionPolicy的一层封装。除了onInit和onCommit方法外它还提供了snapshot和release两个方法。snapshot得到的IndexCommit将不会被删除直到其被release所以比较适用于备份的场影在备份之前调用snapshot直到备份完成再调用release。 SnapshotDeletionPolicy只在内存中保存snapShot信息如果要保证数据持久化不丢失可使用PersistentSnapshotDeletionPolicy。
Lucene二阶段提交的实现
IndexWirter继承了TwoPhaseCommit接口实现三个方法prepareCommitcommit与rollback。
prepareCommit完成二阶段提交第一阶段的工作它会尽可能多的完成更新工作但又避免完成真实的提交。你可以轻松地利用rollback废弃掉当前阶段完成的所有工作。 事实上本次commit所产生的段文件已写入存储。commit方法是完成第二阶段的工作它只作很少的工作只有该方满返回调用者才能确认索引相应操作已完成并持久化到存储。跟踪代码直至SegmentIfnos的finishCommit方法可见commit成功的情况下只做了两件事情一是在segments_N未填入4byte的校验合还有就是close写入流完成fsync。新的commit,只有在校验和正确的情况下对IndexReader可见。final String finishCommit(Directory dir) throws IOException {if (pendingSegnOutput null) {throw new IllegalStateException(prepareCommit was not called);}boolean success false;final String dest;try {CodecUtil.writeFooter(pendingSegnOutput);success true;} finally {if (!success) {// Closes pendingSegnOutput deletes partial segments_N:rollbackCommit(dir);} else {success false;try {pendingSegnOutput.close();success true;} finally {if (!success) {// Closes pendingSegnOutput deletes partial segments_N:rollbackCommit(dir);} else {pendingSegnOutput null;}}}}rollback:废弃掉上次commit以来的所有变更操作。
20151005首发于3dobe.com 链接地址http://3dobe.com/archives/172/
本文分两部份第一部份为译是对是对于lucene事务的一篇佳作《Transactional Lucene》的翻译。第二部份为解是本人对一文中提到一些概念在源码层次的一些理解分析参考lucene源码版本为4.10.4。《Transactional Lucene》中还提到了多commit在实际生产中的一些妙用值得参考。
一、译
很多用户并不了解Lucene API的事务语义及其在搜索应用中的用途。对于初学者应当了解的Lucene ACID特性如下
Atomiciy 原子性 当你在一次IndexWriter session中做操作增加删除文档然后commit要么你的所有的操作修改都是可见的commit成功要么所有的操作修改都不可见commit失败绝不会处于某种中间状态。有些方法有它自身的原子操作如果你调用updateDocument方法其内在实现是先删除后添加文档即使你打开了一个近实时(NRT)reader或者使用另一个线程做commit绝不会出现只有删除而没有添加的情况。与此类似如果使用addDocuments方法添加一组文档对于任何reader而言要么所有的文档可见要么所有文档不可见。(对原文未提到的一点补充通过Indexwriter.getReader获得的Reader是能看到上次commit之后IndexWriter执行至当前的所有变化的在解的部份中将对其进行详细说明。)Consistency 一致性 如果计算机或者OS奔溃或者jvm挂掉或被杀死亦或是电源被拔掉了你的索引都会保持完好。注意像RAM故障cpu位翻转或者文件系统损坏之类的问题还是容易造成索引破坏的。Isolation 隔离性 当IndexWriter正在做更改的时候所有更改都不会对当前搜索该索引的IndexReader可见直到你commit或者打开了一个新的NRT reader。一次只能有一个IndexWriter实例对索引进行更改。Durablity 持久性 一旦commit操作返回所有变更都会被写入到持久化存储。如果计算机或者OS奔溃或者jvm挂掉或被杀死亦或是电源被拔掉了所有的变更都已在索引中保存。
Lucene提供了二阶段提交API: 调用prepareCommit方法完成主要的提交工作应用缓存的delete操作写入被缓存中的文档fsync文件。如果发生故障例如硬盘占满基本可以肯定它会发生在第一阶段prepareCommit阶段。然后调用commit方法完成事务。
当你调用IndexWriter的close方法时会自动调用commit方法。如果你想要丢弃自上次commit以来的所有修改可以调用rollback方法。你甚至可以对一次CREATE进行rollback: 如果你有已经有了一个index你使用OpenMode.CREATE选项打开一个IndexWriter然后再调用rollback,index会保持不变。同样你也可以调用deleteAll方法然后rollback回来。
注意仅在一个新的目录上打开一个IndexWriter并不会产生一个空的commit操作即你无法在这个目录上打开一个IndexReader直到你完成一次commit。
Lucene自身并未实现事务日志但在更高层上可以方便实地现。例发Solr与ElasticSearch就实现了事务日志。
同一索引的多Commit
单个lucene索引可以保存多个commit这是一个强大的特性但往往容易被人们所忽视。每次commit都持有该commit被创建的时间点的过引索视图。 这个特点类似于ZFS和新兴的Btrfs这样的现代文件系统的快照和写克隆功能。事实上Lucene是能够暴露多次commit状态其内在的原理是所有的索引分片和文件只会被写一次正如ZFS和Btrfs上的块。
为了保存多次commit到你的索引中只需要实现你自己的IndexDeletionPolicy并将其传递给IndexWriter。Lucene正是通过这个类才知站定哪些commit是要被删除的IndexWriter会在打开索引和任何一次完成commit的时候调用它。KeepOnlyLastCommitDeletionPolicy是默认的删除机制实现它会删除掉除了最近一次commit以外的所有commit。如果采用NoDeletionPolicy那么每一次commit都会保存。
你可以在commit的时候传送userData (MapString,String)用于记录关于本次commit的一些用户自定义信息对Lucene不透明然后使用IndexReader.listCommits方法获得索引的所有commit信息。一旦你找到了一次commit你可以在其上找开一个IndexReader对commit点上的索引数据进行搜索。
你也可以基于之前的commit打开一个IndexWriter回滚之后的所有变动。这有点类似于rollback方法不同之处在于它允许你在多个commit间rollback而不仅是回滚当前IndexWriter会话中所做的变动。
当你使用OpenMode.CREATE参数打开一个索引的时候老的commit仍会保存。你也可使用OpenMode.CREATE同时还在老的commit上用IndexReader进行搜索。这使得一些有趣的应用情影成为可能例如你可以在不影响当前任何打开的reader前提下在各个commit间做过索引全量重建工作。
组合上这些有意思的事务特征你可以完成一些很酷的工作 利用SnapShotDeletionPolicy或者PersistentSnapshotDeletionPolicy实现热备 these deletion policies make it trivial to take a live backup of the index without blocking ongoing changes with IndexWriter. 备份可以轻易的实现增量只要复制新的文件并去掉删除文件,而你可以减少IO消耗以减轻对于搜索的干扰。 搜索不同版本的目录 也许你在运行一个商业网站却要辗转于不同版本的目录之间。在这种情况下你就可以保存更老的commit允许用户选择搜索哪个版本的目录。 在同一个初始索引上进行多次索引实验也许你想要在同一个大的索引上运行一系列的性能测试实验例如尝试不的RAM缓存大小或者merge因子。要想如此你可以在运行完每次测试之后不要关闭IndexWriter而使用回滚方法快迅地恢得到初始状态以备下次测试。 强制merge所有的片段到单一片段中但依然保存之前的多片段时候的commit。如此你就可以做多个段vs单一段的性能比较实验。 在NFS文件系统上做索引与搜索 由于NFS无法保证当前打开的文件不被删除你必须使用IndexDeletionPolicy来保存每次提交直至所有的reader都不再使用该commit即重新在一个更新的commit上打开。一种简单的解决方案是基本于时间的例如不删除该commit直到创建该commit15分钟之后然后每5分钟重新打开reader一次。不然你在NFS上进行搜索时将会遇到各种可怕的异常。 分布式commit: 如果你有其他的资源需要有lucene索引发生变化的同时提交你就可以使用二阶段提交API。这样的做法很简单但容易在第二阶段发生失败 例如lucene在其第二阶段完成了提交而数据库在第二阶段中发生了一些错误、奔溃或者是断电这时你就可以通过在一个较早的commit上重新打开一个IndexWriter来rollback Lucene的这次commit。 进行实验性的索引更新也许你只想对索引的一个子集做一次re-indexing操作但你又不确定这样的操作是否会成功。这种情况下只要保存老的commit如果操作失败就执行rollback,如果成功则删除老的commit。 基于时间的快照也许你想能够更自由地回滚到1天前一周前一个月之前...的索引状态就保可以根据这些时间点保存这些commit。
注意保存多个版本的commit必然会带来更多的磁盘空间消耗。然而这些消耗往往会比较小因为多个commit往往会共享一些索引片段尤其是那些更大更早的片段。
二、 解
对于Lucene事务的原子性、隔离性以及近时实(NRT)搜索
上文在原子性的描述中提到“要么你的所有的操作修改都是可见的commit成功要么所有的操作修改都不可见commit失败绝不会处于某种中间状态。” 但没有提到通过Indexwriter.getReader获得的Reader是能看到上次commit之后IndexWriter执行到当前的所有变化的。 When you ask for the IndexReader from the IndexWriter, the IndexWriter will be flushed (docs accumulated in RAM will be written to disk) but not committed (fsync files, write new segments file, etc).The returned IndexReader will search over previously committed segments, as well as the new, flushed but not committed segment. Because flushing will likely be processor rather than IO bound, this should be a process that can be attacked with more processor power if found to be too slow. 还有人们常使用openIfChanged方法来实现近实时搜索。我们来看看Lucene DirectoryReader的标准实现StandardDirectoryReader中的oldOpenIfChanged方法的实现。可以发现所谓的doOpenIfChanged内部也是靠优先尝试从IndexWriter获得DirectoryReader来实现的。 Overrideprotected DirectoryReader doOpenIfChanged() throws IOException {return doOpenIfChanged((IndexCommit) null);}Overrideprotected DirectoryReader doOpenIfChanged(final IndexCommit commit) throws IOException {ensureOpen();// If we were obtained by writer.getReader(), re-ask the// writer to get a new reader.if (writer ! null) {return doOpenFromWriter(commit);} else {return doOpenNoWriter(commit);}}现在抛开近实时搜索不使用该新特性那么上文中作者提到的原子性特征是完备的。在不考虑merge的情况下Lucene的每一次commit将内存中积累的索引变更写入到硬盘形成新的索引分段包括文档删除操作。这样的好处在于每一次commit不用去复杂又耗时地修改之前的索引分段只要累加新文件即可。但这样的后果是越来越多的分片存在影响查询效率所以才需要merge机制的存在不断地去合并分段这是另话了。
上文中所提的隔离性就是基于这种机制实现的非NRT的正常reader打开时只能获得当前已commit到磁盘的分段文件信息的索引数据无法得到两次commit的中间状态。 而NRT的reader可以获得打开时IndexWriter所应用的所有变更但也无法感知到之后的IndexWriter所做的索引变化除非重新打开。
至于“一次只能有一个IndexWriter实例对索引进行更改”这是靠Lucene实现的排它锁实现的你将会在你的索引目录下看到write.lock文件的存在。
Lucene commit
Lucene多个commit的保存与删除到底是怎么回事?如何基于commit打开索引? 这一系列的问题还是得需要通过了解lucene如何保存每次commit才能理解。
我们首先看看一个IndexCommit的子类一般都包含哪些信息。一个IndexCommit实例就代表了一次索引commit。对索引内容的任何变更只有在segments_N文件完成写入之后才可见。
public abstract class IndexCommit implements ComparableIndexCommit {/*** Get the segments file (codesegments_N/code) associated * with this commit point.*/public abstract String getSegmentsFileName();/*** Returns all index files referenced by this commit point.*/public abstract CollectionString getFileNames() throws IOException;/*** Returns the {link Directory} for the index.*/public abstract Directory getDirectory();/*** Delete this commit point. This only applies when using* the commit point in the context of IndexWriters* IndexDeletionPolicy.*/public abstract void delete();...
}两个重要的方法getSegmentsFileName与getFileNames。segmentsFileName是与当前commit关联的segments_N文件名。N是一个递增的值每次commit对应的N都会增大。所以如果保存多个commit的话自然会出现多个segments_N文件。segments_N具体格式内容在此不作具体解释总体它记录了lucene索引的所有分段的元数据信息根据segments_N文件可以清楚地知道当前commit的索引内容分布于哪些分段之中。getFileNames返回的是当前commit所引用的所有文件。 如此每次某个commit上打开的IndexReader或IndexWriter就知道应该去加载哪些文件。也正因如此多个commit能够共享一些老的索引分段而不至于每个commit占用太大的存储空间。当涉及到commit删除时由于lucene对索引文件的删除是通过引用计数的方式实现的只要对commit引用的文件调用一次IndexFileDeleter.decRef(Collection files)方法即可。只有引用计数为0的文件才会真正地被删除。
Luncene IndexDeletionPolicy
IndexDeletionPolicy它所能做的是在两个方法时间结点上对IndexCommit做删除。定义如下
public abstract class IndexDeletionPolicy {protected IndexDeletionPolicy() {}public abstract void onInit(List? extends IndexCommit commits) throws IOException;public abstract void onCommit(List? extends IndexCommit commits) throws IOException;
}onInit方法只在IndexWriter初始化时被调用onCommit在每次commit操作的时候被调用。commits列表中包含了当前所有的commit点按从老到新的顺序排列。 默认实现KeepOnlyLastCommitDeletionPolicy删除上一次commit以外的所有commit相关文件减少引用计数。 SnapshotDeletionPolicy是采用wrapper模式对现有IndexDeletionPolicy的一层封装。除了onInit和onCommit方法外它还提供了snapshot和release两个方法。snapshot得到的IndexCommit将不会被删除直到其被release所以比较适用于备份的场影在备份之前调用snapshot直到备份完成再调用release。 SnapshotDeletionPolicy只在内存中保存snapShot信息如果要保证数据持久化不丢失可使用PersistentSnapshotDeletionPolicy。
Lucene二阶段提交的实现
IndexWirter继承了TwoPhaseCommit接口实现三个方法prepareCommitcommit与rollback。
prepareCommit完成二阶段提交第一阶段的工作它会尽可能多的完成更新工作但又避免完成真实的提交。你可以轻松地利用rollback废弃掉当前阶段完成的所有工作。 事实上本次commit所产生的段文件已写入存储。commit方法是完成第二阶段的工作它只作很少的工作只有该方满返回调用者才能确认索引相应操作已完成并持久化到存储。跟踪代码直至SegmentIfnos的finishCommit方法可见commit成功的情况下只做了两件事情一是在segments_N未填入4byte的校验合还有就是close写入流完成fsync。新的commit,只有在校验和正确的情况下对IndexReader可见。final String finishCommit(Directory dir) throws IOException {if (pendingSegnOutput null) {throw new IllegalStateException(prepareCommit was not called);}boolean success false;final String dest;try {CodecUtil.writeFooter(pendingSegnOutput);success true;} finally {if (!success) {// Closes pendingSegnOutput deletes partial segments_N:rollbackCommit(dir);} else {success false;try {pendingSegnOutput.close();success true;} finally {if (!success) {// Closes pendingSegnOutput deletes partial segments_N:rollbackCommit(dir);} else {pendingSegnOutput null;}}}}rollback:废弃掉上次commit以来的所有变更操作。