数据库索引

数据库索引

表索引

表索引是表属性子集的复制品,以便这些属性进行有效访问组织和/或排序。
DBMS 可确保表和索引的内容在逻辑上同步。和索引的内容在逻辑上是同步的。

DBMS 的工作是找出执行每个查询所需的最佳索引。
对于每个数据库创建的索引数量需要权衡。

  • 存储开销
  • 维护开销

B+树

B+Tree是一种自平衡树数据结构保持数据排序并允许搜索,总是顺序访问,以O(log n)插入和删除

  • 是二叉搜索树的推广,因为节点可以有两个以上的孩子。
  • 针对读取和写入大数据块的系统进行了优化。

B+树性质

B+Tree 是一种 M 路搜索树,具有以下属性:

  • 它是完美平衡的(即每个叶节点在树中都处于相同的深度)
  • 除根节点外的每个节点都至少是半满的 即 M/2 - 1 ≤ keys ≤ M-1
  • 每个具有 k 个键的内部节点都有 k+1 个非空子节点

nodes

每个 B+Tree 节点都由一组键/值对组成。

  • 键源自索引所基于的属性。
  • 根据节点被分类为内部节点或叶节点,这些值会有所不同。

数组(通常)按键顺序保存

B+树叶子节点

叶子节点是存储实际数据的部分,而内部节点用于索引和导航。在B+树中,所有叶子节点都位于同一层级,并通过链表连接在一起,形成一个有序的数据序列。这个特性使得范围查询非常高效,同时也方便了顺序访问。

Leaf Node values
  1. Record/Tuple Ids(记录/元组标识):
    在这种方式下,叶子节点存储的是记录或元组的标识,通常是记录在数据库中的主键或行号。实际的数据存储在其他位置,通过标识可以在需要时查找相应的数据。

  2. Tuple Data(元组数据):
    在这种方式下,叶子节点直接存储实际的元组数据,而不仅仅是标识。这意味着叶子节点包含了完整的数据,无需进行额外的IO操作。

B+ Tree Insert

  1. 找到对应的 leaf node,L
  2. 将 key/value pair 按顺序插入到 L 中
  3. 如果 L 还有足够的空间,操作结束;
    如果空间不足,则需要将 L 分裂成两个节点,同时在 parent node 上新增 entry,
    若 parent node 也空间不足,则递归地分裂,直到 root node 为止。

B+ Tree Delete

  1. 从 root 开始,找到目标 entry 所处的 leaf node, L
  2. 删除该 entry
  3. 如果 L 仍然至少处于半满状态,则操作结束;
    否则先尝试从 siblings 那里拆借 entries,如果失败,则将 L 与相应的 sibling 合并
  4. 如果合并发生了,则可能需要递归地删除 parent node 中的 entry

Clustered Indexes

Clustered Indexes 规定了 table 本身的物理存储方式,通常即按 primary key 排序存储,因此一个 table 只能建立一个 cluster index。有些 DBMSs 对每个 table 都添加聚簇索引,如果该 table 没有 primary key,则 DBMS 会为其自动生成一个;而有些 DBMSs 则不支持 clustered indexes。

B+ Tree Design Choice

Node Size

通常来说,disk 的数据读取速度越慢,node size 就越大, 具体情境下的最优大小由 workload 决定

Merge Threshold

某些 DBMS 在节点半满时并不总是合并节点。延迟合并操作可以减少重组量。最好只让较小的节点存在,然后定期重建整个树。也可以利用其它进程来负责周期性地重建 table index。

Variable Length Keys

  1. Pointers:存储指向 key 的指针
  2. Variable Length Nodes:索引中每个节点的大小可以不同,需 要精细的内存管理操作
  3. Padding:始终将Key填充到key类型的最大长度。
  4. Key Map / Indirection:内嵌一个指针数组,指向 node 中的 key/val list

Intra-node Search

  1. Linear : 从头到位Scan
  2. Binary : 二分搜索
  3. Interpolation : 根据key的分布信息估计key的大致位置来Scan

B+ 树实现

#include <iostream>
#include <vector>
#include <algorithm>

