Solr4.8.0源码分析(11)之Lucene的索引文件(4)

Solr4.8.0源码分析(11)之Lucene的索引文件(4)

1. .dvd和.dvm文件

.dvm是存放了DocValue域的元数据,比如DocValue偏移量。

.dvd则存放了DocValue的数据。

在Solr4.8.0中,dvd以及dvm用到的Lucene编码格式是Lucene45DocValuesFormat。跟之前的文件格式类似,它分别包含Lucene45DocValuesProducer

和Lucene45DocValuesConsumer来实现该文件的读和写。

1   @Override
2   public DocValuesConsumer fieldsConsumer(SegmentWriteState state) throws IOException {
3     return new Lucene45DocValuesConsumer(state, DATA_CODEC, DATA_EXTENSION, META_CODEC, META_EXTENSION);
4   }
5
6   @Override
7   public DocValuesProducer fieldsProducer(SegmentReadState state) throws IOException {
8     return new Lucene45DocValuesProducer(state, DATA_CODEC, DATA_EXTENSION, META_CODEC, META_EXTENSION);
9   }

Lucene 4.5 DocValues format通过下面的策略对四种类型进行编码:

  • NUMERIC

    • Delta-compressed(增量压缩):表示文档值的整数写入一个16k大小的block。在每个block中,最小的值被编码,每个入口都是最小值的一个增量。所有这些增量都使用位压缩方法。更多信息见下文BlockPackedWriter。
    • Table-compressed(表压缩):当不同数值的数量非常小(<256)时,或者在文档值序列中有没有使用gap值,solr会使用一个查找表替代。每一个文档值的入口都被替代为表上的序号,这些序号同样适用位压缩的方法压缩为PackedInts格式。
    • GCD-compressed(最大公约数压缩):当所有数字分享同一个除数,最大共同分母(reatest common denominator,GCD) 会被计算出来,并且商会使用Delta-compressed 策略存储起来。
  • BINARY
    • Fixed-width Binary:使用一个固定长度的,很大的拼接起来的位数组。每一个文档值都可以直接用docID * length 得到。
    • Variable-width Binary:同样是一个很大的拼接起来的位数组,不过加入每篇文档的结束地址。这些地址从16k大小的块的起始位置被写入,并且每个入口具有平均长度的增量。对每篇文档,与增量之间的偏差(实际-平均)会记录下来。
    • Prefix-compressed Binary:值会写入16(字节)大小的chunk中,其中第一个值被完整地记录下来,而其他值分享前缀。Chunk的地址被写入16k大小的block中。从block的起始位置开始写,对每个入口使用平均值作为增量。对每篇文档,与增量之间的偏差(实际-平均)会记录下来。
  • Sorted:
    • 使用Prefix-compressed Binary压缩法实现一个从序号到重复的term的映射,同时所有文档的序号使用上面出现的Numeric压缩策略
  • SortedSet:
    • 使用Prefix-compressed Binary压缩法实现一个从序号到重复的term的映射,同时一个序号的列表和所有文档在这个列表上的索引使用上面出现的Numeric压缩策略。

1.1 .dvm和.dvd文件格式

首先来介绍下.dvm的文件格式:

. dvm的文件结构分为好多层:

  • 第一层:.dvm 由Header,<Entry>NumFields,Footer

    • Header和Footer跟之前相同
    • NumFields 个的Entry,Entry为入口。
  • 第二层:Entry具有四种类型,NumericEntry | BinaryEntry | SortedEntry | SortedSetEntry
  • 第三层:
    • NumericEntry:有三种类型GCDNumericEntry | TableNumericEntry | DeltaNumericEntry

      • GCDNumericEntry: 包含NumericHeader,MinValue,GCD三部分
      • TableNumericEntry:包含NumericHeader,TableSize,Int64TableSize三部分
      • DeltaNumericEntry:包含NumericHeader
    • BinaryEntry:   有三种类型FixedBinaryEntry | VariableBinaryEntry | PrefixBinaryEntry
      • FixedBinaryEntry:    包含BinaryHeader
      • VariableBinaryEntry:包含BinaryHeader,AddressOffset,PackedVersion,BlockSize四部分
      • PrefixBinaryEntry:    包含BinaryHeader,AddressInterval,AddressOffset,PackedVersion,BlockSize五部分
    • SortedEntry:  包含FieldNumber,EntryType,BinaryEntry,NumericEntry
    • SortedSetEntry: 包含EntryType,BinaryEntry,NumericEntry,NumericEntry
  • 第四层:
    • NumericHeader: 包含FieldNumber,EntryType,NumericType,MissingOffset,PackedVersion,DataOffset,Count,BlockSize

      • FieldNumber: 域的编号,如果为-1 表示最后一个元数据
      • EntryType:如果为0 则为NumericEntry,如果为1则为BinaryEntry
      • NumericType:Numeric的编码方式
        • 0:Delta-compressed(增量压缩): 16K大小的整数组成一个block,每一个block从这个block的最小整数开始增量递增。
        • 1:GCD-compressed(最大公约数压缩): 当所有整数具有相同公约数时,商将使用增量压缩进行编码
        • 2:Table-compressed(表压缩):当所有不同的数值较小时,使用表压缩方法可以节省空间,在原文档号后面会跟随不同数值的查找表。
      • MissingOffset:指向一个位数组,指明了所有有这个域值的文档。如果MissingOffset为-1,则说明没有丢失的值。
      • PackedVersion:
      • DataOffset:指向.dvd文件中数据起始位置的指针
      • Count:
      • MinLength:
    • BinaryEntry:包含FieldNumber,EntryType,BinaryType,MissingOffset,MinLength,MaxLength,DataOffset。(部分内容同NumericHeade)
      • BinaryType: 二进制的存储方式

        • 0: fixed-width(固定长度):所有值是固定长度的,可以直接通过乘法获取地址
        • 1: variable-width(可变长): 每个值的地址是额外存储的
        • 2:prefix-compressed(前缀压缩): 间隔的值的前缀会存储
      • MinLength and MaxLength:存储Binary 类型的值的位数组的长度的最小值和最大值。如果这两个值是相等的,那么所有的值都是固定的大小,并且可以通过DataOffset + (docID * length)计算出来。否则,Binary的值是不定长的,并且组合起来的整数型元数据(PackedVersion,BlockSize)用于支持这些地址。
    • Sorted域包括两个入口:BinaryEntry指向值的元数据,一般的NumericEntry指向document-to-ord (文档到编号)元数据。
    • SortedSet包括三个入口: 一个BinaryEntry指向值的元数据,两个NumericEntries 分别对应document-to-ord 索引和有序列表元数据。

.dvd文件格式:

同样.dvd 文件具有好几层结构:

  • 第一层:Header,<NumericData | BinaryData | SortedData>NumFields,Footer 与dvm类似,NumFields个Data(SortedData,BinaryData,NumericData其中一个)
  • 第二层:
    • NumericData:DeltaCompressedNumerics | TableCompressedNumerics | GCDCompressedNumerics对应上文讲到的Numeric的压缩方式
    • BinaryData:ByteDataLength,Addresses
    • SortedData:FST<Int64>
  • 第三层:
    • DeltaCompressedNumerics:BlockPackedInts(blockSize=16k)
    • TableCompressedNumerics:PackedInts
    • GCDCompressedNumerics: BlockPackedInts(blockSize=16k)
    • Addresses:MonotonicBlockPackedInts(blockSize=16k)
  • SortedSet入口储存了BinaryData中的序号的列表,使用一个增长的vLong类型的序列,并用差值编码。

1.2 .dvm和.dvd代码实现

前文讲到Lucene45DocValuesFormat分别包含Lucene45DocValuesProducer和Lucene45DocValuesConsumer来实现该文件的读和写,那么本节内容主要以Lucene45DocValuesProducer为例来学习下dvm和dvd。

