实现一个简单的Database9(译文)

实现B-tree二叉搜索与处理主键重复问题
本文介绍了如何在GreatSQL中改进B-tree的实现,使其支持有序存储和检测主键重复。通过修改`execute_insert`函数,实现了基于二叉搜索的正确插入位置查找,以及在发现主键重复时返回错误。同时,添加了`table_find`函数以搜索B树中的键。此外,文章还展示了处理内部节点的初步规划和错误处理的增强。
  • GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。
  • GreatSQL是MySQL的国产分支版本,使用上与MySQL一致。
  • 作者: 花家舍
  • 文章来源:GreatSQL社区原创

前文回顾


译注:cstack在github维护了一个简单的、类似sqlite的数据库实现,通过这个简单的项目,可以很好的理解数据库是如何运行的。本文是第九篇,主要是实现B-tree的二叉搜索并解决主键重复问题

Part 9 二叉搜索与主键重复

上次注意到我们的 B 树存储 key 时仍然是非排序的。现在我们来解决这个问题,并检测和拒绝主键的重复(插入)。

现在我们的 execute_insert() 函数在插入时,选择的位置是在表的结尾处。作为替换,我们需要搜索表(树)中正确的插入位置,然后把 key 插入到这个位置上。如果 key 在这个位置上已经存在,那么就返回(键重复)报错。

ExecuteResult execute_insert(Statement* statement, Table* table) {
   void* node = get_page(table->pager, table->root_page_num);
-  if ((*leaf_node_num_cells(node) >= LEAF_NODE_MAX_CELLS)) {
+  uint32_t num_cells = (*leaf_node_num_cells(node));
+  if (num_cells >= LEAF_NODE_MAX_CELLS) {
     return EXECUTE_TABLE_FULL;
   }

   Row* row_to_insert = &(statement->row_to_insert);
-  Cursor* cursor = table_end(table);
+  uint32_t key_to_insert = row_to_insert->id;
+  Cursor* cursor = table_find(table, key_to_insert);
+
+  if (cursor->cell_num < num_cells) {
+    uint32_t key_at_index = *leaf_node_key(node, cursor->cell_num);
+    if (key_at_index == key_to_insert) {
+      return EXECUTE_DUPLICATE_KEY;
+    }
+  }

   leaf_node_insert(cursor, row_to_insert->id, row_to_insert);

这样的话,我们就不再需要 table_end() 函数了。

-Cursor* table_end(Table* table) {
-  Cursor* cursor = malloc(sizeof(Cursor));
-  cursor->table = table;
-  cursor->page_num = table->root_page_num;
-
-  void* root_node = get_page(table->pager, table->root_page_num);
-  uint32_t num_cells = *leaf_node_num_cells(root_node);
-  cursor->cell_num = num_cells;
-  cursor->end_of_table = true;
-
-  return cursor;
-}

我们用一个方法替换 table_end() 函数,方法实现使用给定的 key 来搜索 Btree。

+/*
+Return the position of the given key.
+If the key is not present, return the position
+where it should be inserted
+*/
+Cursor* table_find(Table* table, uint32_t key) {
+  uint32_t root_page_num = table->root_page_num;
+  void* root_node = get_page(table->pager, root_page_num);
+
+  if (get_node_type(root_node) == NODE_LEAF) {
+    return leaf_node_find(table, root_page_num, key);
+  } else {
+    printf("Need to implement searching an internal node\n");
+    exit(EXIT_FAILURE);
+  }
+}

我先把内部节点实现的分支存根,因为我还没有实现内部节点(internal node)。我们可以在叶子节点中使用二叉搜索法来进行搜索。

+Cursor* leaf_node_find(Table* table, uint32_t page_num, uint32_t key) {
+  void* node = get_page(table->pager, page_num);
+  uint32_t num_cells = *leaf_node_num_cells(node);
+
+  Cursor* cursor = malloc(sizeof(Cursor));
+  cursor->table = table;
+  cursor->page_num = page_num;
+
+  // Binary search
+  uint32_t min_index = 0;
+  uint32_t one_past_max_index = num_cells;
+  while (one_past_max_index != min_index) {
+    uint32_t index = (min_index + one_past_max_index) / 2;
+    uint32_t key_at_index = *leaf_node_key(node, index);
+    if (key == key_at_index) {
+      cursor->cell_num = index;
+      return cursor;
+    }
+    if (key < key_at_index) {
+      one_past_max_index = index;
+    } else {
+      min_index = index + 1;
+    }
+  }
+
+  cursor->cell_num = min_index;
+  return cursor;
+}

这将返回:

  • key 的位置
  • 如果我们需要插入一个新的 key,需要移动 key 的话,返回这个 key 的位置
  • 超出最后一个 key 的位置

因为我们现在会检查节点的类型,所以需要一个函数在节点中获取和设置节点的类型值。

+NodeType get_node_type(void* node) {
+  uint8_t value = *((uint8_t*)(node + NODE_TYPE_OFFSET));
+  return (NodeType)value;
+}
+
+void set_node_type(void* node, NodeType type) {
+  uint8_t value = type;
+  *((uint8_t*)(node + NODE_TYPE_OFFSET)) = value;
+}

需要先把值转为 uint8_t 类型来确保它作为一个序列化的单字节。 还需要初始化节点的类型。

-void initialize_leaf_node(void* node) { *leaf_node_num_cells(node) = 0; }
+void initialize_leaf_node(void* node) {
+  set_node_type(node, NODE_LEAF);
+  *leaf_node_num_cells(node) = 0;
+}

最后,我们需要创建并处理一个新的错误码。

-enum ExecuteResult_t { EXECUTE_SUCCESS, EXECUTE_TABLE_FULL };
+enum ExecuteResult_t {
+  EXECUTE_SUCCESS,
+  EXECUTE_DUPLICATE_KEY,
+  EXECUTE_TABLE_FULL
+};
case (EXECUTE_SUCCESS):
  printf("Executed.\n");
  break;
+      case (EXECUTE_DUPLICATE_KEY):
+        printf("Error: Duplicate key.\n");
+        break;
case (EXECUTE_TABLE_FULL):
  printf("Error: Table full.\n");
  break;

有了这些修改,我们的测试可以更改为检查排序顺序:

"db > Executed.",
"db > Tree:",
"leaf (size 3)",
-      "  - 0 : 3",
-      "  - 1 : 1",
-      "  - 2 : 2",
+      "  - 0 : 1",
+      "  - 1 : 2",
+      "  - 2 : 3",
"db > "
])
end