const int ORDER = 3;  // B+树的阶数

struct Node {
    bool is_leaf;                   // 是否是叶子节点
    std::vector<int> keys;          // 节点的键值
    Node* parent;                   // 父节点指针
    std::vector<Node*> children;    // 子节点指针

    // 构造函数
    Node(bool leaf, Node* parent = nullptr) : is_leaf(leaf), parent(parent) {}
};

class BPlusTree {
private:
    Node* root;

public:
    BPlusTree() {
        root = nullptr;
    }

    // 插入操作
    void insert(int key) {
        if (!root) {
            root = new Node(true);
            root->keys.push_back(key);
            return;
        }

        Node* node = root;
        while (!node->is_leaf) {
            int index = binary_search(node->keys, key);
            node = node->children[index];
        }

        insert_in_leaf(node, key);

        if (node->keys.size() >= ORDER) {
            split_leaf(node);
            if (node->parent && node->parent->keys.size() >= ORDER) {
                split_internal(node->parent);
            }
        }
    }

    // 删除操作
    void remove(int key) {
        if (!root) {
            return;
        }

        Node* node = root;
        while (!node->is_leaf) {
            int index = binary_search(node->keys, key);
            node = node->children[index];
        }

        if (!delete_entry(node, key)) {
            std::cout << "键值 " << key << " 不存在于树中。" << std::endl;
            return;
        }

        if (node->parent && node->keys.size() < (ORDER - 1) / 2) {
            coalesce_nodes(node);
        }
    }

    // 搜索操作
    void search(int key) {
        Node* node = root;
        while (node) {
            int index = binary_search(node->keys, key);
            if (index > 0 && node->keys[index - 1] == key) {
                std::cout << "找到键值 " << key << "!" << std::endl;
                return;
            }

            if (!node->is_leaf) {
                node = node->children[index];
            } else {
                break;
            }
        }

        std::cout << "未找到键值 " << key << "。" << std::endl;
    }

private:
    // 二分查找
    int binary_search(const std::vector<int>& keys, int key) {
        int low = 0;
        int high = keys.size() - 1;
        while (low <= high) {
            int mid = (low + high) / 2;
            if (keys[mid] == key) {
                return mid;
            } else if (keys[mid] < key) {
                low = mid + 1;
            } else {
                high = mid - 1;
            }
        }
        return low;
    }

    // 在叶子节点插入键值
    void insert_in_leaf(Node* leaf, int key) {
        leaf->keys.insert(std::upper_bound(leaf->keys.begin(), leaf->keys.end(), key), key);
    }

    // 分裂叶子节点
    void split_leaf(Node* leaf) {
        Node* new_leaf = new Node(true, leaf->parent);

        int mid_index = leaf->keys.size() / 2;

        // 移动一半的键值到新叶子节点
        for (int i = mid_index; i < leaf->keys.size(); i++) {
            new_leaf->keys.push_back(leaf->keys[i]);
        }
        leaf->keys.erase(leaf->keys.begin() + mid_index, leaf->keys.end());

        // 更新相邻叶子节点指针
        if (leaf->parent) {
            int parent_index = binary_search(leaf->parent->keys, new_leaf->keys[0]);
            leaf->parent->children.insert(leaf->parent->children.begin() + parent_index + 1, new_leaf);
        }

        // 将新叶子节点插入到父节点中
        if (leaf == root) {
            root = new Node(false);
            root->keys.push_back(new_leaf->keys[0]);
            root->children.push_back(leaf);
            root->children.push_back(new_leaf);
            leaf->parent = root;
            new_leaf->parent = root;
        } else {
            insert_in_parent(leaf->parent, new_leaf->keys[0], new_leaf);
        }
    }

