跳至主要內容

索引管理

blockCloth2024年8月31日大约 4 分钟IMIndex

本章涉及代码:com/dyx/simpledb/backend/im/*

前言

IM(Index Manager,索引管理器)是 EasyDB 中用于管理 B+ 树索引的模块。它为 EasyDB 提供了基于 B+ 树的聚簇索引功能。 在 EasyDB 的依赖关系图中可以看到,IM 直接基于 DM(Data Manager)实现,而没有依赖 VM(Version Manager)。这意味着索引数据直接存储在数据库文件中,而无需经过版本管理。本节不深入探讨 B+ 树的算法实现,而是重点描述其在 EasyDB 中的具体实现。

二叉树索引结构

B+ 树由多个节点(Node)组成,每个节点都存储在一条 DataItem 中。其数据结构如下:

[LeafFlag][KeyNumber][SiblingUid]
[Son0][Key0][Son1][Key1]...[SonN][KeyN]

Node 类持有其所属的 B+ 树的引用,DataItem 的引用和 SubArray 的引用,用于方便快速修改和释放数据。

public class Node {
    BPlusTree tree;
    DataItem dataItem;
    SubArray raw;
    long uid;
    ...
}

根节点的初始化

在 B+ 树中,生成根节点数据的方法如下:

static byte[] newRootRaw(long left, long right, long key)  {
    SubArray raw = new SubArray(new byte[NODE_SIZE], 0, NODE_SIZE);
    setRawIsLeaf(raw, false);
    setRawNoKeys(raw, 2);
    setRawSibling(raw, 0);
    setRawKthSon(raw, left, 0);
    setRawKthKey(raw, key, 0);
    setRawKthSon(raw, right, 1);
    setRawKthKey(raw, Long.MAX_VALUE, 1);
    return raw.raw;
}

这个方法生成的根节点包含两个初始子节点 leftright,以及一个初始键值 key。 生成一个空的根节点数据的方法如下:

static byte[] newNilRootRaw()  {
    SubArray raw = new SubArray(new byte[NODE_SIZE], 0, NODE_SIZE);
    setRawIsLeaf(raw, true);
    setRawNoKeys(raw, 0);
    setRawSibling(raw, 0);
    return raw.raw;
}

搜索与插入操作

Node 类提供了两个主要方法,用于辅助 B+ 树执行插入和搜索操作:searchNextleafSearchRange

public SearchNextRes searchNext(long key) {
    dataItem.rLock();
    try {
        SearchNextRes res = new SearchNextRes();
        int noKeys = getRawNoKeys(raw);
        for(int i = 0; i < noKeys; i ++) {
            long ik = getRawKthKey(raw, i);
            if(key < ik) {
                res.uid = getRawKthSon(raw, i);
                res.siblingUid = 0;
                return res;
            }
        }
        res.uid = 0;
        res.siblingUid = getRawSibling(raw);
        return res;
    } finally {
        dataItem.rUnLock();
    }
}
public LeafSearchRangeRes leafSearchRange(long leftKey, long rightKey) {
    dataItem.rLock();
    try {
        int noKeys = getRawNoKeys(raw);
        int kth = 0;
        while(kth < noKeys) {
            long ik = getRawKthKey(raw, kth);
            if(ik >= leftKey) {
                break;
            }
            kth ++;
        }
        List<Long> uids = new ArrayList<>();
        while(kth < noKeys) {
            long ik = getRawKthKey(raw, kth);
            if(ik <= rightKey) {
                uids.add(getRawKthSon(raw, kth));
                kth ++;
            } else {
                break;
            }
        }
        long siblingUid = 0;
        if(kth == noKeys) {
            siblingUid = getRawSibling(raw);
        }
        LeafSearchRangeRes res = new LeafSearchRangeRes();
        res.uids = uids;
        res.siblingUid = siblingUid;
        return res;
    } finally {
        dataItem.rUnLock();
    }
}

根节点的管理

由于 B+ 树在插入和删除操作时会动态调整,根节点并不是固定的。为此,系统设置了一个 bootDataItem,其中存储了根节点的 UID。操作 DM 时,IM 使用的事务 ID 为 SUPER_XID

public class BPlusTree {
    DataItem bootDataItem;

    private long rootUid() {
        bootLock.lock();
        try {
            SubArray sa = bootDataItem.data();
            return Parser.parseLong(Arrays.copyOfRange(sa.raw, sa.start, sa.start+8));
        } finally {
            bootLock.unlock();
        }
    }

    private void updateRootUid(long left, long right, long rightKey) throws Exception {
        bootLock.lock();
        try {
            byte[] rootRaw = Node.newRootRaw(left, right, rightKey);
            long newRootUid = dm.insert(TransactionManagerImpl.SUPER_XID, rootRaw);
            bootDataItem.before();
            SubArray diRaw = bootDataItem.data();
            System.arraycopy(Parser.long2Byte(newRootUid), 0, diRaw.raw, diRaw.start, 8);
            bootDataItem.after(TransactionManagerImpl.SUPER_XID);
        } finally {
            bootLock.unlock();
        }
    }
}

错误处理与恢复

在 B+ 树的操作过程中,可能会出现两种主要错误:节点内部错误和节点间关系错误。

结语

通过上述设计与实现,IM 提供了 EasyDB 的索引管理功能,尽管目前不支持删除索引操作,但可以通过 XMAX 标记来避免数据的不一致性。在后续操作中,如果某个节点发生故障,系统仍然可以通过兄弟节点恢复大部分操作。

本文作者:blockCloth
部分内容转载自:https://shinya.click/projects/mydb/mydb8
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0许可协议。转载请注明来自 blockCloth