最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Lucene?索引刪除策略源碼解析

 更新時間:2023年03月14日 10:46:22   作者:滄叔解碼  
這篇文章主要為大家介紹了Lucene?索引刪除策略源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

Lucene

從今天開始,我們要開始介紹Lucene中索引構建的流程。因為索引構建的邏輯涉及到的東西非常多,如果從構建入口IndexWriter來開始介紹,是很難說清楚的。所以接下來按化零為整的方式 ,從構建相關的各個組件開始介紹,盡量每一篇文章都是可以獨立閱讀,依賴的前置知識都是我已經介紹的內容。不管就算如此,還是會有部分內容可能需要結合整體流程才能明白,對于這部分的內容,大家可以先留個印象,以后介紹相關聯(lián)的內容時,我會再重新指出。

今天我們一起來看看索引文件刪除相關的。

IndexCommit

Lucene中,需要持久化的索引信息都要進行commit操作,然后會生成一個segments_N的索引文件記錄此次commit相關的索引信息。

一次commit生成segments_N之后,就對應了一個IndexCommit,IndexCommit只是一個接口,它定義了可以從IndexCommit中獲取哪些信息:

public abstract class IndexCommit implements Comparable<IndexCommit> {
  // commit對應的segments_N
  public abstract String getSegmentsFileName();
  // commit關聯(lián)的所有的索引文件
  public abstract Collection<String> getFileNames() throws IOException;
  // 索引所在的Directory
  public abstract Directory getDirectory();
  // 刪除commit,后面會看到,刪除其實減少commit關聯(lián)的索引文件的引用計數(shù)
  public abstract void delete();
  // commit是否被刪除了
  public abstract boolean isDeleted();
  // commit關聯(lián)了幾個segment
  public abstract int getSegmentCount();
  // segments_N文件中的N
  public abstract long getGeneration();
  // commit可以記錄一些用戶自定義的信息
  public abstract Map<String, String> getUserData() throws IOException;
  // 用來讀取commit對應的索引數(shù)據(jù)
  StandardDirectoryReader getReader() {
    return null;
  }
}

IndexCommit有三個實現(xiàn)類:

  • CommitPoint
  • ReaderCommit
  • SnapshotCommitPoint

這個三個實現(xiàn)類都有對應的使用場景,在用到的時候我會再詳細介紹,本文中會涉及到SnapshotCommitPoint,后面會詳細介紹它。

IndexDeletionPolicy

在索引的生命周期中,可以有多次的commit操作,因此也會生成多個segments_N文件,對于這些文件是否要保留還是刪除,lucene中是通過IndexDeletionPolicy來管理的。我們先來看下IndexDeletionPolicy的接口定義:

public abstract class IndexDeletionPolicy {
  protected IndexDeletionPolicy() {}
  // 重新打開索引的時候,對所有commit的處理
  public abstract void onInit(List<? extends IndexCommit> commits) throws IOException;
  // 有新提交時對所有commit的處理
  public abstract void onCommit(List<? extends IndexCommit> commits) throws IOException;
}

從上面我可以看到,索引的刪除策略其實只在兩個地方進行應用,一個是加載索引的時候,打開一個舊索引時,根據(jù)當前設置的IndexDeletionPolicy進行處理。另一個是有新的commit產生時,借這個機會處理所有的commit。Lucene中提供的索引刪除策略一共有四種,不過可以分為三類:

NoDeletionPolicy

NoDeletionPolicy索引刪除策略就是保留所有的commit信息,效果就是你有多少次commit就多少個segments_N文件,看個例子:

public class DeletionPolicyTest {
    private static final Random RANDOM = new Random();
    public static void main(String[] args) throws IOException {
        Directory directory = FSDirectory.open(new File("D:\\code\\lucene-9.1.0-learning\\data").toPath());
        WhitespaceAnalyzer analyzer = new WhitespaceAnalyzer();
        IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);
        indexWriterConfig.setUseCompoundFile(true);
        indexWriterConfig.setIndexDeletionPolicy(NoDeletionPolicy.INSTANCE);
        IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig);
        indexWriter.addDocument(getDoc(RANDOM.nextInt(10000),RANDOM.nextInt(10000)));
        // 第一次commit,生成segments_1
        indexWriter.commit();
        indexWriter.addDocument(getDoc(RANDOM.nextInt(10000),RANDOM.nextInt(10000)));
        // 第二次commit,生成segments_2
        indexWriter.commit();
        indexWriter.close();
    }
    private static Document getDoc(int... point) {
        Document doc = new Document();
        IntPoint intPoint = new IntPoint("point", point);
        doc.add(intPoint);
        return doc;
    }
}

上面的例子中有兩次commit,下圖是NoDeletionPolicy策略進行了兩次commit的索引目錄結構,可以看到生成了兩個segments_N文件:

NoDeletionPolicy的代碼實現(xiàn)非常簡單,單例實現(xiàn),并且在onCommit和onInit的時候都是空操作:

public final class NoDeletionPolicy extends IndexDeletionPolicy {
  public static final IndexDeletionPolicy INSTANCE = new NoDeletionPolicy();
  private NoDeletionPolicy() {
  }
  public void onCommit(List<? extends IndexCommit> commits) {}
  public void onInit(List<? extends IndexCommit> commits) {}
}

KeepOnlyLastCommitDeletionPolicy

KeepOnlyLastCommitDeletionPolicy是Lucene默認的索引刪除策略,只保留最新的一次commit,從索引目錄看不管執(zhí)行多少次commit只保留了N最大的segments_N文件,下圖是KeepOnlyLastCommitDeletionPolicy策略進行了兩次commit的結果,KeepOnlyLastCommitDeletionPolicy刪除策略只保留了segments_2。把上面示例代碼中的刪除策略替換成KeepOnlyLastCommitDeletionPolicy,即可得到,注意需要先清空索引目錄:

KeepOnlyLastCommitDeletionPolicy代碼實現(xiàn)也比較簡單,除了最后一個commit之外,其他的commit都刪除:

public final class KeepOnlyLastCommitDeletionPolicy extends IndexDeletionPolicy {
  public KeepOnlyLastCommitDeletionPolicy() {}
  public void onInit(List<? extends IndexCommit> commits) {
    onCommit(commits);
  }
  // commits是從舊到新排序的
  public void onCommit(List<? extends IndexCommit> commits) {
    // 只保留最新的一個
    int size = commits.size();
    for (int i = 0; i < size - 1; i++) {
      commits.get(i).delete();
    }
  }
}

兩個快照相關的刪除策略

快照相關的刪除策略有兩個,SnapshotDeletionPolicy和PersistentSnapshotDeletionPolicy,分別對應了不可持久化和可持久化的模式。不管是SnapshotDeletionPolicy還是PersistentSnapshotDeletionPolicy,他們都封裝了其他的IndexDeletionPolicy來執(zhí)行刪除策略,他們只是提供了為當前最新的commit生成快照的能力。只要快照存在,則跟快照相關的所有索引文件都會被無條件保留。

SnapshotDeletionPolicy

例子