    // 分裂内部节点
    void split_internal(Node* node) {
        Node* new_internal = new Node(false, node->parent);

        int mid_index = node->keys.size() / 2;

        // 移动一半的键值和子节点到新内部节点
        for (int i = mid_index + 1; i < node->keys.size(); i++) {
            new_internal->keys.push_back(node->keys[i]);
        }
        node->keys.erase(node->keys.begin() + mid_index, node->keys.end());

        for (int i = mid_index + 1; i < node->children.size(); i++) {
            new_internal->children.push_back(node->children[i]);
            node->children[i]->parent = new_internal;
        }
        node->children.erase(node->children.begin() + mid_index + 1, node->children.end());

        // 更新相邻叶子节点指针
        if (node->parent) {
            int parent_index = binary_search(node->parent->keys, new_internal->keys[0]);
            node->parent->children.insert(node->parent->children.begin() + parent_index + 1, new_internal);
        }

        // 将新内部节点插入到父节点中
        if (node == root) {
            root = new Node(false);
            root->keys.push_back(new_internal->keys[0]);
            root->children.push_back(node);
            root->children.push_back(new_internal);
            node->parent = root;
            new_internal->parent = root;
        } else {
            insert_in_parent(node->parent, new_internal->keys[0], new_internal);
        }
    }

    // 在父节点插入键值和子节点
    void insert_in_parent(Node* parent, int key, Node* child) {
        parent->keys.insert(std::upper_bound(parent->keys.begin(), parent->keys.end(), key), key);

        int child_index = std::distance(parent->children.begin(), std::find(parent->children.begin(), parent->children.end(), child));
        parent->children.insert(parent->children.begin() + child_index + 1, child);

        if (parent->keys.size() >= ORDER) {
            if (parent->parent) {
                split_internal(parent);
            } else {
                split_root(parent);
            }
        }
    }

    // 分裂根节点
    void split_root(Node* root) {
        Node* new_root = new Node(false);
        Node* left_child = new Node(root->is_leaf);
        Node* right_child = new Node(root->is_leaf);

        int mid_index = root->keys.size() / 2;
        int mid_key = root->keys[mid_index];

        // 移动键值和子节点到新的左右子节点
        if (!root->is_leaf) {
            for (int i = 0; i < mid_index; i++) {
                left_child->keys.push_back(root->keys[i]);
                left_child->children.push_back(root->children[i]);
                root->children[i]->parent = left_child;
            }
            left_child->children.push_back(root->children[mid_index]);
            root->children[mid_index]->parent = left_child;

            for (int i = mid_index + 1; i < root->keys.size(); i++) {
                right_child->keys.push_back(root->keys[i]);
                right_child->children.push_back(root->children[i]);
                root->children[i]->parent = right_child;
            }
            right_child->children.push_back(root->children.back());
            root->children.back()->parent = right_child;
        } else {
            for (int i = 0; i < mid_index; i++) {
                left_child->keys.push_back(root->keys[i]);
            }
            for (int i = mid_index; i < root->keys.size(); i++) {
                right_child->keys.push_back(root->keys[i]);
            }
        }

        // 更新新根节点的键值和子节点
        new_root->keys.push_back(mid_key);
        new_root->children.push_back(left_child);
        new_root->children.push_back(right_child);

        // 更新根节点指针
        this->root = new_root;
    }

    // 删除节点中的键值
    bool delete_entry(Node* node, int key) {
        auto it = std::find(node->keys.begin(), node->keys.end(), key);
        if (it != node->keys.end()) {
            node->keys.erase(it);

            // 更新相邻叶子节点指针
            if (node->is_leaf && node->parent) {
                int index = std::distance(node->parent->children.begin(), std::find(node->parent->children.begin(), node->parent->children.end(), node));
                if (index > 0 && index < node->parent->children.size() - 1) {
                    node->parent->children[index - 1]->siblings.push_back(node->parent->children[index + 1]);
                    node->parent->children[index + 1]->siblings.push_back(node->parent->children[index - 1]);
                } else if (index == 0 && node->parent->children.size() > 1) {
                    node->parent->children[1]->siblings.push_back(node->parent->children[0]);
                } else if (index == node->parent->children.size() - 1 && node->parent->children.size() > 1) {
                    node->parent->children[index - 1]->siblings.push_back(node->parent->children[index]);
                }
            }

            return true;
        }
        return false;
    }

