Lucene -- LockFactory&Lock

Lock的作用是防止一个Lucene索引,同一时刻被多个IndexWriter进行写操作;如果一个Lucene索引同时被多个IndexWriter进行写操作,可能造成索引损坏。在一个Lucene索引被锁住后,Lucene索引文件夹内一定有一个write.lock文件,反之,则不一定。

NativeFSLockFactory

IndexWriter 默认使用 NativeFSLockFactory;NativeFSLockFactory锁是通过 java.nio.* APIs实现的,所以如果这个API有什么问题,那么锁也将会失败。另外,在NFS存储环境中NativeFSLockFactory,也将会锁失败,因为可以多次获取锁,也就可以有多个IndexWriter,会造成索引损坏。在NFS环境推荐使用SimpleFSLockFactory;NativeFSLockFactory的主要好处是,如果JVM出现异常退出,操作系统会删除锁,而不是锁定文件,虽然write.lock依然存在;正常退出的情况下,write.lock也不会被删除,只是Lucene会释放write.lock文件的引用。

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);
    IOException creationException = null;
    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.
      creationException = ignore;
    }
    // fails if the lock file does not exist
    final Path realPath;
    try {
      realPath = lockFile.toRealPath();
    } catch (IOException e) {
      // if we couldn't resolve the lock file, it might be because we couldn't create it.
      // so append any exception from createFile as a suppressed exception, in case its useful
      if (creationException != null) {
        e.addSuppressed(creationException);
      }
      throw e;
    }
    // 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);
    }
  }

NativeFSLock

@Override
    public synchronized void close() throws IOException {
      if (closed) {
        return;
      }
      // NOTE: we don't validate, as unlike SimpleFSLockFactory, we can't break others locks
      // first release the lock, then the channel
      try (FileChannel channel = this.channel;
           FileLock lock = this.lock) {
        assert lock != null;
        assert channel != null;
      } finally {
        closed = true;
        clearLockHeld(path);
      }
    }

SimpleFSLockFactory

使用此API进行锁定的主要缺点是,当JVM异常退出时,可能无法释放锁。在尝试创建IndexWrite时会触发LockObtainFailedException,在这种情况下,需要手动删除文件来显式清除锁文件。前提是要确保没有其他IndexWriter在写入索引,否则很容易破坏你的Lucene索引。正常退出的情况下,会删除write.lock文件。

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);
    // create the file: this will fail if it already exists
    try {
      Files.createFile(lockFile);
    } catch (FileAlreadyExistsException | AccessDeniedException e) {
      // convert optional specific exception to our optional specific exception
      throw new LockObtainFailedException("Lock held elsewhere: " + lockFile, e);
    }
    // used as a best-effort check, to see if the underlying file has changed
    final FileTime creationTime = Files.readAttributes(lockFile, BasicFileAttributes.class).creationTime();
    return new SimpleFSLock(lockFile, creationTime);
  }

SimpleFSLock

public synchronized void close() throws IOException {
      if (closed) {
        return;
      }
      try {
        // NOTE: unlike NativeFSLockFactory, we can potentially delete someone else's
        // lock if things have gone wrong. we do best-effort check (ensureValid) to
        // avoid doing this.
        try {
          ensureValid();
        } catch (Throwable exc) {
          // notify the user they may need to intervene.
          throw new LockReleaseFailedException("Lock file cannot be safely removed. Manual intervention is recommended.", exc);
        }
        // we did a best effort check, now try to remove the file. if something goes wrong,
        // we need to make it clear to the user that the directory may still remain locked.
        try {
          Files.delete(path);
        } catch (Throwable exc) {
          throw new LockReleaseFailedException("Unable to remove lock file. Manual intervention is recommended", exc);
        }
      } finally {
        closed = true;
      }
    }

SingleInstanceLockFactory

是ByteBuffersDirectory(内存索引)的默认实现,一个lock只能被一个IndexWriter使用;只在内存中存在,Lucene索引不管是以那种方式关闭,都不会影响下次锁的获取。

public final class SingleInstanceLockFactory extends LockFactory {

  final HashSet<String> locks = new HashSet<>();

  @Override
  public Lock obtainLock(Directory dir, String lockName) throws IOException {
    synchronized (locks) {
      if (locks.add(lockName)) {
        return new SingleInstanceLock(lockName);
      } else {
        throw new LockObtainFailedException("lock instance already obtained: (dir=" + dir + ", lockName=" + lockName + ")");
      }
    }
  }

  private class SingleInstanceLock extends Lock {
    private final String lockName;
    private volatile boolean closed;

    public SingleInstanceLock(String lockName) {
      this.lockName = lockName;
    }

    @Override
    public void ensureValid() throws IOException {
      if (closed) {
        throw new AlreadyClosedException("Lock instance already released: " + this);
      }
      // check we are still in the locks map (some debugger or something crazy didn't remove us)
      synchronized (locks) {
        if (!locks.contains(lockName)) {
          throw new AlreadyClosedException("Lock instance was invalidated from map: " + this);
        }
      }
    }

    @Override
    public synchronized void close() throws IOException {
      if (closed) {
        return;
      }
      try {
        synchronized (locks) {
          if (!locks.remove(lockName)) {
            throw new AlreadyClosedException("Lock was already released: " + this);
          }
        }
      } finally {
        closed = true;
      }
    }

    @Override
    public String toString() {
      return super.toString() + ": " + lockName;
    }
  }
}

如果更改锁实现,则需要特别小心:首先要确保没有IndexWriter在写入Lucene索引,否则很容易破坏索引。在第一次启动新配置之前,请务必执行LockFactory更改所有Lucene实例,并清除所有剩余的锁文件。不同的实现不能一起工作!

转载于:https://my.oschina.net/5icode/blog/2992082

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java Lucene-Core 是 Apache Lucene 项目的核心依赖库。Lucene 是一个开源的全文检索引擎工具包,提供了强大的全文检索功能,可用于构建各种基于文本的应用程序。 在使用 Lucene 时,需要添加 Lucene-Core 依赖到项目中,以便能够使用 Lucene 提供的各种功能。Lucene-Core 是 Lucene 项目最基本的依赖库,包含了一些必备的类和方法,用于索引和搜索文档。 通过 Lucene-Core,可以使用 Lucene 提供的各种 API 来创建索引、搜索和加权查询。Lucene 使用倒排索引的方式来快速定位包含搜索词的文档,而不需要遍历整个文档集合。这种索引结构使得 Lucene 具有出色的搜索效率和性能。 Lucene-Core 还提供了各种分析器(Analyzer)和查询解析器(Query Parser),用于处理文本的分词、词干处理和查询解析等操作。分析器可用于将文本分割成词语,并根据需要进行一些文本处理操作。查询解析器则用于将用户的查询语句解析成 Lucene 可以理解的查询对象。 除了 Lucene-Core,还存在其他的 Lucene 依赖库,如 Lucene-Analyzers、Lucene-Queries 等,它们提供了更高级的功能和扩展,用于处理多语言分词、模糊查询、范围查询等等。 总之,Java Lucene-Core 依赖是使用 Lucene 的必备库,它提供了构建全文检索应用程序所需的基本功能和工具。通过使用 Lucene-Core,开发人员可以更方便地利用 Lucene 的强大功能来实现高效的全文检索。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值