public class SnapshotDeletionPolicyTest {
    private static final Random RANDOM = new Random();
    public static void main(String[] args) throws IOException, InterruptedException {
        Directory directory = FSDirectory.open(new File("D:\\code\\lucene-9.1.0-learning\\data").toPath());
        WhitespaceAnalyzer analyzer = new WhitespaceAnalyzer();
        IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);
        indexWriterConfig.setUseCompoundFile(true);
        SnapshotDeletionPolicy snapshotDeletionPolicy = new SnapshotDeletionPolicy(new KeepOnlyLastCommitDeletionPolicy());
        indexWriterConfig.setIndexDeletionPolicy(snapshotDeletionPolicy);
        IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig);
        indexWriter.addDocument(getDoc(RANDOM.nextInt(10000),RANDOM.nextInt(10000)));
        // 第一次commit,生成segments_1
        indexWriter.commit();
        indexWriter.addDocument(getDoc(RANDOM.nextInt(10000),RANDOM.nextInt(10000)));
        // 第二次commit,生成segments_2
        indexWriter.commit();
        // segments_2當做快照,無條件保留
        snapshotDeletionPolicy.snapshot();
        indexWriter.addDocument(getDoc(RANDOM.nextInt(10000),RANDOM.nextInt(10000)));
        // 第三次commit,生成segments_3
        indexWriter.commit();
        indexWriter.close();
    }
    private static Document getDoc(int... point) {
        Document doc = new Document();
        IntPoint intPoint = new IntPoint("point", point);
        doc.add(intPoint);
        return doc;
    }
}

在上面的例子中,我們使用SnapshotDeletionPolicy,SnapshotDeletionPolicy底層封裝的是KeepOnlyLastCommitDeletionPolicy,我們進行了三次commit,理論上KeepOnlyLastCommitDeletionPolicy只會保留最后一次,但是因為我們對第一次的commit進行了快照,所以第一次commit也被保留了:

接下來我們看看SnapshotDeletionPolicy是怎么實現(xiàn)。SnapshotDeletionPolicy保證生成快照的commit不會被刪除的原理就是引用計數(shù),SnapshotDeletionPolicy會記錄每個commit生成快照的次數(shù),在刪除的時候,只會刪除引用計數(shù)為0的commit。

成員變量

  // key是IndexCommit的generation,value是對應的IndexCommit有多少個快照
  // 需要注意的是,有被快照引用的才會記錄在refCounts中,也就是只要被記錄在refCounts中,引用次數(shù)至少是1
  protected final Map<Long, Integer> refCounts = new HashMap<>();
  // key是IndexCommit的generation,value是對應的IndexCommit
  protected final Map<Long, IndexCommit> indexCommits = new HashMap<>();
  // SnapshotDeletionPolicy只是增加了支持快照的功能,刪除的邏輯是由primary參數(shù)對應的刪除策略提供的
  private final IndexDeletionPolicy primary;
  // 最近一次提交的commit,只會對這個IndexCommit生成快照
  protected IndexCommit lastCommit;
  // 是否初始化的標記,實例化后,必須先調用onInit方法
  private boolean initCalled;

生成快照

生成快照只會對當前最新的一個commit進行快照:

  public synchronized IndexCommit snapshot() throws IOException {
    if (!initCalled) {
      throw new IllegalStateException(
          "this instance is not being used by IndexWriter; be sure to use the instance returned from writer.getConfig().getIndexDeletionPolicy()");
    }
    if (lastCommit == null) {
      throw new IllegalStateException("No index commit to snapshot");
    }
    // 新增lastCommit的引用計數(shù)
    incRef(lastCommit);
    return lastCommit;
  }
  protected synchronized void incRef(IndexCommit ic) {
    long gen = ic.getGeneration();
    Integer refCount = refCounts.get(gen);
    int refCountInt;
    if (refCount == null) { // 第一次被引用
      indexCommits.put(gen, lastCommit);
      refCountInt = 0;
    } else {
      refCountInt = refCount.intValue();
    }
    // 引用計數(shù)加+1  
    refCounts.put(gen, refCountInt + 1);
  }

釋放指定的快照

public synchronized void release(IndexCommit commit) throws IOException {
  long gen = commit.getGeneration();
  releaseGen(gen);
}
protected void releaseGen(long gen) throws IOException {
  if (!initCalled) {
    throw new IllegalStateException(
        "this instance is not being used by IndexWriter; be sure to use the instance returned from writer.getConfig().getIndexDeletionPolicy()");
  }
  Integer refCount = refCounts.get(gen);
  if (refCount == null) {
    throw new IllegalArgumentException("commit gen=" + gen + " is not currently snapshotted");
  }
  int refCountInt = refCount.intValue();
  assert refCountInt > 0;
  refCountInt--;
  if (refCountInt == 0) { // 引用計數(shù)為0,直接從refCounts中移除
    refCounts.remove(gen);
    indexCommits.remove(gen);
  } else {
    refCounts.put(gen, refCountInt);
  }
}

