问题

大学生如何实现一个数据库?

回答
大学生如何实现一个数据库?

大学生实现一个数据库,这不仅仅是掌握一项技术,更是一个深入理解数据存储、管理和交互的绝佳机会。这个过程可以从简单到复杂,逐步深入。下面我将从概念、工具选择、具体实现步骤以及进阶学习等方面,详细阐述大学生如何实现一个数据库。

一、 理解数据库的核心概念

在动手之前,理解数据库的基本原理至关重要。

1. 什么是数据库?
数据库(Database,DB)是按照一定结构组织、存放数据的仓库。
它可以被看作是一个电子表格的集合,但功能远不止于此。
主要目的是高效地存储、检索、管理和更新数据。

2. 数据库管理系统(DBMS)
DBMS 是一个软件系统,用于创建、维护和使用数据库。
它充当用户和数据库之间的接口,负责数据的安全性、完整性、并发控制等。
常见的 DBMS 有:MySQL, PostgreSQL, SQL Server, Oracle, SQLite 等。

3. 数据模型
数据模型描述了数据如何组织和表示。
关系型模型 (Relational Model) 是目前最主流的模型,数据存储在表中,表之间通过关系(键)连接。
表 (Table):由行(记录)和列(字段/属性)组成。
行 (Row/Record):代表一个独立的数据项。
列 (Column/Field/Attribute):代表数据的某个属性。
主键 (Primary Key):唯一标识表中每一行的字段或字段组合。
外键 (Foreign Key):指向另一个表中主键的字段,用于建立表之间的关系。

4. SQL (Structured Query Language)
SQL 是用于管理关系型数据库的标准语言。
它用于查询、插入、更新和删除数据,以及创建和修改数据库结构。
DML (Data Manipulation Language):用于操作数据,如 `SELECT`, `INSERT`, `UPDATE`, `DELETE`。
DDL (Data Definition Language):用于定义数据库结构,如 `CREATE TABLE`, `ALTER TABLE`, `DROP TABLE`。

二、 选择合适的工具和技术栈

对于大学生而言,选择易于上手且功能强大的工具是关键。

1. 数据库类型选择:
关系型数据库 (RDBMS):
MySQL: 开源免费,社区庞大,功能强大,是学习和实践的首选。非常适合初学者。
PostgreSQL: 开源免费,功能比 MySQL 更强大,更符合 SQL 标准,适合更复杂的应用。
SQLite: 一个轻量级的嵌入式数据库,不需要单独安装服务器,数据直接存储在文件中。非常适合个人项目、移动应用或简单桌面应用。
非关系型数据库 (NoSQL):
MongoDB (文档数据库): 适合存储结构不固定的数据,如 JSON 文档。
Redis (键值数据库/缓存): 非常适合用作缓存、会话存储等。
Neo4j (图数据库): 适合存储和查询关系复杂的网络数据。

建议: 对于大学生初次实现数据库,强烈推荐从 MySQL 或 SQLite 开始学习。MySQL 提供了完整的客户端/服务器架构体验,SQLite 则更简单,可以直接嵌入到项目中。

2. 客户端工具选择:
MySQL:
MySQL Workbench: 官方提供的免费图形化管理工具,功能全面,包括数据库设计、SQL开发、服务器管理等。
DBeaver: 一个免费开源的通用数据库工具,支持多种数据库,功能强大且界面友好。
Navicat (付费,但有试用期或教育版): 非常流行的数据库管理工具,功能强大,用户体验好。
SQLite:
DB Browser for SQLite: 免费开源的 SQLite 数据库图形化管理工具,非常直观易用。

3. 编程语言集成:
你将需要一种编程语言来与数据库交互,执行 SQL 查询并将结果集成到你的应用程序中。
Python: 拥有强大的数据库连接库(如 `mysqlconnectorpython` for MySQL, `sqlite3` module for SQLite)和 ORM (ObjectRelational Mapping) 框架 (如 SQLAlchemy),非常适合快速开发。
Java: 使用 JDBC (Java Database Connectivity) API,并有 Hibernate, MyBatis 等 ORM 框架。
Node.js (JavaScript): 使用 `mysql` 或 `pg` (for PostgreSQL) 等库,以及 Sequelize, TypeORM 等 ORM。

