Pinot中的Forward Index源码分析

还是以Quickstart为例,说一下forward index的创建过程。

  1. 收集各个column的统计数据

    代码同dictionary index。

  2. 再次遍历,按行处理每列的索引

    iterator复位

    // Build the index
    recordReader.rewind();

    重新遍历,对每行索引

    LOGGER.info("Start building IndexCreator!");
    while (recordReader.hasNext()) {
      long start = System.currentTimeMillis();
      GenericRow row = recordReader.next();
      long stop = System.currentTimeMillis();
      indexCreator.indexRow(row);
      long stop1 = System.currentTimeMillis();
      totalRecordReadTime += (stop - start);
      totalIndexTime += (stop1 - stop);
    }

    indexRow的实现:

    @Override
    public void indexRow(GenericRow row) {
        for (final String column : dictionaryCreatorMap.keySet()) {
    
            Object columnValueToIndex = row.getValue(column);
            Object dictionaryIndex;
            if (dictionaryCache.get(column).containsKey(columnValueToIndex)) {
                dictionaryIndex = dictionaryCache.get(column).get(columnValueToIndex);
            } else {
                dictionaryIndex = dictionaryCreatorMap.get(column).indexOf(columnValueToIndex);
                dictionaryCache.get(column).put(columnValueToIndex, dictionaryIndex);
            }
            forwardIndexCreatorMap.get(column).index(docIdCounter, dictionaryIndex);
            if (config.createInvertedIndexEnabled()) {
                invertedIndexCreatorMap.get(column).add(docIdCounter, dictionaryIndex);
            }
        }
        docIdCounter++;
    }

    对每一个值,都需要在dictionary index中调用indexOf以得到该值的位置。

    public Object indexOf(Object e) {
        if (spec.isSingleValueField()) {
            return indexOfSV(e);
        } else {
            return indexOfMV(e);
        }
    }

    对于single value field来说,用二分查找找位置,时间复杂度O(lgn)。不过找到之后会放在map里,所以对于重复的value就是O(1)的时间复杂度了。

    private Integer indexOfSV(Object e) {
        switch (spec.getDataType()) {
            case INT:
                final int intValue = ((Integer) e).intValue();
                return new Integer(searchableByteBuffer.binarySearch(0, intValue));
            case FLOAT:
                final float floatValue = ((Float) e).floatValue();
                return new Integer(searchableByteBuffer.binarySearch(0, floatValue));
            case DOUBLE:
                final double doubleValue = ((Double) e).doubleValue();
                return new Integer(searchableByteBuffer.binarySearch(0, doubleValue));
            case LONG:
                final long longValue = ((Long) e).longValue();
                return new Integer(searchableByteBuffer.binarySearch(0, longValue));
            case STRING:
            case BOOLEAN:
                final StringBuilder bld = new StringBuilder();
                bld.append(e.toString());
                for (int i = 0; i < (stringColumnMaxLength - ((String) e).getBytes(Charset.forName("UTF-8")).length); i++) {
                    bld.append(V1Constants.Str.STRING_PAD_CHAR);
                }
                return new Integer(searchableByteBuffer.binarySearch(0, bld.toString()));
            default:
                break;
        }
    
        throw new UnsupportedOperationException("unsupported data type : " + spec.getDataType() + " : " + " for column : "
            + spec.getName());
    }

    在forward index中实际存储的是int,和column的type没有关系。

    @Override
    public void index(int docId, Object e) {
        final int entry = ((Integer) e).intValue();
        sVWriter.setInt(docId, 0, entry);
    }

    公式有点复杂,根据volumn的宽度来直接定位第n位的位置。注意column都是等宽的。对于String这种不等宽的column,会加padding变为等宽。

    public void setInt(int row, int col, int val) {
        assert val >= minValues[col]  && val <= maxValues[col];
        int bitOffset = rowSizeInBits * row + columnOffsetsInBits[col];
        val = val + offsets[col];
        for (int bitPos = colSizesInBits[col] - 1; bitPos >= 0; bitPos--) {
            if ((val & (1 << bitPos)) != 0) {
                bitSet.setBit(bitOffset + (colSizesInBits[col] - bitPos - 1));
            }
        }
    }

简单来说,forward index包含的内容就是一个一维数组,每个元素对应该行该列的值在dictionary index中的位置。真正得到这个值就需要在dictionary index中进行一次O(1)的查找。

RowId Value
m i (第m行的值在dictionary index中的位置)
n j (第n行的值在dictionary index中的位置)

你可能感兴趣的:(Pinot中的Forward Index源码分析)