刪除commit

  public synchronized void onCommit(List<? extends IndexCommit> commits) throws IOException {
    // 把commits中的所有IndexCommit都封裝成SnapshotCommitPoint,再使用primary執(zhí)行onCommit方法  
    primary.onCommit(wrapCommits(commits));
    // 更新最新的commit  
    lastCommit = commits.get(commits.size() - 1);
  }
  @Override
  public synchronized void onInit(List<? extends IndexCommit> commits) throws IOException {
    // 設置初始化的標記  
    initCalled = true;
    primary.onInit(wrapCommits(commits));
    for (IndexCommit commit : commits) { 
      if (refCounts.containsKey(commit.getGeneration())) {
        indexCommits.put(commit.getGeneration(), commit);
      }
    }
    if (!commits.isEmpty()) {
      lastCommit = commits.get(commits.size() - 1);
    }
  }
  private List<IndexCommit> wrapCommits(List<? extends IndexCommit> commits) {
    List<IndexCommit> wrappedCommits = new ArrayList<>(commits.size());
    for (IndexCommit ic : commits) {
      // 把IndexCommit都封裝成 SnapshotCommitPoint
      wrappedCommits.add(new SnapshotCommitPoint(ic));
    }
    return wrappedCommits;
  }

前面我們列出了SnapshotCommitPoint是IndexCommit的一個實現(xiàn)類,但是沒有詳細介紹,SnapshotCommitPoint除了能夠提供IndexCommit接口所提供的信息之外,最核心的是在刪除的時候,會先判斷IndexCommit是否被快照引用,只有沒有任何快照引用的IndexCommit才能刪除:

public void delete() {
    synchronized (SnapshotDeletionPolicy.this) {
        if (!refCounts.containsKey(cp.getGeneration())) {
            cp.delete();
        }
    }
}

存在的問題

需要注意的是SnapshotDeletionPolicy的快照信息是沒有持久化,我們重新打開SnapshotDeletionPolicyTest例子中生成的索引:

public class SnapshotDeletionPolicyTest2 {
    public static void main(String[] args) throws IOException, InterruptedException {
        Directory directory = FSDirectory.open(new File("D:\\code\\lucene-9.1.0-learning\\data").toPath());
        WhitespaceAnalyzer analyzer = new WhitespaceAnalyzer();
        IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);
        indexWriterConfig.setUseCompoundFile(true);
        SnapshotDeletionPolicy snapshotDeletionPolicy = new SnapshotDeletionPolicy(new KeepOnlyLastCommitDeletionPolicy());
        indexWriterConfig.setIndexDeletionPolicy(snapshotDeletionPolicy);
        // 重新打開索引
        IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig);
        indexWriter.close();
    }
}

可以發(fā)現(xiàn)segments_1被刪除了,因為沒有持久化快照信息,所以根據(jù)KeepOnlyLastCommitDeletionPolicy的刪除策略,只保留了最新的一個commit:

PersistentSnapshotDeletionPolicy

例子

PersistentSnapshotDeletionPolicy主要是為了解決SnapshotDeletionPolicy無法持久化的問題。PersistentSnapshotDeletionPolicy持久化的時候會生成snapshots_N的索引文件,我們看個例子:

public class PersistentSnapshotDeletionPolicyTest {
    private static final Random RANDOM = new Random();
    public static void main(String[] args) throws IOException, InterruptedException {
        Directory directory = FSDirectory.open(new File("D:\\code\\lucene-9.1.0-learning\\data").toPath());
        WhitespaceAnalyzer analyzer = new WhitespaceAnalyzer();
        IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);
        indexWriterConfig.setUseCompoundFile(true);
        PersistentSnapshotDeletionPolicy persistentSnapshotDeletionPolicy = new PersistentSnapshotDeletionPolicy(new KeepOnlyLastCommitDeletionPolicy(), directory);
        indexWriterConfig.setIndexDeletionPolicy(persistentSnapshotDeletionPolicy);
        IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig);
        indexWriter.addDocument(getDoc(RANDOM.nextInt(10000),RANDOM.nextInt(10000)));
        // 第一次commit,生成segments_1
        indexWriter.commit();
        // segments_1當做快照,無條件保留
        persistentSnapshotDeletionPolicy.snapshot();
        indexWriter.addDocument(getDoc(RANDOM.nextInt(10000),RANDOM.nextInt(10000)));
        // 第二次commit,生成segments_2
        indexWriter.commit();
        indexWriter.addDocument(getDoc(RANDOM.nextInt(10000),RANDOM.nextInt(10000)));
        // 第三次commit,生成segments_3
        indexWriter.commit();
        indexWriter.close();
    }
    private static Document getDoc(int... point) {
        Document doc = new Document();
        IntPoint intPoint = new IntPoint("point", point);
        doc.add(intPoint);
        return doc;
    }
}

上面的例子和我們在介紹SnapshotDeletionPolicy的時候邏輯一樣,只是把SnapshotDeletionPolicy換成了PersistentSnapshotDeletionPolicy,我們看結果:

從上面結果圖中可以看到,segments_1和segments_3同樣被保留了,但是多了一個持久化的快照信息的文件snapshots_0,有了這個文件,索引重新打開的時候就可以恢復快照信息,segments_1還是會被保留,用下面的例子我們重新打開索引,可以發(fā)現(xiàn)segments_1還是被保留了:

public class PersistentSnapshotDeletionPolicyTest2 {
    public static void main(String[] args) throws IOException, InterruptedException {
        Directory directory = FSDirectory.open(new File("D:\\code\\lucene-9.1.0-learning\\data").toPath());
        WhitespaceAnalyzer analyzer = new WhitespaceAnalyzer();
        IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);
        indexWriterConfig.setUseCompoundFile(true);
        PersistentSnapshotDeletionPolicy persistentSnapshotDeletionPolicy = new PersistentSnapshotDeletionPolicy(new KeepOnlyLastCommitDeletionPolicy(), directory);
        indexWriterConfig.setIndexDeletionPolicy(persistentSnapshotDeletionPolicy);
        IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig);
        indexWriter.close();
    }
}

接下來我們看看PersistentSnapshotDeletionPolicy的實現(xiàn),主要就是持久化和恢復快照信息的邏輯。

成員變量

  // 持久化快照信息的文件名snapshots_N中的N,從0開始
  private long nextWriteGen;
  // 持久化的文件所在的目錄
  private final Directory dir;

構造函數(shù)

  public PersistentSnapshotDeletionPolicy(IndexDeletionPolicy primary, Directory dir)
      throws IOException {
    this(primary, dir, OpenMode.CREATE_OR_APPEND);
  }
  public PersistentSnapshotDeletionPolicy(IndexDeletionPolicy primary, Directory dir, OpenMode mode)
      throws IOException {
    super(primary);
    this.dir = dir;
    if (mode == OpenMode.CREATE) { // 新建索引的模式,則需要清除所有的快照信息,索引模式以后再介紹
      clearPriorSnapshots();
    }
    // 加載快照信息
    loadPriorSnapshots();
    if (mode == OpenMode.APPEND && nextWriteGen == 0) {
      throw new IllegalStateException("no snapshots stored in this directory");
    }
  }

生成快照