    // 合并相邻叶子节点
    void coalesce_nodes(Node* node) {
        if (!node->parent) {
            adjust_root();
            return;
        }

        int index = std::distance(node->parent->children.begin(), std::find(node->parent->children.begin(), node->parent->children.end(), node));

        if (index > 0 && node->parent->children[index - 1]->keys.size() > (ORDER - 1) / 2) {
            redistribute_left(node->parent, index);
        } else if (index < node->parent->children.size() - 1 && node->parent->children[index + 1]->keys.size() > (ORDER - 1) / 2) {
            redistribute_right(node->parent, index);
        } else if (index > 0) {
            coalesce_with_left_sibling(node);
        } else {
            coalesce_with_right_sibling(node);
        }
    }

    // 调整根节点
    void adjust_root() {
        if (root->keys.empty() && !root->is_leaf) {
            Node* new_root = root->children[0];
            new_root->parent = nullptr;
            root = new_root;
            delete root->parent;
            root->parent = nullptr;
        }
    }

    // 将键值从左兄弟节点重新分配到当前节点
    void redistribute_left(Node* parent, int index) {
        Node* node = parent->children[index];
        Node* left_sibling = parent->children[index - 1];

        node->keys.insert(node->keys.begin(), parent->keys[index - 1]);
        parent->keys[index - 1] = left_sibling->keys.back();
        left_sibling->keys.pop_back();

        if (!node->is_leaf) {
            node->children.insert(node->children.begin(), left_sibling->children.back());
            left_sibling->children.pop_back();
        }
    }

    // 将键值从右兄弟节点重新分配到当前节点
    void redistribute_right(Node* parent, int index) {
        Node* node = parent->children[index];
        Node* right_sibling = parent->children[index + 1];

        node->keys.push_back(parent->keys[index]);
        parent->keys[index] = right_sibling->keys.front();
        right_sibling->keys.erase(right_sibling->keys.begin());

        if (!node->is_leaf) {
            node->children.push_back(right_sibling->children.front());
            right_sibling->children.erase(right_sibling->children.begin());
        }
    }

    // 与左兄弟节点合并
    void coalesce_with_left_sibling(Node* node) {
        Node* parent = node->parent;
        int index = std::distance(parent->children.begin(), std::find(parent->children.begin(), parent->children.end(), node));
        Node* left_sibling = parent->children[index - 1];

        // 合并键值和子节点
        left_sibling->keys.insert(left_sibling->keys.end(), node->keys.begin(), node->keys.end());

        // 更新相邻叶子节点指针
        if (node->is_leaf) {
            left_sibling->siblings.push_back(node->siblings.back());
        }

        // 更新父节点的键值和子节点指针
        parent->keys.erase(parent->keys.begin() + index - 1);
        parent->children.erase(parent->children.begin() + index);

        delete node;  // 释放被合并的节点

        if (parent->keys.size() < (ORDER - 1) / 2) {
            coalesce_nodes(parent);
        }
    }

    // 与右兄弟节点合并
    void coalesce_with_right_sibling(Node* node) {
        Node* parent = node->parent;
        int index = std::distance(parent->children.begin(), std::find(parent->children.begin(), parent->children.end(), node));
        Node* right_sibling = parent->children[index + 1];

        // 合并键值和子节点
        node->keys.insert(node->keys.end(), right_sibling->keys.begin(), right_sibling->keys.end());

        // 更新相邻叶子节点指针
        if (node->is_leaf) {
            node->siblings.push_back(right_sibling->siblings.back());
        }

        // 更新父节点的键值和子节点指针
        parent->keys.erase(parent->keys.begin() + index);
        parent->children.erase(parent->children.begin() + index + 1);

        delete right_sibling;  // 释放被合并的节点

        if (parent->keys.size() < (ORDER - 1) / 2) {
            coalesce_nodes(parent);
        }
    }
};

int main() {
    BPlusTree bptree;

    bptree.insert(10);
    bptree.insert(20);
    bptree.insert(5);
    bptree.insert(15);
    bptree.insert(25);

    bptree.search(10);
    bptree.search(15);
    bptree.search(25);
    bptree.search(30);

    bptree.remove(15);
    bptree.search(15);

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值