Skip to content

Commit

Permalink
add database course lab0 contents (#506)
Browse files Browse the repository at this point in the history
### What problem were solved in this pull request?

Issue Number: close #xxx

Problem:

### What is changed and how it works?

### Other information
  • Loading branch information
nautaa authored Feb 21, 2025
1 parent 10035ac commit e31deeb
Show file tree
Hide file tree
Showing 19 changed files with 612 additions and 3 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ OPTION(CONCURRENCY "Support concurrency operations" OFF)
OPTION(STATIC_STDLIB "Link std library static or dynamic, such as libgcc, libstdc++, libasan" OFF)
OPTION(USE_SIMD "Use SIMD" OFF)
OPTION(USE_MUSL_LIBC "Use musl libc" OFF)
OPTION(WITH_CPPLINGS "Compile cpplings" ON)


MESSAGE(STATUS "HOME dir: $ENV{HOME}")
#SET(ENV{变量名} 值)
Expand Down
2 changes: 2 additions & 0 deletions docs/docs/db_course_lab/lab0.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ private:
MiniOB 中的单测框架使用 [GTest](https://github.com/google/googletest),在默认参数编译后,单测二进制程序位于 `$BUILD_DIR/bin/` 目录下,程序名与单测文件名对应。例如,`ob_bloomfilter_test.cpp` 对应的单测程序为 `$BUILD_DIR/bin/ob_bloomfilter_test`,通过运行该程序即可测试你的实现是否正确。
注意:如需运行单测,请移除单测名称中的 `DISABLED_` 前缀。
```bash
# 编译 miniob 代码
./build.sh debug --make
Expand Down
7 changes: 5 additions & 2 deletions docs/docs/db_course_lab/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ title: 数据库系统实现原理与实践课程实验
# 数据库系统实现原理与实践课程实验

这是数据库系统实现原理与实践课程的配套实验课程,本课程中所有的实验题目都将基于 [MiniOB](https://github.com/oceanbase/miniob) 完成。MiniOB 用 C++ 编写,是一个教学用途的数据库,其目标是为在校学生、数据库从业者、爱好者提供一个友好的数据库学习项目,更好地将理论、实践进行结合,提升同学们的工程实战能力。
这是 OceanBase 团队与华东师范大学联合开发的数据库系统实现原理与实践课程的配套实验课程,课程学习请参考:https://www.shuishan.net.cn/mooc/course/1888745117930643457。本课程中所有的实验题目都将基于 [MiniOB](https://github.com/oceanbase/miniob) 完成。MiniOB 用 C++ 编写,是一个教学用途的数据库,其目标是为在校学生、数据库从业者、爱好者提供一个友好的数据库学习项目,更好地将理论、实践进行结合,提升同学们的工程实战能力。

数据库系统实现原理与实践课程涵盖数据库系统的核心组件,包含存储,查询,事务等章节。该课程的实验项目完全公开,适合学习系统编程技能的高年级本科生,欢迎对数据库系统实现原理与实践感兴趣的同学参与学习。
本课程在学生了解数据库使用的前提下,学习数据管理系统(以关系数据库为主)在实现上的原理与关键技术。重点学习数据库系统的体系结构,包括数据库存储、查询、事务处理与数据库前沿知识四个方面,以及深入各方面包括如数据库索引,查询优化,并发控制,日志管理,等关键的原理与技术,为后续从事数据管理系统的研究和工程打下理论与实践基础,了解数据系统在设计与实践方面的分析、思维与学习方法。

该课程的实验项目完全公开,适合学习系统编程技能的高年级本科生,欢迎对数据库系统实现原理与实践感兴趣的同学参与学习。

## 实验题目
这是本课程的实验题目,实验题目是修改 MiniOB 的各个组件并完成指定的功能,实验题目会持续更新。
Expand Down Expand Up @@ -105,6 +107,7 @@ miniob >
2. 实验题目都需要单人独立完成,不允许组队完成。
3. 这门课程需要你有一定的 C++ 编程基础。
4. 这门课程并不是关于如何使用数据库(例如如何写 SQL 语句,如何基于数据库构建上层应用)的课程。
5. 实验文档中提供了较为详细的指导,请优先仔细阅读文档。文档无法做到囊括所有问题的程度,如果还有问题也请随时提问。对于文档中的**注意** 提示,请认真阅读。对于文档中的 **思考** 提示,请结合实验内容思考,**思考** 不是实验课程作业的一部分,仅供个人学习,同时也不提供答案。

## C++ 编程语言学习

Expand Down
4 changes: 4 additions & 0 deletions docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ nav:
- game/gitee-instructions.md
- game/miniob-vectordb.md
- game/git-introduction.md
- 数据库系统实现原理与实践:
- db_course_lab/overview.md
- db_course_lab/cloudlab_setup.md
- db_course_lab/lab0.md
- 数据库实现简明教程:
- lectures/copyright.md
- lectures/lecture-1.md
Expand Down
5 changes: 5 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,8 @@ ADD_SUBDIRECTORY(obclient)
if (WITH_MEMTRACER)
ADD_SUBDIRECTORY(memtracer)
endif()

if (WITH_CPPLINGS)
ADD_SUBDIRECTORY(cpplings)
endif()
ADD_SUBDIRECTORY(oblsm)
15 changes: 15 additions & 0 deletions src/cpplings/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
function(add_custom_executable target_name)
add_executable(${target_name} ${ARGN})

set_target_properties(${target_name} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/cpplings"
)
target_link_libraries(${target_name} PRIVATE pthread)
endfunction()

add_custom_executable(cas cas.cpp)
add_custom_executable(condition_variable condition_variable.cpp)
add_custom_executable(lambda lambda.cpp)
add_custom_executable(lock lock.cpp)
add_custom_executable(mutex mutex.cpp)
add_custom_executable(smart_pointer smart_pointer.cpp)
4 changes: 3 additions & 1 deletion src/cpplings/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

`cpplings` 包含一些关于 C++ 语言的代码练习,希望可以帮助大家学习和编写 C++ 代码,`cpplings` 的命名参考了 [rustlings](https://github.com/rust-lang/rustlings)。当然 C++ 语言的功能及特性过于庞大和复杂,cpplings 主要包含了在做 MiniOB 相关题目过程中可能用到的一些语法特性,并不能作为完整的 C++ 语言手册来学习。也非常欢迎大家提交 issue 或 PR 来贡献更多的练习题目。

在运行 `./build.sh debug` 后,该目录下每个文件都会生成一个与其对应的同名的可执行文件(位于 `build_debug/bin/cpplings` 目录下),运行该可执行文件即可查看该代码的输出结果。
在运行 `./build.sh debug -DENABLE_ASAN=OFF` 后,该目录下每个文件都会生成一个与其对应的同名的可执行文件(位于 `build_debug/bin/cpplings` 目录下),运行该可执行文件即可查看该代码的输出结果。

注意:由于 MiniOB 中会使用 ASAN 来检测内存错误,ASAN 的使用会导致代码执行变慢,练习中的例子无法很好的暴露出并发异常的问题,因此需要在编译时加上 ```-DENABLE_ASAN=OFF``` 来关闭 ASAN。也可以直接使用 g++ 来编译,例如对于 `lock.cpp` 可以通过 `g++ lock.cpp -o lock -g -lpthread` 来编译。

当你未实现文件中指定的 TODO 要求时,直接运行可执行文件会报错提示,例如:
```
Expand Down
67 changes: 67 additions & 0 deletions src/cpplings/cas.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved.
miniob is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details. */

// CAS (Compare And Swap) 操作通常通过 `std::atomic` 类型的成员函数 `compare_exchange_weak()`
// 或 `compare_exchange_strong()` 函数来实现。
// `compare_exchange_strong()` 的基本语义是比较一个原子变量的当前值与预期值,如果相等,则将其更新为新值。
// 如果不相等,则将原子变量的当前值赋值给预期值。这个操作是原子的,保证了线程安全。
// 详细用法可参考:https://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange

#include <iostream> // std::cout
#include <thread> // std::thread
#include <vector> // std::vector
#include <cassert> // assert

// 一个简单的链表节点
struct Node
{
int value;
Node *next;
};

Node *list_head(nullptr);

// 向 `list_head` 中添加一个值为 `val` 的 Node 节点。
void append_node(int val)
{
Node *old_head = list_head;
Node *new_node = new Node{val, old_head};

// TODO: 使用 compare_exchange_strong 来使这段代码线程安全。
list_head = new_node;
}

int main()
{
std::vector<std::thread> threads;
int thread_num = 50;
for (int i = 0; i < thread_num; ++i)
threads.push_back(std::thread(append_node, i));
for (auto &th : threads)
th.join();

// 注意:在 `append_node` 函数是线程安全的情况下,`list_head` 中将包含 50 个 Node 节点。
int cnt = 0;
for (Node *it = list_head; it != nullptr; it = it->next) {
std::cout << ' ' << it->value;
cnt++;
}
std::cout << '\n';
assert(cnt == thread_num);
std::cout << cnt << std::endl;

Node *it;
while ((it = list_head)) {
list_head = it->next;
delete it;
}
std::cout << "passed!" << std::endl;
return 0;
}
57 changes: 57 additions & 0 deletions src/cpplings/condition_variable.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved.
miniob is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details. */

// condition_variable(条件变量)是 C++11 中提供的一种多线程同步机制,
// 它允许一个或多个线程等待另一个线程发出通知,以便能够有效地进行线程同步。
// wait() 函数用于阻塞线程并等待唤醒,使用 std::unique_lock<std::mutex> 来等待(`void wait(unique_lock<mutex>& Lck);`)
// 如果条件变量当前不满足,线程将被阻塞,同时释放锁,使得其他线程可以继续执行。
// 当另一个线程调用 notify_one() 或 notify_all() 来通知条件变量时,被阻塞的线程将被唤醒,并再次尝试获取锁。
// wait() 函数返回时,锁会再次被持有。
// 详细用法可参考:https://en.cppreference.com/w/cpp/thread/condition_variable

#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
#include <cassert>

int count = 0;
int expect_thread_num = 10;
std::mutex m;
std::condition_variable cv;

// TODO: 每次调用会增加 count 的值,当count 的值达到 expect 的时候通知 waiter_thread
void add_count_and_notify()
{
std::scoped_lock slk(m);
count += 1;
}

void waiter_thread()
{
// TODO: 等待 count 的值达到 expect_thread_num,然后打印 count 的值
std::cout << "Printing count: " << count << std::endl;
assert(count == expect_thread_num);
}

int main()
{
int thread_num = expect_thread_num;
std::vector<std::thread> threads;
std::thread waiter(waiter_thread);
for (int i = 0; i < thread_num; ++i)
threads.push_back(std::thread(add_count_and_notify));
waiter.join();
for (auto &th : threads)
th.join();
std::cout << "passed!" << std::endl;
return 0;
}
47 changes: 47 additions & 0 deletions src/cpplings/lambda.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved.
miniob is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details. */

// lambda 表达式允许我们在另一个函数内定义匿名函数。
// 嵌套很重要,因为它使我们既可以避免名称空间命名污染,
// 又可以将函数定义为尽可能靠近其使用位置(提供额外的上下文)。
// Lambda 的形式如下:
// [ captureClause ] ( parameters ) -> returnType
// {
// statements;
// }
// 如果不需要 `capture`,则 `captureClause` 可以为空。
// 如果不需要 `parameters`,`parameters` 可以为空。除非指定返回类型,否则也可以完全省略它。
// `returnType` 是可选的,如果省略,则将假定为 auto(使用类型推导来确定返回类型)。

#include <iostream>
#include <vector>
#include <cassert>

int main()
{
std::vector<int> numbers = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};

// TODO: 定义一个 lambda 表达式来检查数字是否为偶数

// 使用 lambda 表达式来累计所有偶数的和
int even_sum = 0;
for (const auto &num : numbers) {
(void)(num);
// TODO: 在实现 lambda 表达式后将下面的注释取消注释
// if (is_even(num)) {
// even_sum += num;
// }
}

std::cout << "Sum of even numbers: " << even_sum << std::endl;
assert(even_sum == 12);
std::cout << "passed!" << std::endl;
return 0;
}
75 changes: 75 additions & 0 deletions src/cpplings/lock.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved.
miniob is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details. */

/*
lock_guard 是 C++11 中一个简单的 RAII(Resource Acquisition Is Initialization)风格的锁,
用于在作用域内自动管理互斥量的锁定和解锁。当 lock_guard 对象被创建时,它会自动锁定互斥量,
当对象离开作用域时,它会自动解锁互斥量。lock_guard 不支持手动锁定和解锁,也不支持条件变量。
unique_lock 是 C++11 中一个更灵活的锁,它允许手动锁定和解锁互斥量,以及与 condition_variable 一起使用。
与 lock_guard 类似,unique_lock 也是一个 RAII 风格的锁,当对象离开作用域时,它会自动解锁互斥量。
unique_lock 还支持 lock() 和 unlock() 等操作。
scoped_lock 是 C++17 引入的一个锁,用于同时锁定多个互斥量,以避免死锁。
scoped_lock 是一个 RAII 风格的锁,当对象离开作用域时,它会自动解锁所有互斥量。
scoped_lock 不支持手动锁定和解锁,也不支持条件变量。
它的主要用途是在需要同时锁定多个互斥量时提供简单且安全的解决方案。
*/

#include <iostream> // std::cout
#include <thread> // std::thread
#include <vector> // std::vector
#include <cassert> // assert

struct Node
{
int value;
Node *next;
};

Node *list_head(nullptr);

// 向 `list_head` 中添加一个 value 为 `val` 的 Node 节点。
void append_node(int val)
{
Node *old_head = list_head;
Node *new_node = new Node{val, old_head};

// TODO: 使用 scoped_lock/unique_lock 来使这段代码线程安全。
list_head = new_node;
}

int main()
{
std::vector<std::thread> threads;
int thread_num = 50;
for (int i = 0; i < thread_num; ++i)
threads.push_back(std::thread(append_node, i));
for (auto &th : threads)
th.join();

// 注意:在 `append_node` 函数是线程安全的情况下,`list_head` 中将包含 50 个 Node 节点。
int cnt = 0;
for (Node *it = list_head; it != nullptr; it = it->next) {
std::cout << ' ' << it->value;
cnt++;
}
std::cout << '\n';
assert(cnt == thread_num);
std::cout << cnt << std::endl;

Node *it;
while ((it = list_head)) {
list_head = it->next;
delete it;
}
std::cout << "passed!" << std::endl;
return 0;
}
Loading

0 comments on commit e31deeb

Please sign in to comment.