建议: 如果你已经熟悉 Python,那么它将是与数据库交互的最佳选择。

三、 实现数据库的具体步骤(以 MySQL 为例)

这里以在本地搭建一个 MySQL 数据库并通过 Python 与之交互为例,来详细说明实现步骤。

步骤一:安装 MySQL 服务器

1. 下载 MySQL:
访问 MySQL 官网(dev.mysql.com/downloads/mysql/)。
选择适合你操作系统的 MySQL Community Server 版本。通常下载 MySQL Installer (Windows) 或通过包管理器安装 (macOS/Linux)。
2. 安装过程:
Windows: 运行 MySQL Installer,选择“Custom”安装类型,确保勾选 `MySQL Server` 和 `MySQL Workbench`。在安装过程中会让你设置 `root` 用户密码,请务必记牢。
macOS: 可以通过 Homebrew (`brew install mysql`) 或下载 DMG 文件安装。安装后需要启动 MySQL 服务。
Linux: 通常使用包管理器安装,例如 Ubuntu/Debian 使用 `sudo apt update && sudo apt install mysqlserver`。安装后需要启动服务并进行安全配置 (`sudo mysql_secure_installation`)。
3. 验证安装:
打开终端或命令提示符。
输入 `mysql u root p`,然后输入你设置的 `root` 密码。如果成功登录到 MySQL 命令行提示符 (`mysql>`),说明安装成功。

步骤二:使用 MySQL Workbench 设计和创建数据库结构

1. 连接到 MySQL 服务器:
打开 MySQL Workbench。
点击左上角的 `+` 号,创建一个新的数据库连接。
输入连接名称(例如,“MyProjectDB”)。
Hostname: `localhost` (如果 MySQL 在同一台机器上)
Port: `3306` (MySQL 默认端口)
Username: `root`
Password: 你设置的 `root` 密码。
点击 `Test Connection` 确认连接成功,然后点击 `OK` 保存。
双击新建的连接,即可进入数据库管理界面。

2. 创建数据库 (Schema):
在 MySQL Workbench 的左侧导航栏中,右键点击空白区域,选择 `Create Schema...`。
输入数据库名称(例如,“student_management”)。
点击 `Apply`。Workbench 会生成 `CREATE SCHEMA student_management` SQL 语句,再次点击 `Apply` 执行。
现在,你在左侧导航栏的“Schemas”列表中可以看到你的新数据库。选中你的数据库,这样后面的操作都会在这个数据库中进行。

3. 创建表 (Tables):
在左侧导航栏中,展开你的数据库,右键点击 `Tables`,选择 `Create Table...`。
设计一个例子: 假设我们要做一个简单的学生信息管理系统,需要 `students` 表和 `courses` 表,以及一个关联表 `student_courses` 来记录学生选修了哪些课程。

`students` 表:
列名 (Column Name) | 数据类型 (Data Type) | 主键 (PK) | 非空 (NN) | 唯一 (UQ) | 默认值 (Default) | 索引 (Index) | 自动递增 (AI)
`student_id` | `INT` | `YES` | `YES` | `YES` | `NULL` | `PRIMARY KEY` | `YES`
`name` | `VARCHAR(100)` | `NO` | `YES` | `NO` | `NULL` | `NO` | `NO`
`major` | `VARCHAR(50)` | `NO` | `NO` | `NO` | `NULL` | `NO` | `NO`
`enrollment_date` | `DATE` | `NO` | `NO` | `NO` | `NULL` | `NO` | `NO`

在 Workbench 的“Create Table”界面,逐个添加这些列。点击 `student_id` 列右侧的 `PK` 图标,将其设为主键;点击 `NN` 图标,将其设为非空;点击 `AI` 图标,将其设为自动递增。