首先学习下Lucene45DocValuesProducer的初始化:主要作用是读取.dvm文件和.dvd流。其中在读取.dvm文件过程中,Lucene45DocValuesProducer调用了readFields(in, state.fieldInfos)来获取入口信息。

 1 protected Lucene45DocValuesProducer(SegmentReadState state, String dataCodec, String dataExtension, String metaCodec, String metaExtension) throws IOException {
 2     //.dvm文件名
 3     String metaName = IndexFileNames.segmentFileName(state.segmentInfo.name, state.segmentSuffix, metaExtension);
 4     // read in the entries from the metadata file.
 5     //打开.dvm并获取检验和,获取文件流,
 6     ChecksumIndexInput in = state.directory.openChecksumInput(metaName, state.context);
 7     //获取segment的document个数
 8     this.maxDoc = state.segmentInfo.getDocCount();
 9     boolean success = false;
10     try {
11       //获取.dvm header
12       version = CodecUtil.checkHeader(in, metaCodec,
13                                       Lucene45DocValuesFormat.VERSION_START,
14                                       Lucene45DocValuesFormat.VERSION_CURRENT);
15       numerics = new HashMap<>();
16       ords = new HashMap<>();
17       ordIndexes = new HashMap<>();
18       binaries = new HashMap<>();
19       sortedSets = new HashMap<>();
20
21       //读取NumFields个<Entry>
22       readFields(in, state.fieldInfos);
23
24       //加入Footer
25       if (version >= Lucene45DocValuesFormat.VERSION_CHECKSUM) {
26         CodecUtil.checkFooter(in);
27       } else {
28         CodecUtil.checkEOF(in);
29       }
30
31       success = true;
32     } finally {
33       if (success) {
34         IOUtils.close(in);
35       } else {
36         IOUtils.closeWhileHandlingException(in);
37       }
38     }
39
40     success = false;
41     try {
42       //.dvd文件名
43       String dataName = IndexFileNames.segmentFileName(state.segmentInfo.name, state.segmentSuffix, dataExtension);
44       //打开.dvd文件
45       data = state.directory.openInput(dataName, state.context);
46       //获取.dvd header
47       final int version2 = CodecUtil.checkHeader(data, dataCodec,
48                                                  Lucene45DocValuesFormat.VERSION_START,
49                                                  Lucene45DocValuesFormat.VERSION_CURRENT);
50       if (version != version2) {
51         throw new CorruptIndexException("Format versions mismatch");
52       }
53
54       success = true;
55     } finally {
56       if (!success) {
57         IOUtils.closeWhileHandlingException(this.data);
58       }
59     }
60     //估算类的大小,也就是估算.dvd流的大小
61     ramBytesUsed = new AtomicLong(RamUsageEstimator.shallowSizeOfInstance(getClass()));
62   }

readFields(in, state.fieldInfos)主要是读取EntryType,根据它的值来选择哪种方式来读取后续的Entry信息,

函数中涉及了以下几个方式:

1. Numeric类型readNumericEntry()

2.BinaryEntry类型readBinaryEntry()

3.SortedSetEntry类型readSortedField()

4.SortedSetEntry类型readSortedSetEntry(),同时在该类型下,readFields还分别调用了readSortedSetFieldWithAddresses和readSortedField

 1 private void readFields(IndexInput meta, FieldInfos infos) throws IOException {
 2     //读取Entry的编号,如果编号为-1,表示这是最后一个Entry。
 3     int fieldNumber = meta.readVInt();
 4     while (fieldNumber != -1) {
 5       // check should be: infos.fieldInfo(fieldNumber) != null, which incorporates negative check
 6       // but docvalues updates are currently buggy here (loading extra stuff, etc): LUCENE-5616
 7       if (fieldNumber < 0) {
 8         // trickier to validate more: because we re-use for norms, because we use multiple entries
 9         // for "composite" types like sortedset, etc.
10         throw new CorruptIndexException("Invalid field number: " + fieldNumber + " (resource=" + meta + ")");
11       }
12       //读取EntryType,以此来区分Entry的类型,0表示NUMERICENTRY,1表示BINARYENTRY,2表示SORTEDENTRY,3表示SORTED_SETENTRY
13       byte type = meta.readByte();
14       if (type == Lucene45DocValuesFormat.NUMERIC) {
15         //获取具体的NumericEntry内容,并放入以编号为键,NumericEntry为值的map中
16         numerics.put(fieldNumber, readNumericEntry(meta));
17       } else if (type == Lucene45DocValuesFormat.BINARY) {
18         //获取具体的BinaryEntry内容,并放入以编号为键,BinaryEntry为值的map中
19         BinaryEntry b = readBinaryEntry(meta);
20         binaries.put(fieldNumber, b);
21       } else if (type == Lucene45DocValuesFormat.SORTED) {
22         //读取SortedEntry
23         readSortedField(fieldNumber, meta, infos);
24       } else if (type == Lucene45DocValuesFormat.SORTED_SET) {
25         //读取SortedSetEntry,并放入以编号为键,SortedSetEntry为值的map中
26         SortedSetEntry ss = readSortedSetEntry(meta);
27         sortedSets.put(fieldNumber, ss);
28         //标准的存储有序的集合是否通过address的间接转换,SORTED_SET_WITH_ADDRESSES是docid->address>ord映射
29         if (ss.format == SORTED_SET_WITH_ADDRESSES) {
30           readSortedSetFieldWithAddresses(fieldNumber, meta, infos);
31           //SORTED_SET_SINGLE_VALUED_SORTED 只存储docid->ord的值
32         } else if (ss.format == SORTED_SET_SINGLE_VALUED_SORTED) {
33           if (meta.readVInt() != fieldNumber) {
34             throw new CorruptIndexException("sortedset entry for field: " + fieldNumber + " is corrupt (resource=" + meta + ")");
35           }
36           if (meta.readByte() != Lucene45DocValuesFormat.SORTED) {
37             throw new CorruptIndexException("sortedset entry for field: " + fieldNumber + " is corrupt (resource=" + meta + ")");
38           }
39           readSortedField(fieldNumber, meta, infos);
40         } else {
41           throw new AssertionError();
42         }
43       } else {
44         throw new CorruptIndexException("invalid type: " + type + ", resource=" + meta);
45       }
46       //读取下一个Entry
47       fieldNumber = meta.readVInt();
48     }
49   }

