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

实现一个简单的Database1 作为一名开发人员,在工作中我每天都使用关系型数据库。但是对我来说,它们是一个黑盒。我有一些问题:数据存储使用什么格式?(在内存与磁盘中)数据什么时候从内存转移到磁盘?为什么每张表只能有一个主键?事务回滚是怎么工作的?索引是什么格式的?全表扫描时什么时候发生,如何发生的?预处理语句(prepared statement)是使用什么格式存储的?换句话说,数据库是怎么工作的?为了弄清楚这些,我从头写了一个数据库。 阅读详情
  • GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。
  • GreatSQL是MySQL的国产分支版本,使用上与MySQL一致。
  • 作者: 花家舍
  • 文章来源:GreatSQL社区原创

前文回顾


译注:cstack在github维护了一个简单的、类似sqlite的数据库实现,通过这个简单的项目,可以很好的理解数据库是如何运行的。本文是第十篇,主要是实现B-tree中叶子节点分裂

Part 10 叶子节点分裂

我们 B-Tree 只有一个节点,这看起来不太像一棵标准的 tree。为了解决这个问题,需要一些代码来实现分裂叶子节点。在那之后,需要创建一个内部节点,使其成为两个新的叶子节点的父节点。

基本上,我们这个系列的文章的目标是从这里开始的:

one-node btree

one-node btree

到这样:

two-level btree

two-level btree

首先中的首先,先把处理节点写满错误移除掉:

void leaf_node_insert(Cursor* cursor, uint32_t key, Row* value) {
  void* node = get_page(cursor->table->pager, cursor->page_num);

  uint32_t num_cells = *leaf_node_num_cells(node);
  if (num_cells >= LEAF_NODE_MAX_CELLS) {
    // Node full
-    printf("Need to implement splitting a leaf node.\n");
-    exit(EXIT_FAILURE);
+    leaf_node_split_and_insert(cursor, key, value);
+    return;
  }
ExecuteResult execute_insert(Statement* statement, Table* table) {
   void* node = get_page(table->pager, table->root_page_num);
   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);
   uint32_t key_to_insert = row_to_insert->id;

分裂算法(Splitting Algorithm)

简单的部分结束了。以下是我们需要做的事情的描述(出自:SQLite Database System: Design and Implementation) 原文:If there is no space on the leaf node, we would split the existing entries residing there and the new one (being inserted) into two equal halves: lower and upper halves. (Keys on the upper half are strictly greater than those on the lower half.) We allocate a new leaf node, and move the upper half into the new node. 翻译:如果在叶子节点中已经没有空间,我们需要将驻留在节点中的现有条目和新条目(正在插入)分成相等的两半:低半部分和高半部分(在高半部分中的键要严格大于低半部分中的键)。我们分配一个新的节点,将高半部分的条目移到新的节点中。

现在来处理旧节点并创建一个新的节点:

+void leaf_node_split_and_insert(Cursor* cursor, uint32_t key, Row* value) {
+  /*
+  Create a new node and move half the cells over.
+  Insert the new value in one of the two nodes.
+  Update parent or create a new parent.
+  */
+
+  void* old_node = get_page(cursor->table->pager, cursor->page_num);
+  uint32_t new_page_num = get_unused_page_num(cursor->table->pager);
+  void* new_node = get_page(cursor->table->pager, new_page_num);
+  initialize_leaf_node(new_node);

接下来,拷贝节点中每一个单元格到新的位置:

+  /*
+  All existing keys plus new key should be divided
+  evenly between old (left) and new (right) nodes.
+  Starting from the right, move each key to correct position.
+  */
+  for (int32_t i = LEAF_NODE_MAX_CELLS; i >= 0; i--) {
+    void* destination_node;
+    if (i >= LEAF_NODE_LEFT_SPLIT_COUNT) {
+      destination_node = new_node;
+    } else {
+      destination_node = old_node;
+    }
+    uint32_t index_within_node = i % LEAF_NODE_LEFT_SPLIT_COUNT;
+    void* destination = leaf_node_cell(destination_node, index_within_node);
+
+    if (i == cursor->cell_num) {
+      serialize_row(value, destination);
+    } else if (i > cursor->cell_num) {
+      memcpy(destination, leaf_node_cell(old_node, i - 1), LEAF_NODE_CELL_SIZE);
+    } else {
+      memcpy(destination, leaf_node_cell(old_node, i), LEAF_NODE_CELL_SIZE);
+    }
+  }

更新节点中头部标记的单元格的数量(更新node’s header)

+  /* Update cell count on both leaf nodes */
+  *(leaf_node_num_cells(old_node)) = LEAF_NODE_LEFT_SPLIT_COUNT;
+  *(leaf_node_num_cells(new_node)) = LEAF_NODE_RIGHT_SPLIT_COUNT;

然后我们需要更新节点的父节点。如果原始节点是一个根节点(root node),那么他就没有父节点。这种情况中,创建一个新的根节点来作为它的父节点。这里做另外一个存根(先不具体实现):

+  if (is_node_root(old_node)) {
+    return create_new_root(cursor->table, new_page_num);
+  } else {
+    printf("Need to implement updating parent after split\n");
+    exit(EXIT_FAILURE);
+  }
+}

分配新的页面(Allocating New Pages)

让我们回过头来定义一些函数和常量。当我们创建一个新的叶子节点,我们把它放进一个由get_unused_page_num()函数决定(返回)的页中。

+/*
+Until we start recycling free pages, new pages will always
+go onto the end of the database file
+*/
+uint32_t get_unused_page_num(Pager* pager) { return pager->num_pages; }

现在,我们假定在一个数据库中有N个数据页,页编码从 0 到 N-1 的页已经被分配。因此我们总是可以为一个新页分配页 N编码。在我们最终实现删除(数据)操作后,一些页可能会变成空页,并且他们的页编号可能没有被使用。为了更有效率,我们会回收这些空闲页。

叶子节点的大小(Leaf Node Sizes)

为了保持的树的平衡,我们在两个新的节点之间平等的分发单元格。如果一个叶子节点可以hold住 N 个单元格,那么在分裂期间我们需要分发 N + 1 个单元格在两个节点之间(N 个原有的单元格和一个新插入的单元格)。如果 N+1 是奇数,我比较随意地选择了左侧节点获取多的那个单元格。

+const uint32_t LEAF_NODE_RIGHT_SPLIT_COUNT = (LEAF_NODE_MAX_CELLS + 1) / 2;
+const uint32_t LEAF_NODE_LEFT_SPLIT_COUNT =
+    (LEAF_NODE_MAX_CELLS + 1) - LEAF_NODE_RIGHT_SPLIT_COUNT;

创建新根节点(Creating a New Root)

这里是“SQLite Database System”描述的创建一个新根节点的过程: 原文:Let N be the root node. First allocate two nodes, say L and R. Move lower half of N into L and the upper half into R. Now N is empty. Add 〈L, K,R〉 in N, where K is the max key in L. Page N remains the root. Note that the depth of the tree has increased by one, but the new tree remains height balanced without violating any B+-tree property. 翻译:设 N 为根节点。先分配两个节点,比如 L 和 R。移动 N 中低半部分的条目到 L 中,移动高半部分条目到 R 中。现在 N 已经空了。增加 〈L, K,R〉到 N 中,这里 K 是 L 中最大 key 。页 N 仍然是根节点。注意这时树的深度已经增加了一层,但是在没有违反任何 B-Tree 属性的情况下,新的树仍然保持了高度上平衡。

此时,我们已经分配了右子节点并移动高半部分的条目到这个子节点。我们的函数把这个右子节点作为输入,并且分配一个新的页面来存放左子节点。

+void create_new_root(Table* table, uint32_t right_child_page_num) {
+  /*
+  Handle splitting the root.
+  Old root copied to new page, becomes left child.
+  Address of right child passed in.
+  Re-initialize root page to contain the new root node.
+  New root node points to two children.
+  */
+
+  void* root = get_page(table->pager, table->root_page_num);
+  void* right_child = get_page(table->pager, right_child_page_num);
+  uint32_t left_child_page_num = get_unused_page_num(table->pager);
+  void* left_child = get_page(table->pager, left_child_page_num);

旧的根节点已经被拷贝到左子节点,所以我们可以重用根节点(无需重新分配):

+  /* Left child has data copied from old root */
+  memcpy(left_child, root, PAGE_SIZE);
+  set_node_root(left_child, false);

最后我们初始化根节点作为一个新的、有两个子节点的内部节点。

+  /* Root node is a new internal node with one key and two children */
+  initialize_internal_node(root);
+  set_node_root(root, true);
+  *internal_node_num_keys(root) = 1;
+  *internal_node_child(root, 0) = left_child_page_num;
+  uint32_t left_child_max_key = get_node_max_key(left_child);
+  *internal_node_key(root, 0) = left_child_max_key;
+  *internal_node_right_child(root) = right_child_page_num;
+}

内部节点格式(Internal Node Format)

现在我们终于创建了内部节点,我们就不得不定义它的布局了。它从通用 header 开始,然后是它包含的 key 的数量,接下来是它右边子节点的页号。内部节点的子节点指针始终比它的 key 的数量多一个。这个 子节点指针存储在 header 中。

+/*
+ * Internal Node Header Layout
+ */
+const uint32_t INTERNAL_NODE_NUM_KEYS_SIZE = sizeof(uint32_t);
+const uint32_t INTERNAL_NODE_NUM_KEYS_OFFSET = COMMON_NODE_HEADER_SIZE;
+const uint32_t INTERNAL_NODE_RIGHT_CHILD_SIZE = sizeof(uint32_t);
+const uint32_t INTERNAL_NODE_RIGHT_CHILD_OFFSET =
+    INTERNAL_NODE_NUM_KEYS_OFFSET + INTERNAL_NODE_NUM_KEYS_SIZE;
+const uint32_t INTERNAL_NODE_HEADER_SIZE = COMMON_NODE_HEADER_SIZE +
+                                           INTERNAL_NODE_NUM_KEYS_SIZE +
+                                           INTERNAL_NODE_RIGHT_CHILD_SIZE;

内部节点的 body 是一个单元格的数组,每个单元格包含一个子指针和一个 key 。每个 key 都必须是它的左边子节点中包含的最大 key 。

+/*
+ * Internal Node Body Layout
+ */
+const uint32_t INTERNAL_NODE_KEY_SIZE = sizeof(uint32_t);
+const uint32_t INTERNAL_NODE_CHILD_SIZE = sizeof(uint32_t);
+const uint32_t INTERNAL_NODE_CELL_SIZE =
+    INTERNAL_NODE_CHILD_SIZE + INTERNAL_NODE_KEY_SIZE;

基于这些常量,下边是内部节点布局看上去的样子:

Our internal node format

Our internal node format

注意我们巨大的分支因子(也就是扇出)。因为每个子节点指针/键对儿(child pointer / key pair)太小了,我们可以在每个内部节点中容纳 510 个键和 511 个子指针(也就是每个内部节点可以有510个子节点)。这意味着我们从来不用在查找 key 时遍历树的很多层。

# internal node layersmax # leaf nodesSize of all leaf nodes
0511^0 = 14 KB
1511^1 = 512~2 MB
2511^2 = 261,121~1 GB
3511^3 = 133,432,831~550 GB

实际上,我们不能在每个叶子节点中存储满 4KB 的数据,这是因为存储 header 、 keys 的开销和空间的浪费。 但是我们可以通过从磁盘上加载 4 个 pages (树高四层,每层只需检索一页)来检索大约 500G 的数据。这就是为什么 B-Tree 对数据库来说是很有用的数据结构。

下边是读取和写入一个内部节点的方法:

+uint32_t* internal_node_num_keys(void* node) {
+  return node + INTERNAL_NODE_NUM_KEYS_OFFSET;
+}
+
+uint32_t* internal_node_right_child(void* node) {
+  return node + INTERNAL_NODE_RIGHT_CHILD_OFFSET;
+}
+
+uint32_t* internal_node_cell(void* node, uint32_t cell_num) {
+  return node + INTERNAL_NODE_HEADER_SIZE + cell_num * INTERNAL_NODE_CELL_SIZE;
+}
+
+uint32_t* internal_node_child(void* node, uint32_t child_num) {
+  uint32_t num_keys = *internal_node_num_keys(node);
+  if (child_num > num_keys) {
+    printf("Tried to access child_num %d > num_keys %d\n", child_num, num_keys);
+    exit(EXIT_FAILURE);
+  } else if (child_num == num_keys) {
+    return internal_node_right_child(node);
+  } else {
+    return internal_node_cell(node, child_num);
+  }
+}
+
+uint32_t* internal_node_key(void* node, uint32_t key_num) {
+  return internal_node_cell(node, key_num) + INTERNAL_NODE_CHILD_SIZE;
+}

对于一个内部节点,最大 key 始终是其右键。对于一个叶子节点,最大 key 就是最大索引键。

+uint32_t get_node_max_key(void* node) {
+  switch (get_node_type(node)) {
+    case NODE_INTERNAL:
+      return *internal_node_key(node, *internal_node_num_keys(node) - 1);
+    case NODE_LEAF:
+      return *leaf_node_key(node, *leaf_node_num_cells(node) - 1);
+  }
+}

追踪根节点(Keeping Track of the Root)

我们终于在通用的节点 header 中使用了 is_root 字段。回调它是我们用它来决定怎样来分裂一个叶子节点:

    if (is_node_root(old_node)) {
        return create_new_root(cursor->table, new_page_num);
    } else {
        printf("Need to implement updating parent after split\n");
        exit(EXIT_FAILURE);
    }
}