`courses` 表:
列名 (Column Name) | 数据类型 (Data Type) | 主键 (PK) | 非空 (NN) | 唯一 (UQ) | 默认值 (Default) | 索引 (Index) | 自动递增 (AI)
`course_id` | `INT` | `YES` | `YES` | `YES` | `NULL` | `PRIMARY KEY` | `YES`
`course_name` | `VARCHAR(100)` | `NO` | `YES` | `NO` | `NULL` | `NO` | `NO`
`credits` | `INT` | `NO` | `NO` | `NO` | `NULL` | `NO` | `NO`

`student_courses` 表 (关联表):
列名 (Column Name) | 数据类型 (Data Type) | 主键 (PK) | 非空 (NN) | 唯一 (UQ) | 默认值 (Default) | 索引 (Index) | 自动递增 (AI)
`student_id` | `INT` | `YES` | `YES` | `NO` | `NULL` | `FK` | `NO`
`course_id` | `INT` | `YES` | `YES` | `NO` | `NULL` | `FK` | `NO`
`enrollment_date` | `DATE` | `NO` | `NO` | `NO` | `CURRENT_DATE` | `NO` | `NO`

设置联合主键和外键:
在 `student_courses` 表设计界面,选中 `student_id` 和 `course_id` 两列,点击工具栏中的 `PK` 图标,将它们设为联合主键。
在 `student_id` 列的“Foregin Key”一栏,选择 `FK_students` (如果之前创建 `students` 表时自动生成了,或者你需要手动创建一个)。点击右侧的按钮,弹出“Edit Foreign Key”窗口。在“Referenced Table”中选择 `students`,在“Referenced Column”中选择 `student_id`。
同理,为 `course_id` 列设置指向 `courses` 表 `course_id` 的外键。
点击 `Apply` 完成表创建。

4. SQL 语句创建表(可选,更灵活):
在 MySQL Workbench 的 SQL 标签页中,你可以直接编写 SQL 语句来创建数据库和表。
示例 SQL:
```sql
创建数据库 (如果不存在)
CREATE DATABASE IF NOT EXISTS student_management;

使用数据库
USE student_management;

创建 students 表
CREATE TABLE students (
student_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
major VARCHAR(50),
enrollment_date DATE
);

创建 courses 表
CREATE TABLE courses (
course_id INT AUTO_INCREMENT PRIMARY KEY,
course_name VARCHAR(100) NOT NULL,
credits INT
);

创建 student_courses 关联表
CREATE TABLE student_courses (
student_id INT,
course_id INT,
enrollment_date DATE DEFAULT CURRENT_DATE,
PRIMARY KEY (student_id, course_id), 联合主键
FOREIGN KEY (student_id) REFERENCES students(student_id) ON DELETE CASCADE, 外键关联 students 表,学生删除时,关联记录也删除
FOREIGN KEY (course_id) REFERENCES courses(course_id) ON DELETE CASCADE 外键关联 courses 表,课程删除时,关联记录也删除
);
```
编写完 SQL 后,点击工具栏上的闪电图标执行。

步骤三:在数据库中插入数据

1. 使用 Workbench 插入数据:
在左侧导航栏中,右键点击表名(如 `students`),选择 `Select Rows`。会生成 `SELECT FROM students` SQL 语句。执行后会显示一个数据编辑网格。
双击网格中的空白单元格可以编辑或输入新数据。输入完一行后按回车,会自动进入下一行。
点击工具栏的“Apply”按钮会生成 `INSERT` SQL 语句并执行。
2. 使用 SQL 插入数据:
```sql
插入学生数据
INSERT INTO students (name, major, enrollment_date) VALUES
('张三', '计算机科学', '20220901'),
('李四', '软件工程', '20220901'),
('王五', '网络空间安全', '20230901');

插入课程数据
INSERT INTO courses (course_name, credits) VALUES
('数据结构', 3),
('算法导论', 4),
('操作系统', 3);

关联学生和课程
INSERT INTO student_courses (student_id, course_id) VALUES
(1, 1), 张三 选修 数据结构
(1, 2), 张三 选修 算法导论
(2, 1), 李四 选修 数据结构
(2, 3), 李四 选修 操作系统
(3, 1), 王五 选修 数据结构
(3, 2), 王五 选修 算法导论
(3, 3); 王五 选修 操作系统
```

