# Database Manager System **Repository Path**: chen-youxun/database-manager-system ## Basic Information - **Project Name**: Database Manager System - **Description**: This is a simple database manager system. - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-09-08 - **Last Updated**: 2025-09-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ======== 存储引擎进行标准数据库操作的完整示例 ==================== +-------------------------------------------------------------------+ | 上层应用 / 查询处理器 (Query Processor) | |-------------------------------------------------------------------| | v | |======================== Database (主 API 入口) ====================| |-------------------------------------------------------------------| | v | |========================== Catalog (元数据中心) =====================| | - 管理 Schema, TableHeap, 和【新增】Index 元数据 | |-------------------------------------------------------------------| | v (提供句柄) v (提供句柄) | | +---------------------+ +-----------------------------+ | | | TableHeap | | Index (B+ Tree) | | | | (存储原始数据) <--------+ (提供 Key -> RID 快速查找) | | | +---------------------+ +-----------------------------+ | | | | | | +----------------+---------------+ | | v | |==================== BufferManager (内存缓冲) ======================| | v | |===================== DiskManager (物理I/O) =======================| +-------------------------------------------------------------------+ import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; public class QuickStart { public static void main(String[] args) { Path dbPath = Paths.get("./production_db"); try (Database db = new Database(dbPath)) { // 1. 【创表 & 创索引】 TableHeap products; if (db.getTable("products") == null) { Schema schema = new Schema(Arrays.asList( new Column("id", DataType.INT), new Column("name", DataType.VARCHAR, 50) )); products = db.createTable("products", schema); db.createIndex("idx_prod_id", "products", "id"); System.out.println("Table 'products' and index 'idx_prod_id' created."); } else { products = db.getTable("products"); } // 2. 【增 & 同步索引】 System.out.println("\n--- Inserting and Syncing Index ---"); Schema schema = db.getSchema("products"); BPlusTree index = db.getIndex("idx_prod_id"); Tuple laptop = new Tuple(new Object[]{101, "Laptop"}, schema); // a. 插入数据到表 RID laptopRid = products.insertTuple(laptop); // b. 同步更新索引 index.insert(101, laptopRid); System.out.println("Inserted Laptop, synced index."); // 3. 【查 (使用索引)】 System.out.println("\n--- Using Index for Fast Search ---"); System.out.println("Searching for product with ID 101..."); RID foundRid = index.search(101); // 1. 索引查找 -> O(logN) assert foundRid != null; Tuple foundTuple = products.getTuple(foundRid); // 2. 精确获取数据 -> O(1) System.out.println("Found via Index: " + foundTuple); // 4. 【改 & 同步索引】 System.out.println("\n--- Updating and Syncing Index ---"); Tuple updatedLaptop = new Tuple(new Object[]{101, "Gaming Laptop"}, schema); // a. 更新表 (内部是 delete + insert) RID newRid = products.updateTuple(foundRid, updatedLaptop); // b. 同步更新索引 index.delete(101, foundRid); // 删除旧的索引条目 index.insert(101, newRid); // 插入新的索引条目 System.out.println("Updated Laptop, synced index. New RID is " + newRid); } catch (IOException e) { e.printStackTrace(); } } } 当前版本的存储引擎不会自动同步数据与索引。当您执行写入操作(增、删、改)时,您必须在应用层手动完成对 TableHeap 和 Index 的同步操作,以保证数据一致性。 插入操作 (INSERT) 的正确流程: 调用 table.insertTuple(tuple) 向表中插入数据,并获取返回的 RID。 从 tuple 中提取出被索引的列的键值 key。 调用 index.insert(key, rid) 将新条目添加到索引中。 删除操作 (DELETE) 的正确流程: 获取要删除记录的 RID 和被索引的 key。 调用 table.deleteRecord(rid) 从表中删除数据。 调用 index.delete(key, rid) 从索引中删除条目。 更新操作 (UPDATE) 的正确流程: 获取旧记录的 oldRid 和 oldKey。 调用 table.updateTuple(oldRid, newTuple),获取 newRid。 调用 index.delete(oldKey, oldRid) 删除旧的索引条目。 从 newTuple 中提取 newKey。 调用 index.insert(newKey, newRid) 插入新的索引条目。 ------------------ 2.核心概念 Schema:表的蓝图 Schema 对象定义了一张表的完整结构。把它想象成 Excel 的表头——它规定了有哪些列、每一列的名称、数据类型和顺序。 使用流程: 为表的每一列创建一个 Column 对象。 将这些 Column 对象放入一个列表中。 使用该列表创建一个 Schema 对象。 Java // 示例:创建一个 "products" 表的 Schema // 1. 定义列 Column colId = new Column("id", DataType.INT); Column colName = new Column("name", DataType.VARCHAR, 50); Column colPrice = new Column("price", DataType.INT); // 2. 创建列表 List columns = Arrays.asList(colId, colName, colPrice); // 3. 创建 Schema Schema productSchema = new Schema(columns); 这个 productSchema 对象现在就可以用来创建一张新表了。 2.2 Tuple:一行数据 如果说 Schema 是表头,那么 Tuple 就是表格中的一行具体数据。它是您与数据库交互时所使用的主要数据格式。 如何创建用于“插入”或“更新”的 Tuple: 从数据库获取目标表的 Schema,以确保结构匹配。 创建一个 Object[] 数组,数组中的值的顺序和类型必须与 Schema 中的列严格对应。 使用该数组和 Schema 实例化 Tuple。 Java // 示例:创建一个新的 product 元组 Schema schema = db.getSchema("products"); Object[] values = new Object[]{ 101, "Laptop", 800 }; // 对应 id, name, price Tuple newProduct = new Tuple(values, schema); // 现在 newProduct 可以被传入 table.insertTuple() 方法 如何使用从数据库“查询”到的 Tuple: 从 getTuple() 或 tupleIterator() 获取 Tuple 对象。 使用 getValue(columnIndex) 方法按列的索引(从0开始)提取数据。 将返回的 Object 强制类型转换为正确的类型。 使用 getRid() 方法获取该行数据的物理地址 RID,这对于后续的更新或删除操作至关重要。 Java // 示例:解析一个从数据库中查询到的 Tuple Tuple product = productsTable.getTuple(someRid); int id = (Integer) product.getValue(0); String name = (String) product.getValue(1); int price = (Integer) product.getValue(2); RID rid = product.getRid(); // <-- 获取这条记录的物理地址 System.out.println("Product " + id + ": " + name + ", costs $" + price); System.out.println("Its address in the database is: " + rid); ----------------------------- 3. 主API参考: Database 类 这是与数据库系统交互的顶层入口。 3.1 构造与关闭 方法签名 描述 Database(Path dbDirPath) 打开或创建位于 dbDirPath 目录的数据库。自动管理所有底层资源。 void close() 关闭数据库,将所有未保存的修改持久化到磁盘,并释放资源。(由 try-with-resources 自动调用) 3.2 表管理 方法签名 描述 Catalog createTable(String name, Schema schema) 在数据库中创建一个新表。参数: 表名,表的Schema。返回: 新表的TableHeap句柄。 Catalog getTable(String name) 获取已存在表的操作句柄 (TableHeap)。参数: 表名。返回: TableHeap实例或null。 Schema getSchema(String name) 获取表的Schema。参数: 表名。返回: Schema对象或null。 Catalog dropTable(String name) 删除表 3.3 索引管理 方法签名 描述 Database void createIndex(String iName, String tName, String cName) 创建一个新索引。参数: 索引名, 表名, 列名。此操作会自动扫描表中的存量数据并构建索引(批量加载)。 BPlusTree getIndex(String indexName) 根据名称获取一个索引的操作句柄 (BPlusTree)。如果索引不存在,返回 null。 Database List getIndexesForTable(String tName) 获取一张表上的所有索引的元数据。在需要同步更新所有索引时非常有用。 BPlusTree RID search(int key) 查找索引 void insert(int key, RID rid) 插入索引 delete(int key, RID rid) 删除索引 4. 表数据API参考: TableHeap 类 通过 db.getTable("tableName") 获取的实例,用于对特定表执行CRUD(增删查改)操作。它处理的是逻辑上的 Tuple 对象,完全隐藏了字节操作。 操作 方法签名 描述 增 RID insertTuple(Tuple tuple) 插入一个Tuple对象。返回:新记录的唯一标识符RID。 查 Tuple getTuple(RID rid) 根据RID精确查找并返回一个Tuple对象。 查 Iterator tupleIterator() 返回一个可以遍历表中所有Tuple的迭代器,支持增强for循环,每个turple都包含了RID 删 boolean deleteRecord(RID rid) 根据RID删除一条记录。 改 RID updateTuple(RID oldRid, Tuple newTuple) 5. 核心数据结构 (Core Data Structures) 类名 描述 Tuple 元组: 代表一行逻辑记录,由 Object[] 值和 Schema 构成。 Schema 模式: 定义一张表的结构(列的集合)。 Column 列: 定义一个列的属性:列名 (String)、数据类型 (DataType) 和长度。 DataType 数据类型: 枚举类型,目前支持 INT 和 VARCHAR。 RID 记录标识符: 由 (pageId, slotNum) 组成,是记录在数据库中的唯一物理地址。 6. 内部组件 (Internal Components) 以下组件构成了 Database old.API 的内部实现,上层应用无需直接与之交互。 组件名 职责 old.Catalog 内部元数据管理器,负责持久化表和Schema信息。 BufferManager 内部内存缓冲池,用于缓存磁盘页,提升性能。 DiskManager 内部物理I/O层,负责与磁盘文件进行数据块交换。 SlottedPage 内部页面格式化工具,用于在页面内组织记录。 TupleSerializer 内部序列化工具,负责Tuple与byte[]的转换。 Tuple创建 // 前提:'db' 是一个已初始化的 Database 对象 try { // 步骤 1: 从数据库获取该表准确的 Schema // 这是最佳实践,可以保证 Tuple 结构与表结构绝对一致。 Schema employeeSchema = db.getSchema("employees"); if (employeeSchema == null) { System.err.println("Table 'employees' not found."); return; } // 步骤 2: 准备一个 Object 数组,其顺序和类型必须与 Schema 中的列完全对应 // Schema: [Column("id", INT), Column("name", VARCHAR), Column("age", INT)] Object[] values = new Object[]{103, "Charlie", 28}; // 值的顺序: id, name, age // 步骤 3: 使用值和 Schema 实例化 Tuple 对象 Tuple newEmployeeTuple = new Tuple(values, employeeSchema); System.out.println("成功创建了一个新的 Tuple: " + newEmployeeTuple); // 现在,这个 newEmployeeTuple 对象就可以被用于插入或更新操作了 TableHeap employeesTable = db.getTable("employees"); employeesTable.insertTuple(newEmployeeTuple); System.out.println("新员工已成功插入数据库!"); } catch (IOException e) { e.printStackTrace(); } tuple使用 示例: 从 employees 表中获取并解析一个 Tuple。 try { TableHeap employeesTable = db.getTable("employees"); // 假设 charlieRid 是我们刚刚插入 "Charlie" 时获得的 RID // 1. 从数据库获取 Tuple 对象 Tuple retrievedTuple = employeesTable.getTuple(charlieRid); if (retrievedTuple != null) { // 2. 根据 Schema 的顺序,按索引提取值并进行类型转换 // Schema: [id (INT, index 0), name (VARCHAR, index 1), age (INT, index 2)] int id = (Integer) retrievedTuple.getValue(0); String name = (String) retrievedTuple.getValue(1); int age = (Integer) retrievedTuple.getValue(2); System.out.println("从 Tuple 中解析出的数据:"); System.out.println(" -> ID: " + id); System.out.println(" -> Name: " + name); System.out.println(" -> Age: " + age); } // 在迭代器中使用也一样 System.out.println("\n通过迭代器解析所有元组:"); for (Tuple tuple : employeesTable.tupleIterator()) { String name = (String) tuple.getValue(1); // 只获取姓名 System.out.println("发现员工: " + name); } } catch (IOException e) { e.printStackTrace(); }