数据库我是这样写出来的,Java版本2(支持数据类型),持续更新
了解数据库的内部原理其实很不容易,大部分的读写都停留在理论文章上,因此肖哥带着大家使用Java手写一个完整的数据库,让大家了解数据库的解析器、性能分析器、认证、查询优化器,执行引擎、存储引擎、事务管理、MVCC,数据恢复等一系列功能。这个工作量比较大,属于每日1-2更新,大家如果想了解数据库的内容原理,掌握数据库的核心技术,那么可以跟着肖哥的步骤一步一步的学习。数据库会包含大家熟悉的数据结构与算法例如 B+树索引 ,R树索引 等,仅数据存储就包含了数据块(Chunks)、文件头(File Header)、键值存储(MVMap)、并发控制、事务、序列化与反序列化、压缩、加密、索引、持久化存储、内存映射存储、分片机制、以及计划中的压缩和碎片整理等能力。
公号Solomon肖哥弹架构获取更多精彩内容 ,需要完整代码的可以留言
手写数据库持续更新系列 ,喜欢的读者 , 一定要订阅哦
欢迎 点赞,收藏,关注 。
完整的数据库具备能力图:
手写计划
- 小白能够看得懂的最简化版本数据库
- 标准SQL解析器
- 存储引擎
- 执行引擎
- 事务管理
- 日志系统
- 元数据管理
- 安全管理
- 性能分析器
- 网络版
- 标准JDBC接口对接
整体手写系列的大模块会包含以上功能。
支持数据类型版本
本案例它使用文件系统来实现数据的持久化,并引入了基本的查询优化和数据类型检查。
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;
class Table {
private List<Map<String, Object>> records = new ArrayList<>();
private Map<String, Integer> columnTypes = new HashMap<>();
public Table(List<String> columnNames, List<String> columnTypes) {
this.columnNames = new ArrayList<>(columnNames);
this.columnTypes.putAll(columnTypes.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> parseType(e.getValue()))));
}
private int parseType(String type) {
switch (type.toUpperCase()) {
case "INT":
return Types.INTEGER;
case "VARCHAR":
return Types.VARCHAR;
default:
throw new IllegalArgumentException("Unsupported type: " + type);
}
}
public void insert(Map<String, Object> record) {
if (record.keySet().equals(columnNames)) {
records.add(record);
} else {
throw new IllegalArgumentException("Column mismatch");
}
}
public List<Map<String, Object>> select(Condition condition) {
return records.stream()
.filter(record -> condition.test(record))
.collect(Collectors.toList());
}
public void saveToFile(String filename) throws IOException {
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(filename))) {
out.writeObject(new ArrayList<>(records));
}
}
public void loadFromFile(String filename) throws IOException, ClassNotFoundException {
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(filename))) {
records = (List<Map<String, Object>>) in.readObject();
}
}
interface Condition {
boolean test(Map<String, Object> record);
}
}
class SQLDatabase {
private Map<String, Table> tables = new HashMap<>();
private static final String DATA_DIR = "data";
public void createTable(String tableName, List<String> columnNames, List<String> columnTypes) {
Table table = new Table(columnNames, columnTypes);
tables.put(tableName, table);
new File(DATA_DIR).mkdirs();
table.saveToFile(DATA_DIR + "/" + tableName + ".dat");
}
public void insertInto(String tableName, Map<String, Object> record) {
Table table = tables.get(tableName);
if (table != null) {
table.insert(record);
table.saveToFile(DATA_DIR + "/" + tableName + ".dat");
} else {
System.out.println("Table not found: " + tableName);
}
}
public List<Map<String, Object>> selectFrom(String tableName, Table.Condition condition) {
Table table = tables.get(tableName);
if (table != null) {
return table.select(condition);
} else {
System.out.println("Table not found: " + tableName);
return Collections.emptyList();
}
}
public void executeSQL(String sql) {
// Simple SQL parser (naive implementation)
// This is just a placeholder for a real SQL parser
String[] parts = sql.trim().split("\s+");
switch (parts[0].toUpperCase()) {
case "CREATE":
if (parts[1].toUpperCase().equals("TABLE")) {
String tableName = parts[2];
List<String> columnNames = new ArrayList<>();
List<String> columnTypes = new ArrayList<>();
for (int i = 3; i < parts.length; i++) {
if (parts[i].toUpperCase().equals("(")) {
i++;
while (i < parts.length && !parts[i].toUpperCase().equals(")")) {
columnNames.add(parts[i]);
i++;
}
break;
}
}
createTable(tableName, columnNames, columnTypes);
}
break;
case "INSERT":
if (parts[1].toUpperCase().equals("INTO")) {
String insertTableName = parts[2];
Map<String, Object> record = new HashMap<>();
int valueIndex = parts[3].toUpperCase().equals("VALUES") ? 4 : 3;
for (int i = valueIndex; i < parts.length; i++) {
String[] keyValue = parts[i].split("=");
record.put(keyValue[0].trim(), parseValue(keyValue[1].trim()));
}
insertInto(insertTableName, record);
}
break;
case "SELECT":
if (parts[1].toUpperCase().equals("FROM")) {
String selectTableName = parts[2];
Table.Condition condition = null;
if (parts.length > 3) {
condition = record -> record.get(parts[3].split("=")[0].trim()).equals(parts[3].split("=")[1].trim());
}
List<Map<String, Object>> selectedData = selectFrom(selectTableName, condition);
selectedData.forEach(System.out::println);
}
break;
default:
System.out.println("Unsupported command: " + parts[0]);
}
}
private Object parseValue(String value) {
if (value.matches("-?\d+")) {
return Integer.parseInt(value);
} else {
return value;
}
}
}
// Database main class
public class Main {
public static void main(String[] args) {
SQLDatabase db = new SQLDatabase();
// 执行SQL语句
db.executeSQL("CREATE TABLE users (id INT, name VARCHAR(255), age INT);");
db.executeSQL("INSERT INTO users VALUES (1, 'Alice', 25);");
db.executeSQL("INSERT INTO users VALUES (2, 'Bob', 30);");
db.executeSQL("SELECT * FROM users WHERE age = 25;");
}
}
本案例实现了以下功能:
- 数据持久化:使用Java的序列化机制将表数据保存到文件中。
- 简单的SQL解析器:解析
CREATE TABLE
、INSERT INTO
和SELECT
语句。 - 条件查询:实现了基于单个条件的查询功能。
- 错误处理:在执行SQL语句时添加了基本的错误处理。
后续再次优化支持,暂时不支持如下:
- 它不支持完整的SQL语法,例如子查询、连接、聚合函数等。
- 查询优化非常基础,没有实现索引或更高级的查询优化技术。
- 错误处理很基础,没有实现完整的SQL异常处理。
- 数据类型支持有限,只支持整数和字符串。
转载自:https://juejin.cn/post/7386844718141980681