步骤四:使用 Python 与数据库交互

1. 安装 MySQL Connector/Python:
```bash
pip install mysqlconnectorpython
```
(如果你使用的是其他数据库如 PostgreSQL,则安装 `psycopg2` 等对应库)

2. 编写 Python 代码:
```python
import mysql.connector
from mysql.connector import Error

数据库连接配置
db_config = {
"host": "localhost",
"user": "root",
"password": "YOUR_ROOT_PASSWORD", 替换成你的 root 密码
"database": "student_management"
}

def create_connection():
"""创建数据库连接"""
connection = None
try:
connection = mysql.connector.connect(db_config)
if connection.is_connected():
print("成功连接到 MySQL 数据库")
except Error as e:
print(f"连接数据库时出错: {e}")
return connection

def execute_query(connection, query, params=None):
"""执行 SQL 查询,支持参数化查询以防 SQL 注入"""
cursor = connection.cursor()
try:
cursor.execute(query, params)
connection.commit() 提交事务 (对于 INSERT, UPDATE, DELETE)
print("查询执行成功")
return cursor 返回 cursor 以便获取插入 ID 等信息
except Error as e:
print(f"执行查询时出错: {e}")
connection.rollback() 发生错误时回滚
return None
finally:
if cursor:
cursor.close()

def fetch_data(connection, query, params=None):
"""执行查询并返回结果"""
cursor = connection.cursor(dictionary=True) dictionary=True 使结果为字典列表
try:
cursor.execute(query, params)
results = cursor.fetchall()
return results
except Error as e:
print(f"获取数据时出错: {e}")
return None
finally:
if cursor:
cursor.close()

def main():
connection = create_connection()

if connection:
示例:插入新学生并获取其 ID
new_student_query = "INSERT INTO students (name, major, enrollment_date) VALUES (%s, %s, %s)"
student_data = ('赵六', '人工智能', '20230901')
cursor = execute_query(connection, new_student_query, student_data)
if cursor:
new_student_id = cursor.lastrowid
print(f"新插入的学生 ID: {new_student_id}")

示例:查询所有学生及其选修的课程
query_student_courses = """
SELECT
s.name AS student_name,
c.course_name,
sc.enrollment_date
FROM
students s
JOIN
student_courses sc ON s.student_id = sc.student_id
JOIN
courses c ON sc.course_id = c.course_id
WHERE
s.student_id = %s
"""
student_id_to_query = 1 查询张三的课程
student_courses = fetch_data(connection, query_student_courses, (student_id_to_query,))

if student_courses:
print(f" 学生 (ID: {student_id_to_query}) 选修的课程:")
for row in student_courses:
print(f" 课程: {row['course_name']}, 选课日期: {row['enrollment_date']}")
else:
print(f"未找到 ID 为 {student_id_to_query} 的学生或其选修课程。")

示例:查询某门课程的学生
query_course_students = """
SELECT
s.name AS student_name,
s.major
FROM
students s
JOIN
student_courses sc ON s.student_id = sc.student_id
JOIN
courses c ON sc.course_id = c.course_id
WHERE
c.course_name = %s
"""
course_name_to_query = '数据结构'
course_students = fetch_data(connection, query_course_students, (course_name_to_query,))

if course_students:
print(f" 选修课程 '{course_name_to_query}' 的学生:")
for row in course_students:
print(f" 姓名: {row['student_name']}, 专业: {row['major']}")
else:
print(f"未找到选修课程 '{course_name_to_query}' 的学生。")


connection.close()
print("数据库连接已关闭")

if __name__ == "__main__":
main()
```
注意:
将 `YOUR_ROOT_PASSWORD` 替换为你自己的 MySQL root 密码。
`params=()` 中的 `()` 是为了确保传递的是一个元组,即使只有一个参数也要这样写。
`dictionary=True` 参数在 `connection.cursor()` 中非常有用,它使得 `fetchall()` 返回的结果是字典列表,而不是元组列表,更易于阅读和使用。

