【leveldb源码】“读”流程及实现

读一条value可真费劲

总体代码

@Override
    public byte[] get(byte[] key, ReadOptions options)
            throws DBException
    {
        checkBackgroundException();
        LookupKey lookupKey;
        mutex.lock();
        try {
            SnapshotImpl snapshot = getSnapshot(options);
            lookupKey = new LookupKey(Slices.wrappedBuffer(key), snapshot.getLastSequence());

            LookupResult lookupResult = memTable.get(lookupKey);
            if (lookupResult != null) {
                Slice value = lookupResult.getValue();
                if (value == null) {
                    return null;
                }
                return value.getBytes();
            }
            if (immutableMemTable != null) {
                lookupResult = immutableMemTable.get(lookupKey);
                if (lookupResult != null) {
                    Slice value = lookupResult.getValue();
                    if (value == null) {
                        return null;
                    }
                    return value.getBytes();
                }
            }
        }
        finally {
            mutex.unlock();
        }

        LookupResult lookupResult = versions.get(lookupKey);

        mutex.lock();
        try {
            if (versions.needsCompaction()) {
                maybeScheduleCompaction();
            }
        }
        finally {
            mutex.unlock();
        }

        if (lookupResult != null) {
            Slice value = lookupResult.getValue();
            if (value != null) {
                return value.getBytes();
            }
        }
        return null;
    }

步骤

step0. 加互斥锁,ReentrantLock

step1. 根据option决定是否生成快照

step2. 根据编码规则生成LookupKey

LookupKey的类型是InternalKey(User key+sequence number+value type)

step3. 从memtable中找value,找不到执行下一步

step4. 从immutableMemTable中找,找不到执行下一步

step5. 从当前version的sst文件中找

在versionSet中的当前版本找

public LookupResult get(LookupKey key)
    {
        return current.get(key);
    }

在当前版本中一层一层往下找

public LookupResult get(LookupKey key)
    {
        // 首先从level 0 找,找不到的话,一层一层往下找
        ReadStats readStats = new ReadStats();
        LookupResult lookupResult = level0.get(key, readStats);
        if (lookupResult == null) {
            for (Level level : levels) {
                lookupResult = level.get(key, readStats);
                if (lookupResult != null) {
                    break;
                }
            }
        }
        updateStats(readStats.getSeekFileLevel(), readStats.getSeekFile());
        return lookupResult;
    }

ReadStats记录了level信息,需要在每次查找中携带

public class ReadStats
{
    private int seekFileLevel = -1;
    private FileMetaData seekFile;
}

level 0的查找源码如下:

public LookupResult get(LookupKey key, ReadStats readStats)
    {
        if (files.isEmpty()) {
            return null;
        }

        List<FileMetaData> fileMetaDataList = new ArrayList<>(files.size());
        // level0 内的.sst文件,两个文件可能存在key重叠,所以需要遍历level0内的sst,找到要查找的key在sst内的所有sst
        // 如果不是level0 内的.sst文件,key不存在重叠
        for (FileMetaData fileMetaData : files) {
            if (internalKeyComparator.getUserComparator().compare(key.getUserKey(), fileMetaData.getSmallest().getUserKey()) >= 0 &&
                    internalKeyComparator.getUserComparator().compare(key.getUserKey(), fileMetaData.getLargest().getUserKey()) <= 0) {
                fileMetaDataList.add(fileMetaData);
            }
        }

        // 根据file number,将新的fileMetaData排在前面
        Collections.sort(fileMetaDataList, NEWEST_FIRST);

        readStats.clear();
        for (FileMetaData fileMetaData : fileMetaDataList) {
            // 根据fileMetaData中的file number,从tableCache中获得对应的table的iterator
            InternalTableIterator iterator = tableCache.newIterator(fileMetaData);

            // 在table中指向 >= lookup key的第一个key
            iterator.seek(key.getInternalKey());

            if (iterator.hasNext()) {
                // 解析出block中的key
                Map.Entry<InternalKey, Slice> entry = iterator.next();
                InternalKey internalKey = entry.getKey();
                checkState(internalKey != null, "Corrupt key for %s", key.getUserKey().toString(UTF_8));

                // 如果找到了key
                //  1. valuetype是value,那么返回LookupResult
                //  1. valuetype是delete,那么返回LookupResult
                if (key.getUserKey().equals(internalKey.getUserKey())) {
                    if (internalKey.getValueType() == ValueType.DELETION) {
                        return LookupResult.deleted(key);
                    }
                    else if (internalKey.getValueType() == VALUE) {
                        return LookupResult.ok(key, entry.getValue());
                    }
                }
            }
            // 如果readStats中没有File信息,设置当前最新的sst文件为level0
            if (readStats.getSeekFile() == null) {

                readStats.setSeekFile(fileMetaData);
                readStats.setSeekFileLevel(0);
            }
        }

        return null;
    }