public synchronized IndexCommit snapshot() throws IOException {
  // 使用SnapshotDeletionPolicy來生成快照  
  IndexCommit ic = super.snapshot();
  // 標記持久化是否成功,不成功的話需要刪除快照  
  boolean success = false;
  try {
    // 持久化最新的快照信息
    persist();
    success = true;
  } finally {
    if (!success) { // 持久化失敗,刪除快照
      try {
        super.release(ic);
      } catch (
          @SuppressWarnings("unused")
          Exception e) {
        // Suppress so we keep throwing original exception
      }
    }
  }
  return ic;
}

釋放快照

public synchronized void release(IndexCommit commit) throws IOException {
  // 使用SnapshotDeletionPolicy來釋放快照  
  super.release(commit);
  // 持久化快照信息是否成功  
  boolean success = false;
  try {
    // 持久化最新的快照信息  
    persist();
    success = true;
  } finally {
    if (!success) { // 持久化失敗,重新加回快照信息
      try {
        incRef(commit);
      } catch (
          @SuppressWarnings("unused")
          Exception e) {
        // Suppress so we keep throwing original exception
      }
    }
  }
}

持久化快照信息

private synchronized void persist() throws IOException {
  // 快照文件名  
  String fileName = SNAPSHOTS_PREFIX + nextWriteGen;
  boolean success = false;
  try (IndexOutput out = dir.createOutput(fileName, IOContext.DEFAULT)) {
    CodecUtil.writeHeader(out, CODEC_NAME, VERSION_CURRENT);
    out.writeVInt(refCounts.size());
    for (Entry<Long, Integer> ent : refCounts.entrySet()) { // 持久化所有的引用信息
      out.writeVLong(ent.getKey());
      out.writeVInt(ent.getValue());
    }
    success = true;
  } finally {
    if (!success) {
      IOUtils.deleteFilesIgnoringExceptions(dir, fileName);
    }
  }
  dir.sync(Collections.singletonList(fileName));
  if (nextWriteGen > 0) {
    String lastSaveFile = SNAPSHOTS_PREFIX + (nextWriteGen - 1);
    // 刪除前一個快照文件,因為每次持久化都是把當前的快照信息全量持久化,所以只需要保留最新的一個就可以
    // 這里有可能刪除失敗,所以在啟動加載的時候會再次嘗試把舊版本的文件都刪掉  
    IOUtils.deleteFilesIgnoringExceptions(dir, lastSaveFile);
  }
  nextWriteGen++;
}

加載快照信息

private synchronized void loadPriorSnapshots() throws IOException {
  long genLoaded = -1;
  IOException ioe = null;
  List<String> snapshotFiles = new ArrayList<>();
  for (String file : dir.listAll()) {
    if (file.startsWith(SNAPSHOTS_PREFIX)) { // 找到快照文件
      long gen = Long.parseLong(file.substring(SNAPSHOTS_PREFIX.length()));
      if (genLoaded == -1 || gen > genLoaded) { // 找到gen最大的快照文件
        snapshotFiles.add(file);
        Map<Long, Integer> m = new HashMap<>();
        IndexInput in = dir.openInput(file, IOContext.DEFAULT);
        try {
          CodecUtil.checkHeader(in, CODEC_NAME, VERSION_START, VERSION_START);
          int count = in.readVInt();
          for (int i = 0; i < count; i++) {
            long commitGen = in.readVLong();
            int refCount = in.readVInt();
            m.put(commitGen, refCount);
          }
        } catch (IOException ioe2) {
          // 保存第一個捕獲到的異常
          if (ioe == null) {
            ioe = ioe2;
          }
        } finally {
          in.close();
        }
        genLoaded = gen;
        // 清除舊數(shù)據(jù)  
        refCounts.clear();
        // 保留最新的  
        refCounts.putAll(m);
      }
    }
  }
  if (genLoaded == -1) { // 沒有加載快照文件
    if (ioe != null) { // 加載過程中捕獲到異常了,直接拋出
      throw ioe;
    }
  } else { // 把舊版本的快照文件都刪掉
    if (snapshotFiles.size() > 1) {
      String curFileName = SNAPSHOTS_PREFIX + genLoaded;
      for (String file : snapshotFiles) {
        if (!curFileName.equals(file)) {
          IOUtils.deleteFilesIgnoringExceptions(dir, file);
        }
      }
    }
    nextWriteGen = 1 + genLoaded;
  }
}