下面是 getter & setter:

+bool is_node_root(void* node) {
+  uint8_t value = *((uint8_t*)(node + IS_ROOT_OFFSET));
+  return (bool)value;
+}
+
+void set_node_root(void* node, bool is_root) {
+  uint8_t value = is_root;
+  *((uint8_t*)(node + IS_ROOT_OFFSET)) = value;
+}

初始化这两种类型的节点(内部节点&叶子节点)应默认设置 is_root 为 false。

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

+void initialize_internal_node(void* node) {
+  set_node_type(node, NODE_INTERNAL);
+  set_node_root(node, false);
+  *internal_node_num_keys(node) = 0;
+}

当创建表的第一个节点时我们需要设置 is_root 为 true 。

// New database file. Initialize page 0 as leaf node.
void* root_node = get_page(pager, 0);
initialize_leaf_node(root_node);
+    set_node_root(root_node, true);
}

return table;

打印树(Printing the Tree)

为了帮助我们可视化数据库的状态,我们应该更新我们的 .btree 元命令以打印多级树。

我要替换当前的 print_leaf_node() 函数:

-void print_leaf_node(void* node) {
-  uint32_t num_cells = *leaf_node_num_cells(node);
-  printf("leaf (size %d)\n", num_cells);
-  for (uint32_t i = 0; i < num_cells; i++) {
-    uint32_t key = *leaf_node_key(node, i);
-    printf("  - %d : %d\n", i, key);
-  }
-}