四、进阶学习与实践建议

1. SQL 进阶:
JOIN 语句: 熟练掌握 `INNER JOIN`, `LEFT JOIN`, `RIGHT JOIN`, `FULL OUTER JOIN`。
子查询 (Subqueries): 在 WHERE, SELECT, FROM 子句中使用子查询。
聚合函数 (Aggregate Functions): `COUNT`, `SUM`, `AVG`, `MAX`, `MIN` 结合 `GROUP BY` 和 `HAVING`。
窗口函数 (Window Functions): 例如 `ROW_NUMBER()`, `RANK()`, `LAG()`, `LEAD()` 等,用于更复杂的分析。
索引 (Indexes): 理解索引的作用,如何创建和优化,以提高查询性能。
视图 (Views): 创建虚拟表,简化复杂查询。
存储过程 (Stored Procedures) 和触发器 (Triggers): 在数据库服务器端执行代码,实现更复杂的业务逻辑和自动化。

2. 数据库设计原则:
范式 (Normalization): 了解 1NF, 2NF, 3NF 等,学习如何设计出减少数据冗余、避免更新异常的数据库结构。
ER 图 (EntityRelationship Diagram): 学习使用 ER 图来可视化和设计数据库结构。

3. ORM 框架:
一旦熟悉了原始 SQL,可以尝试学习 ORM 框架,如 Python 的 SQLAlchemy。ORM 可以将数据库表映射到 Python 类,让你用面向对象的方式操作数据库,减少编写 SQL 的工作量,并提供数据库抽象。

4. 其他数据库系统:
尝试使用 PostgreSQL 来体验其更强大的功能和更严格的标准。
学习 NoSQL 数据库,了解它们的应用场景和优势,例如用 MongoDB 构建一个简单的博客系统。

5. 项目实践:
将数据库应用到你的课程项目、个人网站、毕业设计或任何你感兴趣的项目中。例如:
学生成绩管理系统
图书借阅管理系统
简单的电商商品管理
个人博客后台
社交网络用户数据管理

五、总结

大学生实现一个数据库,最实际的路径是:

1. 理解核心概念: 数据库、DBMS、SQL、表、字段、主键、外键。
2. 选择合适工具: 推荐 MySQL + MySQL Workbench + Python。
3. 动手实践:
安装 MySQL 服务器。
使用 Workbench 设计和创建数据库及表。
插入和查询数据。
学习使用 Python 连接数据库,执行增删改查操作。
4. 深入学习: 掌握更高级的 SQL、数据库设计原则,并尝试其他数据库系统和 ORM 框架。

这个过程会让你对数据有更深刻的理解,并掌握一项非常重要的 IT 技能,为未来的学习和工作打下坚实的基础。祝你实践顺利!

网友意见

user avatar

工作两年回看,当时自豪的作品已变成了玩具;

不变的是对技术的好奇和热情;

18年底加入PingCAP,真正迈入了DB领域;

有兴趣的同学可看看TiDB系列文章,并贡献PR成为contributor,满足自己好奇心并为开源社区创造价值;

对PingCAP有兴趣的同学也可内推实习或者工作;


=====================

更新了一篇boltDB的模型和分析文档, 感兴趣的同学可以看看 github.com/qw4990/blog/

=====================

大概半年前, 我收到了这个问题的邀请.

现在我数据库完成, 我觉得我有底气回答这个问题了.

算是对我这将近一年工作的总结, 也当做是一些分享(zhuangX).