總結

本文介紹的索引刪除策略是在IndexCommit粒度的控制,具體到每個索引文件是怎么控制的,我們下一篇文章介紹,更多關于Lucene 索引刪除策略的資料請關注腳本之家其它相關文章!

相關文章

  • 使用SpringMVC響應json格式返回的結果類型

    使用SpringMVC響應json格式返回的結果類型

    這篇文章主要介紹了使用SpringMVC響應json格式返回的結果類型,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • 詳細解析Java中抽象類和接口的區(qū)別

    詳細解析Java中抽象類和接口的區(qū)別

    這篇文章主要介紹了Java中抽象類和接口的區(qū)別詳解,需要的朋友可以參考下
    2014-10-10
  • 簡單學習Java+MongoDB

    簡單學習Java+MongoDB

    本文給大家介紹的是如何簡單的使用java+MongoDB實現(xiàn)數(shù)據(jù)調用的問題,非常的實用,有需要的小伙伴可以參考下
    2016-03-03
  • IDEA去除掉代碼中虛線、波浪線和下劃線實線的方法

    IDEA去除掉代碼中虛線、波浪線和下劃線實線的方法

    初次安裝使用IDEA,總是能看到導入代碼后,出現(xiàn)很多的波浪線,下劃線和虛線,這是IDEA給我們的一些提示和警告,但是有時候我們并不需要,反而會讓人看著很不爽,這里簡單記錄一下自己的調整方法,供其他的小伙伴在使用的時候參考
    2024-09-09
  • 老生常談 java匿名內部類

    老生常談 java匿名內部類

    下面小編就為大家?guī)硪黄仙U刯ava匿名內部類。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-07-07
  • redis分布式鎖的實現(xiàn)原理詳解

    redis分布式鎖的實現(xiàn)原理詳解

    這篇文章主要為大家詳細介紹了redis分布式鎖,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • springboot整合Nacos組件環(huán)境搭建和入門案例詳解(最新推薦)

    springboot整合Nacos組件環(huán)境搭建和入門案例詳解(最新推薦)

    本文介紹了Nacos的基礎概念、關鍵特性、專業(yè)術語和生態(tài)圈,如何在Windows環(huán)境下搭建Nacos單個服務,以及如何整合SpringBoot2來使用Nacos進行服務注冊和配置管理,感興趣的朋友一起看看吧
    2025-03-03
  • IDEA的Terminal無法執(zhí)行git命令問題

    IDEA的Terminal無法執(zhí)行git命令問題

    這篇文章主要介紹了IDEA的Terminal無法執(zhí)行git命令問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • mybatis中的緩存機制

    mybatis中的緩存機制

    這篇文章主要介紹了mybatis中的緩存機制用法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • java編譯時與運行時概念與實例詳解

    java編譯時與運行時概念與實例詳解

    本篇文章通過實例對 java程序編譯時與運行時進行了詳解,需要的朋友可以參考下
    2017-04-04

最新評論

精河县| 遂溪县| 延长县| 南皮县| 潮安县| 龙泉市| 兴海县| 明光市| 北流市| 元朗区| 友谊县| 扶风县| 大荔县| 淮北市| 汝城县| 平原县| 同仁县| 金湖县| 蒲江县| 八宿县| 尉氏县| 新营市| 铜山县| 会理县| 黔南| 武宁县| 平安县| 宝清县| 焉耆| 无极县| 大悟县| 长海县| 离岛区| 桐柏县| 东丽区| 泰兴市| 开鲁县| 当雄县| 禹州市| 岳普湖县| 出国|