【leveldb源码】核心结构之BlockIterator

直接上代码,解释及逻辑都在注释里
在 public void seek(Slice targetKey)函数中有一块非常精华的部分,因为有重启点的位置信息,且存储点是有序的,可以二分查找重启点,直到找到小于targetKey的最大key,在该重启点及后面的压缩key区域线性查找对应的key。源码这个二分思想太漂亮了!!

int left = 0;
        int right = restartCount - 1;
        // 二分查找重启点和targetKey,直到找到大于targetkey的前面一个key
        while (left < right) {
            int mid = (left + right + 1) / 2;
            seekToRestartPosition(mid);
            if (comparator.compare(nextEntry.getKey(), targetKey) < 0) {
                left = mid;
            } else {
                right = mid - 1;
            }
        }

        // 在当前重启点及后面的区域里,线性查找Entry
        for (seekToRestartPosition(left); nextEntry != null; next()) {
            if (comparator.compare(peek().getKey(), targetKey) >= 0) {
                break;
            }
        }

下面是BlockIterator的完整源码

public class BlockIterator
        implements SeekingIterator<Slice, Slice>
{
    private final SliceInput data;
    private final Slice restartPositions;
    private final int restartCount;
    private final Comparator<Slice> comparator;
	/**
	 * BlockEntry的结构:
	 * private final Slice key;
	 * private final Slice value;
	 */
    private BlockEntry nextEntry;
	// data是k-v存储区,restartPositions是重启点存储区(不包含最后一个重启点数量)
    public BlockIterator(Slice data, Slice restartPositions, Comparator<Slice> comparator)
    {
        requireNonNull(data, "data is null");
        requireNonNull(restartPositions, "restartPositions is null");
        checkArgument(restartPositions.length() % INT_UNIT == 0, "restartPositions.readableBytes() must be a multiple of %s", INT_UNIT);
        requireNonNull(comparator, "comparator is null");

        this.data = data.input();

        this.restartPositions = restartPositions.slice();
        // 重启点的长度 / int的长度(即4个byte),得到重启点的个数
        restartCount = this.restartPositions.length() / INT_UNIT;

        this.comparator = comparator;
        // 将迭代器至于起始位置
        seekToFirst();
    }

    @Override
    public boolean hasNext()
    {
        return nextEntry != null;
    }

    @Override
    public BlockEntry peek()
    {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }
        return nextEntry;
    }

    @Override
    public BlockEntry next()
    {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }

        BlockEntry entry = nextEntry;

        if (!data.isReadable()) {
            nextEntry = null;
        }
        else {
            // 在当前位置读取Entry,data指向下一个节点,nextEntry用于拼接公共前缀
            nextEntry = readEntry(data, nextEntry);
        }

        return entry;
    }

    @Override
    public void remove()
    {
        throw new UnsupportedOperationException();
    }

    /**
     * 通过设置重启点的偏移量指向的位置,来决定访问的数据
     */
    @Override
    public void seekToFirst()
    {
        if (restartCount > 0) {
            seekToRestartPosition(0);
        }
    }

    /**
     * 查找大于等于targetKey的Entry
     */
    @Override
    public void seek(Slice targetKey)
    {
        if (restartCount == 0) {
            return;
        }
        int left = 0;
        int right = restartCount - 1;
        // 二分查找重启点和targetKey,直到找到大于targetkey的前面一个key
        while (left < right) {
            int mid = (left + right + 1) / 2;
            seekToRestartPosition(mid);
            if (comparator.compare(nextEntry.getKey(), targetKey) < 0) {
                left = mid;
            } else {
                right = mid - 1;
            }
        }

        // 在当前重启点及后面的区域里,线性查找Entry
        for (seekToRestartPosition(left); nextEntry != null; next()) {
            if (comparator.compare(peek().getKey(), targetKey) >= 0) {
                break;
            }
        }

    }

    /**
     * 读取指定重启点的Entry,因为重启点没有压缩key的部分,所以previousEntry是null
     *
     */
    private void seekToRestartPosition(int restartPosition)
    {
        checkPositionIndex(restartPosition, restartCount, "restartPosition");

        // 根据重启点的偏移获得k-v存储区的偏移量
        int offset = restartPositions.getInt(restartPosition * INT_UNIT);
        data.setPosition(offset);

        // 这里因为当前点是重启点,没有压缩前缀,所以nextEntry赋值为null
        nextEntry = null;

        // 读取Entry,这里因为当前点是重启点,没有压缩前缀,所以readEntry传入null
        nextEntry = readEntry(data, null);
    }

    /**
     * 将缓存数据转为BlockEntry
     * 读取完这个BlockEntry之后,index会指向后面一个data的index
     */
    private static BlockEntry readEntry(SliceInput data, BlockEntry previousEntry)
    {
        requireNonNull(data, "data is null");

        // 读取Block当前key的共享前缀长度, 前缀之后的字符串长度,值的长度
        int sharedKeyLength = Coding.decodeInt(data);
        int nonSharedKeyLength = Coding.decodeInt(data);
        int valueLength = Coding.decodeInt(data);

        // 读取key
        final Slice key;
        if (sharedKeyLength > 0) {
            // 根据贡献key的长度和非共享key的长度创建新的key
            key = Slices.allocate(sharedKeyLength + nonSharedKeyLength);
            // 创建key的SliceOutput
            SliceOutput sliceOutput = key.output();
            checkState(previousEntry != null, "Entry has a shared key but no previous entry was provided");
            // 将前缀key写入到sliceOutput
            sliceOutput.writeBytes(previousEntry.getKey(), 0, sharedKeyLength);
            // 将key的后半部分写入到sliceOutput
            sliceOutput.writeBytes(data, nonSharedKeyLength);
        }
        else {
            // 没有前缀的情况下,将nonSharedKeyLength长度的数据读到key
            key = data.readSlice(nonSharedKeyLength);
        }
        // 读取value
        Slice value = data.readSlice(valueLength);

        return new BlockEntry(key, value);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值