其他level的查找如下:

public LookupResult get(LookupKey key, ReadStats readStats)
    {
        if (files.isEmpty()) {
            return null;
        }

        List<FileMetaData> fileMetaDataList = new ArrayList<>(files.size());
        // level0 内的.sst文件,两个文件可能存在key重叠,所以需要遍历level0内的sst,找到要查找的key在sst内的所有sst
        // 如果不是level0 内的.sst文件,key不存在重叠,就可以直接二分
        if (levelNumber == 0) {
            for (FileMetaData fileMetaData : files) {
                if (internalKeyComparator.getUserComparator().compare(key.getUserKey(), fileMetaData.getSmallest().getUserKey()) >= 0 &&
                        internalKeyComparator.getUserComparator().compare(key.getUserKey(), fileMetaData.getLargest().getUserKey()) <= 0) {
                    fileMetaDataList.add(fileMetaData);
                }
            }
        }
        else {
            // 二分查找最小的 key >= ikey的文件
            int index = ceilingEntryIndex(Lists.transform(files, FileMetaData::getLargest), key.getInternalKey(), internalKeyComparator);

            // 如果已经找到了文件最后,都没找到,说明sstable中不包含key
            if (index >= files.size()) {
                return null;
            }

            // 验证文件的最小key是不是大于key
            FileMetaData fileMetaData = files.get(index);
            if (internalKeyComparator.getUserComparator().compare(key.getUserKey(), fileMetaData.getSmallest().getUserKey()) < 0) {
                return null;
            }

            // 将该文件添加到带查找列表
            fileMetaDataList.add(fileMetaData);
        }

        FileMetaData lastFileRead = null;
        int lastFileReadLevel = -1;
        readStats.clear();
        for (FileMetaData fileMetaData : fileMetaDataList) {
            if (lastFileRead != null && readStats.getSeekFile() == null) {
                // 记录第一个文件的信息
                readStats.setSeekFile(lastFileRead);
                readStats.setSeekFileLevel(lastFileReadLevel);
            }

            lastFileRead = fileMetaData;
            lastFileReadLevel = levelNumber;

            // 根据fileMetaData中的file number,从tableCache中获得对应的table的iterator
            InternalTableIterator iterator = tableCache.newIterator(fileMetaData);

            // 在table中指向 >= lookup key的第一个key
            iterator.seek(key.getInternalKey());

            if (iterator.hasNext()) {
                // 解析出block中的key
                Map.Entry<InternalKey, Slice> entry = iterator.next();
                InternalKey internalKey = entry.getKey();
                checkState(internalKey != null, "Corrupt key for %s", key.getUserKey().toString(UTF_8));

                // 如果找到了key
                //  1. valuetype是value,那么返回LookupResult
                //  1. valuetype是delete,那么返回LookupResult
                if (key.getUserKey().equals(internalKey.getUserKey())) {
                    if (internalKey.getValueType() == ValueType.DELETION) {
                        return LookupResult.deleted(key);
                    }
                    else if (internalKey.getValueType() == VALUE) {
                        return LookupResult.ok(key, entry.getValue());
                    }
                }
            }
        }

        return null;
    }

step6. 加互斥锁

step7. 判断是否需要compact,需要就compact

压缩条件如下:

public boolean needsCompaction()
    {
        return current.getCompactionScore() >= 1 || current.getFileToCompact() != null;
    }

// todo:压缩过程待另写一篇博客

step8. 释放互斥锁

step9. 返回sst文件查询结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值