并且我们能够为重复键增加一个新的测试:

+  it 'prints an error message if there is a duplicate id' do
+    script = [
+      "insert 1 user1 person1@example.com",
+      "insert 1 user1 person1@example.com",
+      "select",
+      ".exit",
+    ]
+    result = run_script(script)
+    expect(result).to match_array([
+      "db > Executed.",
+      "db > Error: Duplicate key.",
+      "db > (1, user1, person1@example.com)",
+      "Executed.",
+      "db > ",
+    ])
+  end

就是这样!接下来:实现拆分叶节点和创建内部节点。****

Enjoy GreatSQL 😃

关于 GreatSQL

GreatSQL是由万里数据库维护的MySQL分支,专注于提升MGR可靠性及性能,支持InnoDB并行查询特性,是适用于金融级应用的MySQL分支版本。

相关链接: GreatSQL社区 Gitee GitHub Bilibili

GreatSQL社区:

image

社区有奖建议反馈: https://greatsql.cn/thread-54-1-1.html

社区博客有奖征稿详情: https://greatsql.cn/thread-100-1-1.html

社区2022年度勋章获奖名单: https://greatsql.cn/thread-184-1-1.html

(对文章有疑问或者有独到见解都可以去社区官网提出或分享哦~)

随着信息技术在管理上越来越深入而广泛的应用,作为学校以及一些培训机构,都在用信息化战术来部署线上学习以及线上考试,可以与线下的考试有机的结合在一起,实现基于SSM的小码创客教育教学资源库的设计与实现在技术上已成熟。本文介绍了基于SSM的小码创客教育教学资源库的设计与实现的开发全过程。通过分析企业对于基于SSM的小码创客教育教学资源库的设计与实现的需求,创建了一个计算机管理基于SSM的小码创客教育教学资源库的设计与实现的方案。文章介绍了基于SSM的小码创客教育教学资源库的设计与实现的系统分析部分,包括可行性分析等,系统设计部分主要介绍了系统功能设计和数据库设计。 本基于SSM的小码创客教育教学资源库的设计与实现有管理员,校长,教师,学员四个角色。管理员可以管理校长,教师,学员等基本信息,校长角色除了校长管理之外,其他管理员可以操作的校长角色都可以操作。教师可以发布论坛,课件,视频,作业,学员可以查看和下载所有发布的信息,还可以上传作业。因而具有一定的实用性。 本站是一个B/S模式系统,采用Java的SSM框架作为开发技术,MYSQL数据库设计开发,充分保证系统的稳定性。系统具有界面清晰、操作简单,功能齐全的特点,使得基于SSM的小码创客教育教学资源库的设计与实现管理工作系统化、规范化。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值