实现一个递归函数,可以接受任何节点,然后打印它和它的子节点。它接受一个缩进级别作为参数,缩进级别每次在每次递归时会递增。我还在缩进中添加了一个很小的辅助函数。

+void indent(uint32_t level) {
+  for (uint32_t i = 0; i < level; i++) {
+    printf("  ");
+  }
+}
+
+void print_tree(Pager* pager, uint32_t page_num, uint32_t indentation_level) {
+  void* node = get_page(pager, page_num);
+  uint32_t num_keys, child;
+
+  switch (get_node_type(node)) {
+    case (NODE_LEAF):
+      num_keys = *leaf_node_num_cells(node);
+      indent(indentation_level);
+      printf("- leaf (size %d)\n", num_keys);
+      for (uint32_t i = 0; i < num_keys; i++) {
+        indent(indentation_level + 1);
+        printf("- %d\n", *leaf_node_key(node, i));
+      }
+      break;
+    case (NODE_INTERNAL):
+      num_keys = *internal_node_num_keys(node);
+      indent(indentation_level);
+      printf("- internal (size %d)\n", num_keys);
+      for (uint32_t i = 0; i < num_keys; i++) {
+        child = *internal_node_child(node, i);
+        print_tree(pager, child, indentation_level + 1);
+
+        indent(indentation_level + 1);
+        printf("- key %d\n", *internal_node_key(node, i));
+      }
+      child = *internal_node_right_child(node);
+      print_tree(pager, child, indentation_level + 1);
+      break;
+  }
+}

