数据库索引
表索引
表索引是表属性子集的复制品,以便这些属性进行有效访问组织和/或排序。
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
-
Record/Tuple Ids(记录/元组标识):
在这种方式下,叶子节点存储的是记录或元组的标识,通常是记录在数据库中的主键或行号。实际的数据存储在其他位置,通过标识可以在需要时查找相应的数据。 -
Tuple Data(元组数据):
在这种方式下,叶子节点直接存储实际的元组数据,而不仅仅是标识。这意味着叶子节点包含了完整的数据,无需进行额外的IO操作。
B+ Tree Insert
- 找到对应的 leaf node,L
- 将 key/value pair 按顺序插入到 L 中
- 如果 L 还有足够的空间,操作结束;
如果空间不足,则需要将 L 分裂成两个节点,同时在 parent node 上新增 entry,
若 parent node 也空间不足,则递归地分裂,直到 root node 为止。
B+ Tree Delete
- 从 root 开始,找到目标 entry 所处的 leaf node, L
- 删除该 entry
- 如果 L 仍然至少处于半满状态,则操作结束;
否则先尝试从 siblings 那里拆借 entries,如果失败,则将 L 与相应的 sibling 合并 - 如果合并发生了,则可能需要递归地删除 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
- Pointers:存储指向 key 的指针
- Variable Length Nodes:索引中每个节点的大小可以不同,需 要精细的内存管理操作
- Padding:始终将Key填充到key类型的最大长度。
- Key Map / Indirection:内嵌一个指针数组,指向 node 中的 key/val list
Intra-node Search
- Linear : 从头到位Scan
- Binary : 二分搜索
- 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;
}