lucene中write.lock索引锁机制的原理

write.lock加锁的实现原理:

实现源码(lucene 6.0):

@Override
protected Lock obtainFSLock(FSDirectory dir, String lockName) throws IOException {
    Path lockDir = dir.getDirectory();
    // Ensure that lockDir exists and is a directory.
    // note: this will fail if lockDir is a symlink
    Files.createDirectories(lockDir);
    Path lockFile = lockDir.resolve(lockName);
    try {
        Files.createFile(lockFile);
    } catch (IOException ignore) {
        // we must create the file to have a truly canonical path.
        // if it's already created, we don't care. if it cant be created, it will fail below.
    }
    // fails if the lock file does not exist
    final Path realPath = lockFile.toRealPath();
    // used as a best-effort check, to see if the underlying file has changed
    final FileTime creationTime = Files.readAttributes(realPath, BasicFileAttributes.class).creationTime();
    if (LOCK_HELD.add(realPath.toString())) {
        FileChannel channel = null;
        FileLock lock = null;
        try {
            channel = FileChannel.open(realPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
            lock = channel.tryLock();
            if (lock != null) {
                return new NativeFSLock(lock, channel, realPath, creationTime);
            } else {
                throw new LockObtainFailedException("Lock held by another program: " + realPath);
            }
        } finally {
            if (lock == null) { // not successful - clear up and move out
                IOUtils.closeWhileHandlingException(channel); // TODO: addSuppressed
                clearLockHeld(realPath);  // clear LOCK_HELD last 
            }
        }
    } else {
        throw new LockObtainFailedException("Lock held by this virtual machine: " + realPath);
    }
}

首先会递归的创建多层目录,然后创建write.lock文件,如果已经存在抛出java.nio.file.FileAlreadyExistsException异常,捕获该异常之后不做任何处理,因为一旦索引创建之后,write.lock文件会一直存在,即使IndexWriter已经关闭,所以不能以该文件是否存在判断是否有多个IndexWriter被打开了,那么是根据什么来判断的呢?

接着会获取该文件创建时候的时间戳,并且将该文件的真实路径加入LOCK_HED, LOCK_HELD的声明如下,是一个同步的HashSet集合,在第一次打开IndexWriter的时候LOCK_HELD.add(realPath.toString())成功,然后调用FIleChannel的API去h获取锁,注意这里面重点就是channel.trylock()获取到的是FIleLock,这是系统级别的锁,即使其它进程想打开IndexWriter的时候,它虽然能够LOCK_HELD.add(realPath.toString())成功,但是在channel.tryLock()步会加锁失败,得到null,这时候程序依然会抛LockObtainFailedException异常;对于多JVM去同时写同一份索引的情况同样如此,如果在同一个进程内,后面希望打开另外的IndexWriter的时候必然LOCK_HELD.add(realPath.toString())失败,抛LockObtainFailedException异常所以如果程序或JVM崩溃,LOCK_HELD在内存中必然也失效,系统级别的锁(使用tryLock获取的文件锁)也会释放,相当于是自动解锁了,不影响下次的重新加锁操作。

补充:FileChannel中的lock()与tryLock()方法都是尝试去获取在某一文件上的独有锁(以下简称独有锁),可以实现进程间操作的互斥。区别在于lock()会阻塞(blocking)方法的执行,tryLock()则不会。

如果进程在执行lock()或tryLock()后获取到独有锁(return a FileLock object),那么进程会一直持有该锁到被释放(文件流被关闭 或 调用release() )。

如果进程P(A)持有独有锁:

1、进程P(B)执行lock()获取独有锁,则lock()所在方法会一直阻塞,直到独有锁被进程P(A)释放。

2、进程P(B)执行tryLock()获取独有锁,则tryLock()会抛出异常java.io.IOException: fcntl failed: EAGAIN (Try again)异常

 

释放锁源码:

@Override
public void close() throws IOException {
  if (config.getCommitOnClose()) {
    shutdown();
  } else {
    rollback();
  }

在shutdown中,Lucene会先判断一系列预先设置的参数,然后进行刷新操作,将所有在内存中缓存的更新刷新到Directory中,然后静静等待合并结束,合并之后会进行内部的提交操作

转载:

lucene源码解析:http://codepub.cn/2016/11/23/Lucene-index-file-lock-principle/

FIleChannel介绍:https://blog.csdn.net/leunging/article/details/73911927

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值