并更新对 print 函数的调用,传递缩进级别为零。

} else if (strcmp(input_buffer->buffer, ".btree") == 0) {
  printf("Tree:\n");
-    print_leaf_node(get_page(table->pager, 0));
+    print_tree(table->pager, 0, 0);
  return META_COMMAND_SUCCESS;

下面是一个对新的打印函数的测例

+  it 'allows printing out the structure of a 3-leaf-node btree' do
+    script = (1..14).map do |i|
+      "insert #{i} user#{i} person#{i}@example.com"
+    end
+    script << ".btree"
+    script << "insert 15 user15 person15@example.com"
+    script << ".exit"
+    result = run_script(script)
+
+    expect(result[14...(result.length)]).to match_array([
+      "db > Tree:",
+      "- internal (size 1)",
+      "  - leaf (size 7)",
+      "    - 1",
+      "    - 2",
+      "    - 3",
+      "    - 4",
+      "    - 5",
+      "    - 6",
+      "    - 7",
+      "  - key 7",
+      "  - leaf (size 7)",
+      "    - 8",
+      "    - 9",
+      "    - 10",
+      "    - 11",
+      "    - 12",
+      "    - 13",
+      "    - 14",
+      "db > Need to implement searching an internal node",
+    ])
+  end

新格式有点简化,所以我们需要更新现有的 .btree 测试:

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

这是新测试本身的 .btree 输出:

Tree:
- internal (size 1)
  - leaf (size 7)
    - 1
    - 2
    - 3
    - 4
    - 5
    - 6
    - 7
  - key 7
  - leaf (size 7)
    - 8
    - 9
    - 10
    - 11
    - 12
    - 13
    - 14

在缩进最小的级别,我们看到根节点(一个内部节点)。它输出的 size 为 1 因为它有一个 key 。缩进一个级别,我们看到叶子节点,一个 key ,和一个叶子节点。根节点中的 key (7)是第一个左子节点中最大的 key 。每个大于7的 key 存放在第二个子节点中。

一个主要问题(A Major Problem)

如果你一直密切关注,你可能会注意到我们错过了一些大事。看看如果我们尝试插入额外一行会发生什么:

db > insert 15 user15 person15@example.com
Need to implement searching an internal node

哦吼!是谁写的TODO信息?(作者在故弄玄虚!明明是他自己在 table_find() 函数中把内部节点搜索的功能存根的!)

下次我们将通过在多级树上实现搜索来继续史诗般的 B 树传奇。

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

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

技术交流群:

微信&QQ群: QQ群:533341697 微信群:添加GreatSQL社区助手(微信号:wanlidbc )好友,待社区助手拉您进群。

SQLite Database System Design and Implementation SQLite数据库系统设计与实现 英文原版PDF,非常好的SQLite源码学习文档 立即下载

相关推荐

DatabaseSystem:哈工大数据库实验

实验一 交互式SQL 导出整个数据库 mysqldump -u 用户名 -p 数据库名 > 导出的文件名 mysqldump -u dbuser -p dbname > dbname.sql 导入数据库 mysql -u root -p mysql>use 数据库 mysql>source d:/dbname.sql 实验二 嵌入式SQL(java) 运行环境 IDEA 2018 + Maven + tomcat 9 + Vue 实验三 MIS系统开发(java) 运行环境 IDEA 2018 + Maven + tomcat 9 实验四 数据库操作实现算法(C++) 实验内容 掌握B树索引查找算法,多路归并排序算法,并用高级语言实现 运行环境 VS 2015

SQLite Database System Design and Implemention Pager Module 概述

通过阅读本节,你应当能够解释解释以下问题: 1.page cache是什么?为什么需要它?谁在使用它? 2.一般的缓存管理技术 3.SQLite采用的事务处理过程以及回滚过程 本章讨论了pager模块,该模块在原生的字节文件上实现了抽象的数据库页文件系统,扮演着 固定大小的数据页面的管理者...

chuancu0042的博客 878

Database System Implementation 数据库系统实现英文版

Database System Implementation 数据库系统实现英文版 Database System Implementation 数据库系统实现英文版Database System Implementation 数据库系统实现英文版

如何学习sqlite源码

先来说说sqlite的资料. 首先sqlite的资料其实是不多不少的. 不少的原因是因为这些资料确实能够让你明白sqlite的设计原理. 说它不多的原因是也就让你明白其原理, 仅此而已. sqlite的资料主要来自四个地方, 从简到深依次为: 1.<>, 这是一本介绍sqlite"基本原理"的小书, 只有80页左右, 介绍了sqlite的"后端"原理. 这本书在百度有下载, 纯英文版...

qq_34849157的博客 964

Database Design and Implementation

A practical introduction using Oracle SQL Howard Gould

Database System Implementation 2nd and solution

This textbook was designed for a second course in the computer science database sequence at Stanford, and is preceded by A First Course in Database Systems , 1997. It covers the implementation of a DBMS, notably storage structure, query processing, and transaction management. Annotation c. Book News, Inc., Portland, OR

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

GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。 GreatSQL是MySQL的国产分支版本,使用上与MySQL一致。 前文回顾 实现一个简单的Database1(译文) 实现一个简单的Database2(译文) 实现一个简单的Database3(译文) 实现一个简单的Database4(译文) 译注:cstsck在github维护了一个简单的、类似SQLite的数据库实现,通过这个简单的项目,可以很好的理解数据库是如何运行的。本文是第五篇,主要是实现数据持久化 Part

GreatSQL2021的博客 460

Database Systems:Design, Implementation, & Management

Carlos Coronel, Steven Morris-Database Systems_ Design, Implementation, & Management, 2016, 12 edition

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

cstsck在github维护了一个简单的、类似SQLite的数据库实现,通过这个简单的项目,可以很好的理解数据库是如何运行的。本文是第四篇,主要是使用rspec对目前实现的功能进行测试并解决测试出现BUG 译注:cstsck在github维护了一个简单的、类似sqlite的数据库实现,通过这个简单的项目,可以很好的理解数据库是如何运行的。本文是第四篇,主要是使用r

GreatSQL2021的博客 300

Database system Design

美国数据库教材, An eleventh edition is a milestone that few textbooks achieve.We wrote the first edition of this book because we wanted to explain the complexity of database systems in a language that was easy for students to understand. Over the years, we have maintained this emphasis on reaching out to students to explain complex concepts in a practical, approachable manner. This book has been successful through ten editions because the authors, editors, and the pub- lisher paid attention to the impact of technology and to adopter questions and suggestions.We believe that this eleventh edition successfully reflects the same attention to such factors. In many respects, rewriting a book is more difficult than writing it the first time. If the book is successful, as this one is, a major concern is that the updates, inserts, and deletions will adversely affect writing style and continuity of coverage. The combination of superb reviewers and editors, plus a wealth of feedback from adopters and students of the previous editions, helped make this new edition the best yet.

ChipCamp探索系列 -- 7A. 开源CPU之BOOM的来龙去脉

本文梳理一下Berkeley的乱序超标量CPU项目----Berkeley Out of Order Machine (BOOM),作为探索7系的首篇(7A)。

ChipCamp的博客---https://chipcamp.blog.youkuaiyun.com/ 1558

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

前文回顾:实现一个简单的Database1(译文) 译注:cstsck在github维护了一个简单的、类似sqlite的数据库实现,通过这个简单的项目,可以很好的理解数据库是如何运行的。本文是第二篇,主要是实现数据库的前端组件,编译器与虚拟机部分功能 Part 2 世界上最简单的SQL编译器与虚拟机 我们正在实现一个sqlite的克隆版本。sqlite的前端是SQL编译器,编译器用来解析字符串并输出一个内部的表示,叫做字节码。 这些字节码被传到虚拟机(virtual machine),在虚拟机中,字节码将被

GreatSQL2021的博客 486

别再死记硬背了!用这5个Qt Design Studio小项目,彻底玩转核心组件

本文通过5个Qt Design Studio实战项目,帮助开发者深入理解核心组件的协作与应用。从动态登录界面到游戏化任务列表,每个项目都展示了组件属性与交互设计的巧妙结合,让你不再死记硬背,而是真正掌握Qt Design Studio的核心技能。

weixin_29202213的博客 271

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

cstsck在github维护了一个简单的、类似sqlite的数据库实现,通过这个简单的项目,可以很好的理解数据库是如何运行的。本文是第四篇,主要是使用rspec对目前实现的功能进行测试并解决测试出现BUG_

GreatSQL2021的博客 243

COLMAP在Ubuntu下的编译陷阱与实战避坑指南——从环境配置到三维重建全流程

本文详细介绍了在Ubuntu系统下编译和配置COLMAP三维重建工具的全流程,包括环境准备、依赖库安装、CUDA加速配置以及常见编译问题的解决方案。通过实战案例演示了从图像特征提取到稠密重建的完整步骤,并提供了性能优化和问题排查的实用技巧,帮助用户高效完成三维重建任务。

weixin_29003437的博客 1048

用SimaPro做碳足迹分析:手把手教你完成产品生命周期评估报告

本文详细介绍了如何使用SimaPro进行碳足迹分析,从软件安装到生命周期评估报告的完整流程。通过实战案例演示,帮助制造业企业、咨询机构和科研团队掌握产品生命周期评估(LCA)的核心技术,精准识别环境热点并优化决策,为可持续发展转型提供科学依据。

yhn45678901的博客 715

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

前文回顾 实现一个简单的Database1(译文) 实现一个简单的Database2(译文) 实现一个简单的Database3(译文) 译注:cstsck在github维护了一个简单的、类似sqlite的数据库实现,通过这个简单的项目,可以很好的理解数据库是如何运行的。本文是第三篇,主要是实现数据库的实现内存中的数据结构并存储数据 Part 3 在内存中,只追加的单表数据库 我们从一个小型的,有许多限制的数据库开始。现在数据库将: 支持两个操作:插入一行并打印所有行 数据驻留在内存中(没有持久化到磁盘

GreatSQL2021的博客 141

DEA各种模型原理及stata代码实现

DEA各种模型原理及stata代码实现DEA各种模型原理及stata代码实现一、CCR和BCC1.原理2.效率测算stata代码3.Malmquist指数3.1M指数3.2Global-Malmquist指数4.指数计算代码与案例二、SBM模型1.原理2.stata代码实现三、方向性距离函数(DDF)1.原理2.stata代码实现3.非径向DDF模型(NDDF)3.GML指数4.指数测算四、总结 DEA各种模型原理及stata代码实现 一、CCR和BCC 1.原理 CCR模型产出导向下的效率通过求解以下规划

wonder1322的博客 4万+

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

GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。 GreatSQL是MySQL的国产分支版本,使用上与MySQL一致。 作者: 花家舍 文章来源:GreatSQL社区原创 前文回顾 实现一个简单的Database系列 译注:cstack在github维护了一个简单的、类似sqlite的数据库实现,通过这个简单的项目,可以很好的理解数据库是如何运行的。本文是第十一篇,主要是实现递归搜索B-Tree Part 11 递归搜索B-Tree 上次我们在插入第15行数据报错的时

GreatSQL2021的博客 136

嵌入式:简单的service框架

在嵌入式中,我们获取一个资源、设置一个变量、更新数据的话最直接的方式就是直接调用一个函数。这样的写法也可以满足基本的功能,前提是这个系统是只要一个任务,只有一个while(1)如图1.1。图1.1当有两个或者两个以上的任务的时候,同时调用一个函数(设置一个状态值、操作同一个资源、操作指针等行为)的流程如图1.2。图1.2当两个任务同时操作一个资源的时候,就会有重入的问题,会导致想不到的异常问题(数据不对、指针异常等)。

stupid的博客 986

【GlobalMapper精品教程】094:GlobalMapper26简体中文版安装教程(附安装包下载)

【GlobalMapper精品教程】094:GlobalMapper26简体中文版安装教程(附安装包下载)- 基于深度学习的图像分析工具,用于自动识别土地覆盖分类、建筑物提取和车辆检测。新的太阳阴影分析工具,用于计算加载地形和 3D 要素的阴影。新的 GENERATE_SHADOWS 脚本命令允许自动执行这些计算。新增点云模型关键点自动识别。能够在自动点云分析工具中保存和加载自定义创建的要素模型。改进了像素到点中各种潜在重叠图像中 GCP 的自动识别。

智慧空间实验室 4894

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

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

GreatSQL2021的博客 124

OpenClaw 超级 AI 实战专栏【模型推理与实战】(二)官方预训练模型下载与使用

OpenClaw 预训练模型需按「固定目录结构」存放(模型文件夹包含权重 + 配置文件),否则会加载失败;核心流程:下载模型→验证文件完整性→加载模型→传入图片执行推理→可视化结果;常见问题集中在「路径错误」「显存不足」「文件缺失」,按文中方案可快速解决。

qq_36130719的博客 307

实现一个简单Database8(译文)

前文回顾 实现一个简单的Database系列 译注:cstack在github维护了一个简单的、类似sqlite的数据库实现,通过这个简单的项目,可以很好的理解数据库是如何运行的。本文是第八篇,主要是对B-tree的叶子节点格式的实现 Part 8 B-Tree叶子节点格式 我们准备把表的格式从非排序的数组格式行(rows)改成B-Tree。这是一个相当大变化,需要多个篇幅才能实现。在本文结束时,我们将定义叶子节点的布局,支持插入键值对儿到单节点的B-Tree。但是首先,来回顾一下把数据结构(从数组a

GreatSQL2021的博客 171

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

实现一个简单Database6

GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。 GreatSQL是MySQL的国产分支版本,使用上与MySQL一致。 前文回顾 实现一个简单的Database1(译文) 实现一个简单的Database2(译文) 实现一个简单的Database3(译文) 实现一个简单的Database4(译文) 实现一个简单的Database5(译文) 译注:cstsck在github维护了一个简单的、类似sqlite的数据库实现,通过这个简单的项目,可以很好的理解数据库是如何运行的。本

GreatSQL2021的博客 382

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

实现一个简单Database7

GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。 GreatSQL是MySQL的国产分支版本,使用上与MySQL一致。 前文回顾 实现一个简单的Database1(译文) 实现一个简单的Database2(译文) 实现一个简单的Database3(译文) 实现一个简单的Database4(译文) 实现一个简单的Database5(译文) 实现一个简单的Database6(译文) 译注:cstsck在github维护了一个简单的、类似sqlite的数据库实现,通过这个简单的

GreatSQL2021的博客 342

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

上一篇: 实现一个简单的Database9(译文)
下一篇: 故障案例:MySQL唯一索引有重复值,官方却说This is not a bug
GreatSQL社区
博客等级 码龄5年 1210粉丝 390原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值