我大致说一下我从一开始做, 到完成我的作品, 大致经历了哪些阶段, 以做参考.

这里是我作品的github: GitHub - qw4990/NYADB2: NYADB2



PS:

因为大家对数据库的认知和了解都不同, 所以我的切入点必定无法满足所有人.

不过我想会关注这个问题的人, 大多应该都是用过简单的数据库功能, 感觉非常好奇, 想自己实现一个的人, 就同一年前的我一样.



下面我描述了我实现DB的各个阶段, 你可以在任意一个阶段停下, 然后实现它.



========================================================================================================================================



阶段1: 无事务, 单线程, 仅存在于内存的数据库.

该状态下的数据库, 其实就是一个”索引结构”+”语法分析器”.

语法分析器分析SQL语句, 然后根据逻辑, 去执行相应的操作.

索引结构则是用来快速查询.

由于该版本仅存在于内存, 所以只要你会一些常见的索引算法, 即可完成, 可以称之为”简易内存数据库”.

如你会B+树算法, 就可以实现一个B+树, Bt.

它实现了两个接口, Bt.Insert(key, value) -> void, Bt.Search(key) -> value.

再实现一个”语法分析器”.

如来了一条语句”Insert into student value (tony, 22, 123)”.

”语法分析器”分析该语句, 将value包裹一下, 选取一个该value的键值key.

然后调用 Bt.Insert(key, value).

之后执行”Read from student …” 其实也就是分析一下, 然后执行Bt.Search(key).

该版本数据库完成.



========================================================================================================================================



阶段2: 无事务, 单线程, 不可靠的磁盘数据库

“磁盘”表示该版本将信息存放在磁盘上.

“不可靠”表示, 当数据库被非正常结束时, 不保证重启后, 数据库内容还会正确.



2.1: 思路描述

该版本也非常简单, 直接在版本1上修改.

可以这样, 如你索引结构的最小单位为Unit, (如B+树的每个节点就是一个Unit).

你将Unit编码成二进制数据, 然后为每个Unit, 在某个文件中, 分配一段固定的空间, 用来存放它.

于是, 当你需要Unit的信息是, 你从该文件的固定位置读入.

当修改Unit的信息后, 你再将它写到那个固定位置.

如此一来, 数据就被存放于磁盘上了.



2.2: 实现

这里为B+树提供一种最简单的思路.

首先将索引数据和实际数据分别存放于两份文件, 称之为IndexFile, DB.

B+树有一个BALANCE_NUMBER, 简称BN, 为定值, 那么一个B+树节点最多有2*BN个(key, value)的键值对.

我们将key固定为uint64, value固定为uint64类型.

那么一个B+树节点最多占用(8+8)*2*BN这么多byte, 将其表示为MAX_BYTES.

于是, 就可以这样来编码B+树了.

规定根节点在IndexFile的位移为0.

每当创建新的节点时, 在IndexFile尾部, 追加MAX_BYTES大小的空间.

然后将该空间在IndexFile的位移, 作为这个新节点的”位置”, 用该空间存放新节点.

于是, B+树内部节点的value就用来存放”对应子节点的位置”.

叶节点的value, 也被作为”位置”, 指向了该条记录在DB中的位移.



2.3: 优化

上述实现会频繁的读写磁盘文件, 效率影响甚大.

为了解决这个问题, 可以加入一个模块, 这个模块分页管理IndexFile文件, 并对其进行必要的缓存, 以加快访问效率.

关于分页管理细节, 缓存算法, 不展开说了.




========================================================================================================================================



阶段3: 单事务, 单线程, 可靠的磁盘数据库

版本3在2的基础上, 同时支持了事务和数据库可靠性.



3.1: 关于事务

首先需要了解事务的基本概念, 参考<<数据库系统概念>>.

事务有ACID的性质, 由于现在是单线程版本, 所以不考虑其隔离性(I).