1. readNumericEntry()

 1  static NumericEntry readNumericEntry(IndexInput meta) throws IOException {
 2     NumericEntry entry = new NumericEntry();
 3     entry.format = meta.readVInt();                //NumericType,Numeric的编码方式
 4     entry.missingOffset = meta.readLong();         //MissingOffset表示该field在哪个document中缺失,如果为-1表示没有document缺失字段
 5     entry.packedIntsVersion = meta.readVInt();     //PackedVersion  打包整数的version
 6     entry.offset = meta.readLong();                //DataOffset 指向.dvd文件中数据起始位置的指针
 7     entry.count = meta.readVLong();                //Count      已写的值的个数
 8     entry.blockSize = meta.readVInt();             //BlockSize  已打包的整数的大小
 9     switch(entry.format) {
10       case GCD_COMPRESSED:                         //GCD-compressed(最大公约数压缩)
11         entry.minValue = meta.readLong();          //MinValue
12         entry.gcd = meta.readLong();               //GCD
13         break;
14       case TABLE_COMPRESSED:                       //Table-compressed(表压缩)
15         if (entry.count > Integer.MAX_VALUE) {
16           throw new CorruptIndexException("Cannot use TABLE_COMPRESSED with more than MAX_VALUE values, input=" + meta);
17         }
18         final int uniqueValues = meta.readVInt();  //TableSize
19         if (uniqueValues > 256) {                  //TableSize必须小于256
20           throw new CorruptIndexException("TABLE_COMPRESSED cannot have more than 256 distinct values, input=" + meta);
21         }
22         entry.table = new long[uniqueValues];      //TableSize个Long
23         for (int i = 0; i < uniqueValues; ++i) {
24           entry.table[i] = meta.readLong();
25         }
26         break;
27       case DELTA_COMPRESSED:                      //Delta-compressed(增量压缩)
28         break;
29       default:
30         throw new CorruptIndexException("Unknown format: " + entry.format + ", input=" + meta);
31     }
32     return entry;
33   }

2. BinaryEntry()

 1 static BinaryEntry readBinaryEntry(IndexInput meta) throws IOException {
 2     BinaryEntry entry = new BinaryEntry();
 3     entry.format = meta.readVInt();                 //BinaryType类型
 4     entry.missingOffset = meta.readLong();          //缺失表示,同NuericEntry
 5     entry.minLength = meta.readVInt();              //存储Binary 类型的值的位数组的长度的最小值和最大值。
 6                                                     //如果这两个值是相等的,那么所有的值都是固定的大小,
 7                                                     //并且可以通过DataOffset + (docID * length)计算出来。
 8                                                     //否则,Binary的值是不定长的
 9     entry.maxLength = meta.readVInt();
10     entry.count = meta.readVLong();
11     entry.offset = meta.readLong();                 //实际二进制数的偏移
12     switch(entry.format) {
13       case BINARY_FIXED_UNCOMPRESSED:               //Fixed-width Binary
14         break;
15       case BINARY_PREFIX_COMPRESSED:                //Variable-width Binary
16         entry.addressInterval = meta.readVInt();
17         entry.addressesOffset = meta.readLong();
18         entry.packedIntsVersion = meta.readVInt();
19         entry.blockSize = meta.readVInt();
20         break;
21       case BINARY_VARIABLE_UNCOMPRESSED:            //Prefix-compressed Binary
22         entry.addressesOffset = meta.readLong();
23         entry.packedIntsVersion = meta.readVInt();
24         entry.blockSize = meta.readVInt();
25         break;
26       default:
27         throw new CorruptIndexException("Unknown format: " + entry.format + ", input=" + meta);
28     }
29     return entry;
30   }

