最近有个项目要用solr,solr是基于lucene的,今天在测试indexwriter时遇到了lock的问题:

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import  java.io.File;
import  java.io.IOException;
import  org.apache.lucene.analysis.Analyzer;
import  org.apache.lucene.analysis.standard.StandardAnalyzer;
import  org.apache.lucene.index.IndexWriter;
import  org.apache.lucene.index.IndexWriterConfig;
import  org.apache.lucene.store.Directory;
import  org.apache.lucene.store.FSDirectory;
import  org.apache.lucene.util.Version;
public  class  TestLock {
     private  Directory dir;
     public  static  TestLock ttt;
     public  static  IndexWriter writer2;
     private  Analyzer analyzer =  new  StandardAnalyzer(Version.LUCENE_48);
     private  IndexWriterConfig iwc =  new  IndexWriterConfig(Version.LUCENE_48, analyzer);
     public  void  init()  throws  Exception {
         String pathFile =  "D://luceneindex" ;
         try {
                dir = FSDirectory. open( new  File(pathFile));
         catch  (IOException e) {        
             throw  new  RuntimeException(e);
         }
         IndexWriter writer = getWriter();             
         System. out.println( "init ok,test IndexWriter lock"  );
         LockTest( writer);      
         //writer.close();
     }
     public  IndexWriter getWriter()  throws  Exception {
         //analyzer.close();
         return  new  IndexWriter(dir, iwc);
     }
     public  void  LockTest(IndexWriter w1) {
        try {
                if (w1.isLocked (dir )){
                      System. out.println( "write1 locked"  );
                      IndexWriterConfig iwc1 =  new  IndexWriterConfig(Version.LUCENE_48 , analyzer );
                       writer2 =  new  IndexWriter(dir, iwc1); 
               else {
                      System. out.println( "write1 not locked"  );                      
               }      
        catch (Exception e){
               e.printStackTrace();
        }       
     }  
     public  static  void  main(String[] args){
        ttt =  new  TestLock();
        try {
                ttt.init();
        catch (Exception e){
               e. printStackTrace();              
        }      
     }
}

报错信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
org.apache.lucene.store.LockObtainFailedException : Lock obtain timed out: NativeFSLock @D :\luceneindex\write.lock: java.nio.channels.OverlappingFileLockException
        at org.apache.lucene.store.Lock.obtain( Lock.java: 89 )
        at org.apache.lucene.index.IndexWriter.<init>( IndexWriter.java: 710 )
        at TestLock.LockTest( TestLock.java: 38 )
        at TestLock.init( TestLock.java: 26 )
        at TestLock.main( TestLock.java: 51 )
Caused by: java.nio.channels.OverlappingFileLockException
        at sun.nio.ch.SharedFileLockTable.checkList(Unknown Source)
        at sun.nio.ch.SharedFileLockTable.add(Unknown Source)
        at sun.nio.ch.FileChannelImpl.tryLock(Unknown Source)
        at java.nio.channels.FileChannel.tryLock(Unknown Source)
        at org.apache.lucene.store.NativeFSLock.obtain(NativeFSLockFactory.java: 148 )
        at org.apache.lucene.store.Lock.obtain( Lock.java: 100 )
        ...  4  more

从错误信息可以看到是IndexWriter获取锁出错导致,从堆栈信息可以看到,是在运行IndexWriter的构造方法时,涉及到锁的操作。

查看IndexWriter的相关源码:

1
2
3
4
doc:
Opening an IndexWriter creates a lock  file  for  the directory  in  use. Trying to  open  another IndexWriter  on the same directory will lead to a
LockObtainFailedException. The  LockObtainFailedException is also thrown  if  an IndexReader on the same directory is used to delete documents
from the index.

两个和锁相关的属性:

1
2
   public  static  final  String WRITE_LOCK_NAME =  "write.lock"  ;
   private  Lock writeLock;

在IndexWriter的构造函数中:

1
2
3
4
5
6
7
   public  IndexWriter(Directory d, IndexWriterConfig conf)  throws  IOException
....
      directory = d;
.....
     writeLock = directory.makeLock(WRITE_LOCK_NAME);
     if  (! writeLock.obtain(config .getWriteLockTimeout()))  // obtain write lock(尝试获取锁)
       throw  new  LockObtainFailedException( "Index locked for write: "  + writeLock );

其中Lock是一个抽象类(org.apache.lucene.store.Lock):   

锁的获取主要实现是在obtain方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   public  static  long  LOCK_POLL_INTERVAL =  1000 ;
   public  static  final  long  LOCK_OBTAIN_WAIT_FOREVER = - 1 ;
   public  final  boolean  obtain( long  lockWaitTimeout)  throws  IOException {
     failureReason =  null ;
     boolean  locked = obtain();
     if  (lockWaitTimeout <  0  && lockWaitTimeout != LOCK_OBTAIN_WAIT_FOREVER )
       throw  new  IllegalArgumentException( "lockWaitTimeout should be LOCK_OBTAIN_WAIT_FOREVER or a non-negative number (got "  + lockWaitTimeout +  ")" );
     long  maxSleepCount = lockWaitTimeout / LOCK_POLL_INTERVAL;
     long  sleepCount =  0 ;
     while  (!locked) {
       if  (lockWaitTimeout != LOCK_OBTAIN_WAIT_FOREVER && sleepCount++ >= maxSleepCount) {
         String reason =  "Lock obtain timed out: "  this  .toString();
         if  (failureReason !=  null ) {
           reason +=  ": "  + failureReason ;
         }
         LockObtainFailedException e =  new  LockObtainFailedException(reason);
         if  (failureReason !=  null ) {
           e.initCause( failureReason);
         }
         throw  e;
       }
       try  {
         Thread. sleep(LOCK_POLL_INTERVAL);
       catch  (InterruptedException ie) {
         throw  new  ThreadInterruptedException(ie);
       }
       locked = obtain();
     }
     return  locked;
   }

可以看到有由下面几个因素决定:

1.lockWaitTimeout 超时时间(总时间,超过这个时间即timeout),默认1000ms

2.LOCK_OBTAIN_WAIT_FOREVER  是否无限获取(-1),如果设置为-1,会永不超生

3.LOCK_POLL_INTERVAL  重试间隔时间,默认1000ms

在关闭IndexWriter时调用close方法(实现了Closeable接口的类都会有close方法)即可正常释放锁

更改代码为如下即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
   public  void  LockTest(IndexWriter w1) {
        try {
                if (w1.isLocked (dir )){
                      System. out.println( "write1 locked"  );
                      w1.close();
                      IndexWriterConfig iwc1 =  new  IndexWriterConfig(Version.LUCENE_48 , analyzer );
                       writer2 =  new  IndexWriter(dir, iwc1); 
               else {
                      System. out.println( "write1 not locked"  );                      
               }      
        catch (Exception e){
               e.printStackTrace();
        }       
     }

另外关于lock的判断和unlock方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
  public  static  boolean  isLocked(Directory directory)  throws  IOException {   // 判断写目录是否被lock
     return  directory.makeLock( WRITE_LOCK_NAME).isLocked();
   }
   /**
    * Forcibly unlocks the index in the named directory.
    * <P>
    * Caution: this should only be used by failure recovery code,
    * when it is known that no other process nor thread is in fact
    * currently accessing this index.
    */
   public  static  void  unlock(Directory directory)  throws  IOException {  // 解锁
     directory.makeLock(IndexWriter. WRITE_LOCK_NAME).close();
   }