对于ACD这几个性质, 通常配合一定的”日志机制”完成.

于是需要去了解常见的”日志机制”.

这里推荐<<数据库系统概念>>日志恢复的那几章节.



3.2: 实现

有了”日志机制”, 具体实现的时候还要考虑一些更加细节的东西.

这里是Sqlite的一篇官文, 描述了一些错误会怎么发生, 应该对操作系统做什么样的假设.

不必了解该文档每个细节, 但是可以扩展下思路: How To Corrupt An SQLite Database File

这里是Sqlite官方介绍怎么实现原子性的文档: Atomic Commit In SQLite

同样不需要了解每个细节, 可以扩展下思路.



3.3: 个人总结

通常, 利用设计好的日志机制来保证事务的ACD性质.

然后利用对操作系统的一些假设, 来保证关键信息的原子性修改, 如数据库的”Boot”信息等.

如在我自己的实现中, 我就假设了操作系统的”rename”是原子性的.




========================================================================================================================================



阶段4: 多事务, 多线程, 可靠的数据库

前面三个阶段已经有一些内容了, 但是和多线程下的情况相比, 微不足道.




4.1: 可串行化调度

首先需要了解操作冲突的概念, 可串行化调度, 以及解决该问题的”两段锁协议”等, 推荐<<数据库系统概念>>.

两段锁协议会带来一个新的问题, 死锁.

于是, 你还需要去了解解决死锁的一些办法.

我使用的是有向图判环. <<数据库系统概念>>中有一定的介绍.




4.2: 解决读写冲突

使用”两段锁”能够完成可串行化调度, 但是它会造成”读写阻塞”, 很影响数据库的效率.

当然, 你也可以不解决该问题.

不过我借鉴了Postgresql, 引用了MVCC(多版本并发控制)来解决该问题.

MVCC的资料就大家自行搜索.

总体思路大致是: 为每条数据维护多个版本, 如果事务1锁定了该条数据, 而事务2准备读取的话, 就返回给事务2更老的版本.




4.3: 事务隔离度

还是得先了解隔离度的基本概念: 事务隔离

然后在MVCC的基础上, Postgresql通过维护各个版本对事务的可见性, 来实现了多种隔离度.

关于Postgresql怎么实现MVCC, 也请大家自行搜索, 或者直接看我的模型中的VM模块, 我借鉴了此方法.




4.4: 并发的索引

除了事务本身需要进行并发控制, 之前那些没考虑并发的模块, 也要加上并发支持.

其中最重要的一个就是并发的索引结构.

B+树本身是不支持并发访问的, 为了让他支持并发, 需要设计一些协议, 或者更改B+树算法来保证其支持并发.

我借鉴了一份文档的办法, 引入了这份B+树并发访问协议: Sixth Chapter

解决了该问题.




4.5: 总结

4.1到4.4大致说明了并发的情况下, 数据库会遇到哪些新的问题, 以及解决它们的办法.

虽然每个小节都只有几句话, 但是坑挺深, 每个问题都有各种各样的解决办法, 我只说了我使用到的.

但是, 比起单个解决这些问题, 最重要的, 是考虑怎么让它们组合起来使用也不会出错.

在组合这些方法的过程中, 你需要对这些方法做调整, 其实也就是设计并组合你自己的模块, 这非常重要, 也非常有趣.

如果想明白了上面各种方法怎么协同工作, 且发现不会引入新的问题, 那么可以把上面所有方法的总结抽象为一个完整”模型”了.

而下一步就是将这个”模型”实现.




========================================================================================================================================



阶段5: 实现版本4

想清楚了模型, 那么开始实现它.



5.1: 准备

首先需要肯定的是将会在编程中用到并发, 需要去了解一些常见的并发概念, 问题, 以及解决方法.

如临界区, 信号量, 锁, 读者写者问题, 哲学家就餐问题等概念.

接着你需要选择一门并发支持较好的语言(我选的是Go).

然后去学习该语言的一些并发编程技巧.