3. readSortedSetFieldWithAddresses()

 1 private void readSortedSetFieldWithAddresses(int fieldNumber, IndexInput meta, FieldInfos infos) throws IOException {
 2     // sortedset = binary + numeric (addresses) + ordIndex
 3     if (meta.readVInt() != fieldNumber) {
 4       throw new CorruptIndexException("sortedset entry for field: " + fieldNumber + " is corrupt (resource=" + meta + ")");
 5     }
 6     if (meta.readByte() != Lucene45DocValuesFormat.BINARY) {
 7       throw new CorruptIndexException("sortedset entry for field: " + fieldNumber + " is corrupt (resource=" + meta + ")");
 8     }
 9     BinaryEntry b = readBinaryEntry(meta);
10     binaries.put(fieldNumber, b);
11
12     if (meta.readVInt() != fieldNumber) {
13       throw new CorruptIndexException("sortedset entry for field: " + fieldNumber + " is corrupt (resource=" + meta + ")");
14     }
15     if (meta.readByte() != Lucene45DocValuesFormat.NUMERIC) {
16       throw new CorruptIndexException("sortedset entry for field: " + fieldNumber + " is corrupt (resource=" + meta + ")");
17     }
18     NumericEntry n1 = readNumericEntry(meta);
19     ords.put(fieldNumber, n1);
20
21     if (meta.readVInt() != fieldNumber) {
22       throw new CorruptIndexException("sortedset entry for field: " + fieldNumber + " is corrupt (resource=" + meta + ")");
23     }
24     if (meta.readByte() != Lucene45DocValuesFormat.NUMERIC) {
25       throw new CorruptIndexException("sortedset entry for field: " + fieldNumber + " is corrupt (resource=" + meta + ")");
26     }
27     NumericEntry n2 = readNumericEntry(meta);
28     ordIndexes.put(fieldNumber, n2);
29   }

4. readSortedField

 1 private void readSortedField(int fieldNumber, IndexInput meta, FieldInfos infos) throws IOException {
 2     // sorted = binary + numeric
 3     if (meta.readVInt() != fieldNumber) {
 4       throw new CorruptIndexException("sorted entry for field: " + fieldNumber + " is corrupt (resource=" + meta + ")");
 5     }
 6     if (meta.readByte() != Lucene45DocValuesFormat.BINARY) {
 7       throw new CorruptIndexException("sorted entry for field: " + fieldNumber + " is corrupt (resource=" + meta + ")");
 8     }
 9     BinaryEntry b = readBinaryEntry(meta);
10     binaries.put(fieldNumber, b);
11
12     if (meta.readVInt() != fieldNumber) {
13       throw new CorruptIndexException("sorted entry for field: " + fieldNumber + " is corrupt (resource=" + meta + ")");
14     }
15     if (meta.readByte() != Lucene45DocValuesFormat.NUMERIC) {
16       throw new CorruptIndexException("sorted entry for field: " + fieldNumber + " is corrupt (resource=" + meta + ")");
17     }
18     NumericEntry n = readNumericEntry(meta);
19     ords.put(fieldNumber, n);
20   }
时间: 2024-12-22 09:06:15

Solr4.8.0源码分析(11)之Lucene的索引文件(4)的相关文章

Solr4.8.0源码分析(10)之Lucene的索引文件(3)

Solr4.8.0源码分析(10)之Lucene的索引文件(3) 1. .si文件 .si文件存储了段的元数据,主要涉及SegmentInfoFormat.java和Segmentinfo.java这两个文件.由于本文介绍的Solr4.8.0,所以对应的是SegmentInfoFormat的子类Lucene46SegmentInfoFormat. 首先来看下.si文件的格式 头部(header) 版本(SegVersion) doc个数(SegSize) 是否符合文档格式(IsCompoundF

Solr4.8.0源码分析(12)之Lucene的索引文件(5)

Solr4.8.0源码分析(12)之Lucene的索引文件(5) 1. 存储域数据文件(.fdt和.fdx) Solr4.8.0里面使用的fdt和fdx的格式是lucene4.1的.为了提升压缩比,StoredFieldsFormat以16KB为单位对文档进行压缩,使用的压缩算法是LZ4,由于它更着眼于速度而不是压缩比,所以它能快速压缩以及解压. 1.1 存储域数据文件(.fdt) 真正保存存储域(stored field)信息的是fdt文件,该文件存放了压缩后的文档,按16kb或者更大的模块大

Solr4.8.0源码分析(8)之Lucene的索引文件(1)

Solr4.8.0源码分析(8)之Lucene的索引文件(1) 题记:最近有幸看到觉先大神的Lucene的博客,感觉自己之前学习的以及工作的太为肤浅,所以决定先跟随觉先大神的博客学习下Lucene的原理.由于觉先大神主要介绍的是Lucene3.X系的,那我就根据源码以及结合觉先大神的来学习下4.X系的.内容可能会有些变化,且加入下我个人的理解. http://www.cnblogs.com/forfuture1978/archive/2009/12/14/1623597.html 一. 基本类型

Solr4.8.0源码分析(9)之Lucene的索引文件(2)

Solr4.8.0源码分析(9)之Lucene的索引文件(2) 一. Segments_N文件 一个索引对应一个目录,索引文件都存放在目录里面.Solr的索引文件存放在Solr/Home下的core/data/index目录中,一个core对应一个索引. Segments_N例举了索引所有有效的segments信息以及删除的具体信息,一个索引可以有多个Segments_N,但是有效的往往总是N最大的那个,为什么会出现多个segments_N,主要是由于暂时无法删除它们或者有indexwriter

Solr4.8.0源码分析(13)之LuceneCore的索引修复

Solr4.8.0源码分析(13)之LuceneCore的索引修复 题记:今天在公司研究elasticsearch,突然看到一篇博客说elasticsearch具有索引修复功能,顿感好奇,于是点进去看了下,发现原来是Lucene Core自带的功能,于是就回家先学习下,正好也跟之前看的索引文件的格式相应.有空也研究下Lucene的一些小工具. 索引的修复主要是用到CheckIndex.java这个类,可以直接查看类的Main函数来了解下. 1. CheckIndex的使用 首先使用以下命令来查看

Solr4.8.0源码分析(22)之 SolrCloud的Recovery策略(三)

Solr4.8.0源码分析(22)之 SolrCloud的Recovery策略(三) 本文是SolrCloud的Recovery策略系列的第三篇文章,前面两篇主要介绍了Recovery的总体流程,以及PeerSync策略.本文以及后续的文章将重点介绍Replication策略.Replication策略不但可以在SolrCloud中起到leader到replica的数据同步,也可以在用多个单独的Solr来实现主从同步.本文先介绍在SolrCloud的leader到replica的数据同步,下一篇

Solr4.8.0源码分析(4)之Eclipse Solr调试环境搭建

Solr4.8.0源码分析(4)之Eclipse Solr调试环境搭建 由于公司里的Solr调试都是用远程jpda进行的,但是家里只有一台电脑所以不能jpda进行调试,这是因为jpda的端口冲突.所以只能在Eclipse 搭建Solr的环境,折腾了一小时终于完成了. 1. JDPA远程调试 搭建换完成Solr环境后,对${TOMCAT_HOME}/bin/startup.sh 最后一行进行修改,如下所示: 1 set JPDA_ADDRESS=7070 2 exec "$PRGDIR"

Solr4.8.0源码分析(25)之SolrCloud的Split流程

Solr4.8.0源码分析(25)之SolrCloud的Split流程(一) 题记:昨天有位网友问我SolrCloud的split的机制是如何的,这个还真不知道,所以今天抽空去看了Split的原理,大致也了解split的原理了,所以也就有了这篇文章.本系列有两篇文章,第一篇为core split,第二篇为collection split. 1. 简介 这里首先需要介绍一个比较容易混淆的概念,其实Solr的HTTP API 和 SolrCloud的HTTP API是不一样,如果接受到的是Solr的

Solr4.8.0源码分析(24)之SolrCloud的Recovery策略(五)

Solr4.8.0源码分析(24)之SolrCloud的Recovery策略(五) 题记:关于SolrCloud的Recovery策略已经写了四篇了,这篇应该是系统介绍Recovery策略的最后一篇了.本文主要介绍Solr的主从同步复制.它与前文<Solr4.8.0源码分析(22)之SolrCloud的Recovery策略(三)>略有不同,前文讲到的是SolrCloud的leader与replica之间的同步,不需要通过配置solrconfig.xml来实现.而本文主要介绍单机模式下,利用so