5.2: 开始编程

这个过程就没什么可以多说的了, 就是考验编程功底.

将模型抽象清楚, 然后开始写:)

我前后的尝试过程大致为:

  1. 一开始尝试用c++写个单线程版本, 后面放弃了.
  2. 用java实现了一个单线程版本, 总共大概约1200行.
  3. 尝试用java实现多线程版本, 最后放弃了.
  4. 用Go实现了一个多线程版本, 最后代码加注释大致10000行.
  5. 重构了Go的多线程版本, 得到现在的版本, 注释加代码大概7500行.

整个过程是痛苦并快乐的, 毫无疑问非常锻炼编程和抽象能力.


========================================================================================================================================

阶段6: 测试



6.1: 各模块测试

这里的测试包括分模块测试和整体测试.

你设计的各个模块之间, 应该是可以通过指定一些"协议"来解耦的.

于是模拟这些协议, 你的模块应该是可以单独被独立测试的.

如模块A对模块B的访问遵循了协议C.

现在你想单独测试模块B, 那么可以编写一个MockA, 模拟A的操作, 并且遵守协议C.

这样讲B和MockA一起测试.



6.2: 整体测试

其实我自己的DB在整体测试上做的是不够的.

目前我针对一些特定功能, 做了一些手动的测试.

关于更好的测试方法目前我也还在思考中.




========================================================================================================================================

其他问题:

实实在在的实现一个数据库当然还有其他很多问题, 如Server与Client的交互方法, 制定自己的SQL文法, 怎么有效优雅的解析SQL语句, 数据库运行状态的监控, 对日志文件进行自动归档等.

我上面描述的是”数据库引擎”需要解决的重点问题, 这些问题就略过了.

这些问题都是可以被作为"甜点", 在"主菜"完成后慢慢品尝的.

所以分清楚哪些问题是重点, 哪些问题是可以之后慢慢解决的也很重要.

总的来说, 只要你设计好了自己的"模型", "模型"之外的问题, 几乎都可以被作为"甜点"了.




========================================================================================================================================

总结:

数据库的功能点非常多, 选好要解决的问题, 然后去查找对应问题的解决办法.

接着将这些单个问题的解决办法, 组合成一个能正确工作的模型.

每个数据库都有自己的模型, 设计这个模型是数据库最好玩, 也是最难的地方, 这是"主菜".

将模型抽象好, 用合适的方法去将其实现, 这是难点二.

这个难点就没有多说的了, 就考验编程功底.

最后就是对数据库进行测试, 以及不断的完善.

则是"甜点"了.

所以数据库不管在理论, 还是工程上, 还是在考验人的耐心上, 真是都挺难啊哈哈= =.




========================================================================================================================================



一些可能会用到的资料推荐:

1.可以看一下简单的自动机实现, 用于分析语法.

2.B+树算法, 常见的缓存算法等, 推荐看wiki.

3.<<数据库系统概念>>, 这本书可以看看有关事务, 恢复, 锁的那几章, 以做基础概念.

4.<<inside sqlite>>, 这本书介绍了sqlite的后端模型, 原书非常短小, 大概80到100页.

5.http://www.sqlite.org/howtocorrupt.html, www3.sqlite.org/atomicc 这两篇Sqlite官方文档, 当做开阔思路.

6.<<SQLite Database System: Design and Implementation>>, 也是介绍Sqlite实现的书, 和<<inside sqlite>>有部分重复, 可以选看.

7.MVCC的相关文档以及Postgresql的可见性逻辑, 请自行谷歌.

8.然后, 就是我自己实现的数据库模型文档了: gitbook.com/book/qw4990

9.最后, 最重要的还是自己思考. 遇到一个问题, 解决一个问题.




========================================================================================================================================



本人很少上知乎, 虽然在知乎发了这篇文, 但还是鼓励大家多动手, 少上知乎. (会被和谐么?

最后, 感谢一直给予我帮助的左老师:)

类似的话题

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有