实现简单数据库

学习意义

众所周知,无论是前端、后端,最终系统数据都会通过数据库系统进行读取和写入等操作,如下图所示,注册数据经过后端服务器最终进入数据库,因此可以认为数据库是系统的核心,特别是一旦涉及到高并发时,数据库可能成为系统的瓶颈,因此数据库的底层有必要深入学习,这里简单总结下基础数据库的底层实现,为后续深入学习数据库打下基础

image-20220726213828648

背景知识

存储&数据库

存储系统包括:

  • 块存储:存储软件栈里的底层系统,但接口过于朴素

  • 文件存储:使用最广泛的存储系统,接口友好,实现五花八门

  • 对象存储:公有云上的王牌产品,immutable语义加持

  • key-value存储:形式最灵活,存在大量的开源/黑盒产品

数据库系统包括:

  • 关系型:基于关系和关系代数构建,一般支持事务和SQL访问,使用体验友好的存储产品

  • 非关系型:结构和访问方式灵活,不同场景有不同的针对性产品

分布式架构包括:

  • 数据分布策略:决定了数据怎么分布到集群里的多个物理节点,是否均匀,是否能做到高性能

  • 数据复制协议:影响IO路径的性能、机器故障场景的处理方式

  • 分布式事务算法:多个数据库节点协同保障一个事务的ACID特性的算法,通常基于2pc的思想设计

数据库结构

一条SQL语句在数据库中执行过程如图:

image-20220726214740398

  1. 数据库接受客户端传的SQL语句文本
  2. 经过词法解析得到一组词条
  3. 经过语义解析得到语法树(Abstract Syntax Tree,AST)
  4. 经过语义解析得到表达式
  5. 经过规则优化(Rule-Based Optimization,RBO),主要是查询重写,表达式化简,谓词下推等
  6. 经过代价优化(Cost-Based Optimization,CBO)得最优查询表达式,即列举所有路径并计算各路径代价选择代价最小路径
  7. 构建逻辑计划再构建物理计划
  8. 执行期执行计划
  9. 返回结果

数据库包括三大引擎:

SQL引擎

  • Parser:查询解析,生成语法树,并进行合法性校验(词法、语法、语义)
  • Optimizer:根据语法树选择最优执行路径
  • Executor:查询执行流程,真实的对数据进行处理

以sql语句为例说明:

SELECT a FROM test WHERE a > 4;

词法解析,sql语句被切割成词条:

SELECTaFROMtestWHEREa>4

语法解析将词条序列组合成各类语法短句,与既定的语法规则匹配,若匹配成功则生成对应的抽象语法树,否则报语法错误,既定规则:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cWzeHMib-1658938296741)(https://blog-1258366838.cos.ap-nanjing.myqcloud.com/view)]

语法解析时,每次移到一个词条进行匹配,匹配上就规约操作,否则继续移,直到所有词条移完且成功规约则解析完毕,生成对应语法树。以上面词条为例:移到SELECT无规约且剩余词条,继续移到a,a可规约成tartet_list,用tartet_list替代词条a,移到FROM,继续移动到test规约成from_list替代词条test,然后from和from_list还可以规约成from_clause,继续规约移到WHERE,继续移到a规约成expr替代a,继续移到>,继续移进4可规约成expr替代4,此时expr>expr可规约成a_expr,用a_expr替代之,where和a_expr规约成where_clause,最终SELECT和target_list和from_clause和where_clause规约成simple_select_clause,解析完生成对应的语法树

语义分析是对语法树(AST)进行有效性审查,如表、列、列类型、函数、表达式等进行检查。继续上面为例会审查三个地方:

  1. from_clause:审查语句中的表test是否存在
  2. target_list:审查a列是否是from子句某个关系或视图的属性
  3. where_clause:审查a列是否是from子句某个关系或视图的属性且a列的类型是否能进行>4的比较操作

语义解析结束后会生成对应表达式供优化器使用

事务引擎

实现事务ACID四大特性

事务的概念:事务是个数据库操作命令序列,要么都执行要么不执行,是整体不可分割的逻辑单元,通过事务整体性保证数据一致性

事务的ACID特性:

  1. 原子性:事务不可再分割,事务中的操作要么都发生或都不发生

  2. 一致性:事务开始前和结束后,数据库的完整性约束没有被破坏

  3. 隔离性:并发环境中不同事务独立,不依赖于或影响其他事务

  4. 持久性:事务完成后,对数据库的更改便持久的保存在数据库之中,并不会被回滚

事务间的4个影响(间接):

  1. 脏读:一个事务读取另一个事务未提交的数据,而这个数据有可能回滚
  2. 不可重复读:一个事务内两个相同的查询却返回不同数据。是由于查询时系统中其他事务修改的提交引起
  3. 幻读:一事务对一表中的数据修改,修改涉及到表中全部数据行。同时,另一事务也向表中插入一行新数据。则操作前一事务的用户会发现表中还有没有修改的数据行,就象发生幻觉
  4. 丢失更新:两事务同时读取同一记录,A先改,B也改记录(B不知道A改过),B提交数据后B的修改结果覆盖A的结果

事务间的四大隔离:

隔离说明作用
未提交读read uncommitted读未提交的数据不解决脏读
提交读read committed读已提交的数据可解决脏读,Oracle和SQL Server默认隔离级别
可重复读repeatable read重复读取可解决脏读和不可重复读 ,mysql默认隔离级别
串行读serializable串行化相当于锁表,每次读写都需要获得表级共享锁,相互阻塞

事务控制语句:

  • 开始事务:BEGIN 或 START TRANSACTION,显式地开启一个事务
  • 提交事务:COMMIT 或 COMMIT WORK,对数据库进行的所有修改变为永久性的,set autocommit=0或1设置是否自动提交
  • 回滚:ROLLBACK 或 ROLLBACK WORK,会结束用户的事务,并撤销正进行的所有未提交的修改
    • 创建回滚点:SAVEPOINT S1,在事务中创建一回滚点s1,一事务中可有多个回滚点
    • 回滚到回滚点:ROLLBACK TO [SAVEPOINT] S1,把事务回滚到标记点s1

存储引擎

存储数据、索引、日志

存储引擎是数据库将数据存储在文件系统中的存储方式或格式,每种存储引擎都使用不同存储机制、索引技巧并最终提供不同功能,存储引擎处于文件系统上,在数据保存到数据文件前会传输到存储引擎,之后按各存储引擎的存储格式进行存储

系统设计

项目分解

如下图是整个系统的各模块之间的交互,主要有Parser、Optimizer、Executor等,具体来说主要功能包括:

image-20220726214331039

  • SQL引擎:
    • Parser:查询解析,生产语法树,并进行合法性校验
    • Optimizer:由语法树选择最优执行路径
    • Executor:基于火山模型查询执行流程
  • 事务引擎:支持事务提交和回滚机制
  • 存储引擎:
    • 数据结构设计
    • 索引结构设计

项目搭建

用CMake工具编写CMakeLists.txt文件,进行跨平台编译,参考链接,这里直接给出cmake文件

cmake_minimum_required(VERSION 3.8)
project(MyDB)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_COMPILER "g++")
set(CMAKE_CXX_FLAGS "-g -Wall -Werror -std=c++11")
set(CMAKE_CXX_FLAGS_DEBUG "-O0")
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG ")

set(CMAKE_INSTALL_PREFIX "install")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY
    ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY
    ${CMAKE_BINARY_DIR}/bin)

include_directories(${CMAKE_SOURCE_DIR}/sql-parser/include)

add_subdirectory(src/main)
add_subdirectory(src/sql-parser-test)

SQL引擎设计

Parser

SQL的解析非常繁琐,直接使用开源的SQL解析器,因此只需将其编译成库并包含其头文件即可

由于sql-parser库只提供词法分析和语法分析,生成如下图不同查询树,不能进行语义分析,也就是合法性校验,因此对sql-parser库进行封装,增加语义分析功能

image-20220727231638318

语义分析的核心逻辑是调用SQLParser::parse接口解析sql语句,并校验是否合法,调用checkStmtsMeta进行语法树语义分析,而该函数调用checkMeta根据不同语法树类别做不同校验

//语义分析
bool Parser::parseStatement(std::string query) {
  result_ = new SQLParserResult;
  SQLParser::parse(query, result_);//接口
  //校验
  if (result_->isValid()) {
    return checkStmtsMeta();
  } else {
    std::cout << "[BYDB-Error]  Failed to parse sql statement." << std::endl;
  }

  return true;
}

bool Parser::checkStmtsMeta() {
  for (size_t i = 0; i < result_->size(); ++i) {
    const SQLStatement* stmt = result_->getStatement(i);
    if (checkMeta(stmt)) {
      return true;
    }
  }

  return false;
}
//根据不同类别做不同校验
bool Parser::checkMeta(const SQLStatement* stmt) {
  switch (stmt->type()) {
    case kStmtSelect:
      return checkSelectStmt(static_cast<const SelectStatement*>(stmt));
    case kStmtInsert:
      return checkInsertStmt(static_cast<const InsertStatement*>(stmt));
    case kStmtUpdate:
      return checkUpdateStmt(static_cast<const UpdateStatement*>(stmt));
    case kStmtDelete:
      return checkDeleteStmt(static_cast<const DeleteStatement*>(stmt));
    case kStmtCreate:
      return checkCreateStmt(static_cast<const CreateStatement*>(stmt));
    case kStmtDrop:
      return checkDropStmt(static_cast<const DropStatement*>(stmt));
    case kStmtTransaction:
    case kStmtShow:
      return false;
    default:
      std::cout << "[BYDB-Error]  Statement type "
                << StmtTypeToString(stmt->type()) << " is not supported now."
                << std::endl;
  }

  return true;
}

这里以kStmtSelect为例简要说明,其他都差不多,具体可参考代码,若语句类型为select,则先获取表名,判断是否存在该表,否则继续判断select语句中的groupBy子句是否为空、是否支持UNION等操作,具体可参考代码,不在赘述

bool Parser::checkSelectStmt(const SelectStatement* stmt) {
  TableRef* table_ref = stmt->fromTable;
  Table* table = getTable(table_ref);
  if (table == nullptr) {
    std::cout << "[BYDB-Error]  Can not find table "
              << TableNameToString(table_ref->schema, table_ref->name)
              << std::endl;
    return true;
  }

  if (stmt->groupBy != nullptr) {
    std::cout << "[BYDB-Error]  Do not support 'Group By' clause" << std::endl;
    return true;
  }

  if (stmt->setOperations != nullptr) {
    std::cout << "[BYDB-Error]  Do not support Set Operation like 'UNION', "
                 "'Intersect', ect."
              << std::endl;
    return true;
  }

  if (stmt->withDescriptions != nullptr) {
    std::cout << "[BYDB-Error]  Do not support 'with' clause." << std::endl;
    return true;
  }

  if (stmt->lockings != nullptr) {
    std::cout << "[BYDB-Error]  Do not support 'lock' clause." << std::endl;
    return true;
  }

  if (stmt->selectList != nullptr) {
    for (auto expr : *stmt->selectList) {
      if (checkExpr(table, expr)) {
        return true;
      }
    }
  }

  if (stmt->whereClause != nullptr) {
    if (checkExpr(table, stmt->whereClause)) {
      return true;
    }
  }

  if (stmt->order != nullptr) {
    for (auto order : *stmt->order) {
      if (checkExpr(table, order->expr)) {
        return true;
      }
    }
  }

  if (stmt->limit != nullptr) {
    if (checkExpr(table, stmt->limit->limit)) {
      return true;
    }
    if (checkExpr(table, stmt->limit->offset)) {
      return true;
    }
  }

  return false;
}

Optimizer

根据产生的查询树,生成对应的计划树,计划树由各基础算子组成,针对本项目中要求的场景,构造了如下基础算子:

image-20220727232120461

同样,根据不同类型,生成不同计划树

Plan* Optimizer::createPlanTree(const SQLStatement* stmt) {
  switch (stmt->type()) {
    case kStmtSelect:
      return createSelectPlanTree(static_cast<const SelectStatement*>(stmt));
    case kStmtInsert:
      return createInsertPlanTree(static_cast<const InsertStatement*>(stmt));
    case kStmtUpdate:
      return createUpdatePlanTree(static_cast<const UpdateStatement*>(stmt));
    case kStmtDelete:
      return createDeletePlanTree(static_cast<const DeleteStatement*>(stmt));
    case kStmtCreate:
      return createCreatePlanTree(static_cast<const CreateStatement*>(stmt));
    case kStmtDrop:
      return createDropPlanTree(static_cast<const DropStatement*>(stmt));
    case kStmtTransaction:
      return createTrxPlanTree(static_cast<const TransactionStatement*>(stmt));
    case kStmtShow:
      return createShowPlanTree(static_cast<const ShowStatement*>(stmt));
    default:
      std::cout << "[BYDB-Error]  Statement type "
                << StmtTypeToString(stmt->type()) << " is not supported now."
                << std::endl;
  }
  return nullptr;
}

比如一条UPDATE查询,对应的UpdatePlan计划树如下:

image-20220727233639014

通过调用createUpdatePlanTree创建UpdatePlan计划树,代码如下:

Plan* Optimizer::createUpdatePlanTree(const UpdateStatement* stmt) {
  Table* table = g_meta_data.getTable(stmt->table->schema, stmt->table->name);
  Plan* plan;

  ScanPlan* scan = new ScanPlan();
  scan->type = kSeqScan;
  scan->table = table;
  plan = scan;

  if (stmt->where != nullptr) {
    Plan* filter = createFilterPlan(table->columns(), stmt->where);
    filter->next = plan;
    plan = filter;
  }

  UpdatePlan* update = new UpdatePlan();
  update->table = table;
  update->next = plan;

  for (auto upd : *stmt->updates) {
    size_t idx = 0;
    update->values.push_back(upd->value);
    for (auto col : *table->columns()) {
      if (strcmp(upd->column, col->name) == 0) {
        update->idxs.push_back(idx);
        break;
      }
      idx++;
    }
  }

  return update;
}

Executor

image-20220727234031773

依赖计划树生成对应的执行树,每个Plan生成一个对应的Operator,生成如图Operator,代码如下:

image-20220727234738946

BaseOperator* Executor::generateOperator(Plan* plan) {
  BaseOperator* op = nullptr;
  BaseOperator* next = nullptr;

  /* Build Operator tree from the leaf. */
  if (plan->next != nullptr) {
    next = generateOperator(plan->next);
  }

  switch (plan->planType) {
    case kCreate:
      op = new CreateOperator(plan, next);
      break;
    case kDrop:
      op = new DropOperator(plan, next);
      break;
    case kInsert:
      op = new InsertOperator(plan, next);
      break;
    case kUpdate:
      op = new UpdateOperator(plan, next);
      break;
    case kDelete:
      op = new DeleteOperator(plan, next);
      break;
    case kSelect:
      op = new SelectOperator(plan, next);
      break;
    case kScan: {
      ScanPlan* scan_plan = static_cast<ScanPlan*>(plan);
      if (scan_plan->type == kSeqScan) {
        op = new SeqScanOperator(plan, next);
      }
      break;
    }
    case kFilter:
      op = new FilterOperator(plan, next);
      break;
    case kTrx:
      op = new TrxOperator(plan, next);
      break;
    case kShow:
      op = new ShowOperator(plan, next);
      break;
    default:
      std::cout << "[BYDB-Error]  Not support plan node "
                << PlanTypeToString(plan->planType);
      break;
  }

  return op;
}

每个Operator调用next_.exec来调用下层Operator产生数据

class BaseOperator {
 public:
  BaseOperator(Plan* plan, BaseOperator* next) : plan_(plan), next_(next) {}
  ~BaseOperator() {}
  virtual bool exec() = 0;

  Plan* plan_;
  BaseOperator* next_;
};

事务引擎

不考虑并发,且数据无需落盘持久化,事务引擎设计就变得简单。不需实现MVCC机制,只需能实现事务Commit和Rollback功能即可

这里实现一个undo stack的机制,每次更新一行数据,就把这行数据老的版本push到undo stack中。如果事务回滚,那么就从undo stack中把老版本的数据逐个pop出来,恢复到原有的数据中去

image-20220727235057195

事务定义如下,有三种类型kInsertUndo、kDeleteUndo、kUpdateUndo::

enum UndoType { kInsertUndo, kDeleteUndo, kUpdateUndo };

struct Undo {
  Undo(UndoType t)
      : type(t), tableStore(nullptr), curTup(nullptr), oldTup(nullptr) {}
  ~Undo() {
    if (type == kUpdateUndo) {
      free(oldTup);
    }
  }

  UndoType type;
  TableStore* tableStore;
  Tuple* curTup;
  Tuple* oldTup;
};

class Transaction {
 public:
  Transaction() : inTransaction_(false) {}
  ~Transaction() {}
......

  void begin();
  void rollback();
  void commit();

  bool inTransaction() { return inTransaction_; }

 private:
  bool inTransaction_;
  std::stack<Undo*> undoStack_;
};

extern Transaction g_transaction;

commitrollback代码如下:

void Transaction::rollback() {
  while (!undoStack_.empty()) {
    auto undo = undoStack_.top();
    TableStore* table_store = undo->tableStore;
    undoStack_.pop();
    switch (undo->type) {
      case kInsertUndo:
        table_store->removeTuple(undo->curTup);
        break;
      case kDeleteUndo:
        table_store->recoverTuple(undo->oldTup);
        break;
      case kUpdateUndo:
        memcpy(undo->curTup->data, undo->oldTup->data,
               table_store->tupleSize() - TUPLE_HEADER_SIZE);
        break;
      default:
        break;
    }
    delete undo;
  }
  inTransaction_ = false;
}

void Transaction::commit() {
  while (undoStack_.empty()) {
    auto undo = undoStack_.top();
    TableStore* table_store = undo->tableStore;
    undoStack_.pop();
    if (undo->type == kDeleteUndo) {
      table_store->freeTuple(undo->oldTup);
    }
    delete undo;
  }
  inTransaction_ = false;
}

存储引擎

数据结构

因为是内存态数据库,所以数据结构设计简单。每次申请一批记录内存,降低内存碎片化,提高内存效率。然后将这批记录的内存放到FreeList中。数据插入时,从FreeList中获取一块内存写入,并放入DataList。数据删除时,将数据从DataList归还到FreeList中,为方便这里使用双向链表

struct Tuple {
  Tuple* prev;
  Tuple* next;
  uchar data[];
};

class TupleList {
 public:
  TupleList() {
    head_ = static_cast<Tuple*>(malloc(sizeof(Tuple)));
    tail_ = static_cast<Tuple*>(malloc(sizeof(Tuple)));
    head_->next = tail_;
    tail_->prev = head_;
    head_->prev = nullptr;
    tail_->next = nullptr;
  }

......

 private:
  Tuple* head_;
  Tuple* tail_;
};

class TableStore {
 public:
  TableStore(std::vector<ColumnDefinition*>* columns);
  ~TableStore();
......
  TupleList freeList_;
  TupleList dataList_;
};

索引设计

因为这里只要求实现等值匹配,所以用最简单的hash索引

总结

简单实现一个数据库原型,还非常粗糙,但对初学数据库底层原理还是很有帮助,能够对数据库原理有个整体概念,实际代码参考链接

  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[FK_Borrow_Info_Book_Info]') and OBJECTPROPERTY(id, N'IsForeignKey') = 1) ALTER TABLE [dbo].[Borrow_Info] DROP CONSTRAINT FK_Borrow_Info_Book_Info GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[FK_Borrow_Info_Proof_Info]') and OBJECTPROPERTY(id, N'IsForeignKey') = 1) ALTER TABLE [dbo].[Borrow_Info] DROP CONSTRAINT FK_Borrow_Info_Proof_Info GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Admin_Info]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [dbo].[Admin_Info] GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Book_Info]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [dbo].[Book_Info] GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Borrow_Info]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [dbo].[Borrow_Info] GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Proof_Info]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [dbo].[Proof_Info] GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Punish_Info]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [dbo].[Punish_Info] GO CREATE TABLE [dbo].[Admin_Info] ( [User_Name] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL , [Password] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL , [WorkID] [float] NOT NULL ) ON [PRIMARY] GO CREATE TABLE [dbo].[Book_Info] ( [Book_ID] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL , [Book_Name] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL , [Writer] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL , [Press] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL , [Price] [float] NOT NULL , [InLibrary_Date] [smalldatetime] NOT NULL , [Total_Amount] [int] NOT NULL , [Now_Amount] [int] NOT NULL ) ON [PRIMARY] GO CREATE TABLE [dbo].[Borrow_Info] ( [BorrowID] [int] NOT NULL , [Proof_ID] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL , [Book_ID] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL , [Borrow_Date] [smalldatetime] NOT NULL ) ON [PRIMARY] GO CREATE TABLE [dbo].[Proof_Info] ( [Proof_ID] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL , [Name] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL , [Sex] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL , [Birth_Time] [smalldatetime] NOT NULL , [Address] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL , [ID_Number] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL , [Tel_Number] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL , [Now_Borrow_Amount] [int] NOT NULL ) ON [PRIMARY] GO CREATE TABLE [dbo].[Punish_Info] ( [Puni_ID] [int] NOT NULL , [Proof_ID] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL , [Book_ID] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL , [Borrow_Date] [smalldatetime] NOT NULL , [Return_Date] [smalldatetime] NOT NULL , [Puni_Money] [float] NOT NULL ) ON [PRIMARY] GO Insert into Book_Info values ('10001', '数据库原理','杜胜胜','中国地质大学大学',50,'2009-11-17 0:17:00',5,5) Insert into Proof_Info values (1, '童红兵','Male','2009-11-17 0:17:00','武汉','1','027-87433110',0) Insert into Borrow_Info values (1, 1,10001,'2009-11-17 0:17:00') Insert into Punish_Info values (1, 1,10001,'2009-11-17 0:17:00','2005-11-17 0:17:00',10)
作者: Hector Garcia-Molina, Jeffrey D. Ullman, Jennifer Widom 本书是斯坦福大学计算机科学专业数据库系列课程第二门课的教科书。书中对数据库系统实现原理进行了深入阐述,并具体讨论了数据库管理系统的三个主要成分—存储管理器、查询处理器和事务管理器的实现技术。书中还对信息集成的最新技术,例如数据仓库、OLAP、数据挖掘、Mediator、数据立方体系统等进行了介绍。本书适合于作为高等院校计算机专业研究生的教材或本科生的教学参考书,也适合作为从事相关研究或开发工作的专业技术人员的高级参考资料 译者序 前言 第1章 DBMS实现概述 1.1 Megatr on 2000数据库系统介绍 1.1.1 Megatr on 2000实现细节 1.1.2 Megatron 2000如何执行查询 1.1.3 Megatron 2000有什么问题 1.2 数据库管理系统概述 1.2.1 数据定义语言命令 1.2.2 查询处理概述 1.2.3 主存缓冲区和缓冲区管理器 1.2.4 事务处理 1.2.5 查询处理器 1.3 本书梗概 1.3.1 预备知识 1.3.2 存储管理概述 1.3.3 查询处理概述 1.3.4 事务处理概述 1.3.5 信息集成概述 1.4 数据库模型和语言回顾 1.4.1 关系模型回顾 1.4.2 SQL回顾 1.4.3 关系的和面向对象的数据 1.5 小结 1.6 参考文献 第2章 数据存储 2.1 存储器层次 2.1.1 高速缓冲存储器 2.1.2 主存储器 2.1.3 虚拟存储器 2.1.4 第二级存储器 2.1.5 第三级存储器 2.1.6 易失和非易失存储器 习题 2.2 磁盘 2.2.1 磁盘结构 2.2.2 磁盘控制器 2.2.3 磁盘存储特性 2.2.4 磁盘访问特性 2.2.5 块的写入 2.2.6 块的修改 习题 2.3 辅助存储器的有效使用 2.3.1 计算的I/O模型 2.3.2 辅助存储器中的数据排序 2.3.3 归并排序 2.3.4 两阶段多路归并排序 2.3.5 扩展多路归并以排序更大的关系 习题 2.4 改善辅助存储器的访问时间 2.4.1 按柱面组织数据 2.4.2 使用多个磁盘 2.4.3 磁盘镜像 2.4.4 磁盘调度和电梯算法 2.4.5 预取和大规模缓冲 2.4.6 各种策略及其优缺点 习题 2.5 磁盘故障 2.5.1间断性故障 2.5.2 校验和 2.5.3 稳定存储 2.5.4 稳定存储的错误处理能力 习题 2.6 从磁盘崩溃中恢复 2.6.1 磁盘的故障模型 2.6.2 作为冗余技术的镜像 2.6.3 奇偶块 2.6.4 一种改进:RAID 2.6.5 多个盘崩溃时的处理 习题 2.7 小结 2.8 参考文献 第3章 数据元素的表示 3.1 数据元素和字段 3.1.1 关系型数据库元素的表示 3.1.2 对象的表示 3.1.3 数据元素的表示 3.2 记录 3.2.1 定长记录的构造 3.2.2 记录首部 3.2.3 定长记录在块中的放置 习题 3.3 块和记录地址的表示 3.3.1 客户机-服务器系统 3.3.2 逻辑地址和结构地址 3.3.3 指针混写 3.3.4 块返回磁盘 3.3.5 被固定的记录和块 习题 3.4 变长数据和记录 3.4.1 具有变长字段的记录 3.4.2 具有重复字段的记录 3.4.3 变格式的记录 3.4.4 不能装入一个块中的记录 3.4.5 BLOBS 习题 3.5 记录的修改 3.5.1 插入 3.5.2 删除 3.5.3 修改 习题 3.6 小结 3.7 参考文献 第4章 索引结构 4.1 顺序文件上的索引 4.1.1 顺序文件 4.1.2 稠密索引 4.1.3 稀疏索引 4.1.4 多级索引 4.1.5 重复键的索引 4.1.6 数据修改期间的索引维护 习题 4.2 辅助索引 4.2.1 辅助索引的设计 4.2.2 辅助索引的应用 4.2.3 辅助索引中的间接 4.2.4 文档检索和倒排索引 习题 4.3 B树 4.3.1 B树的结构 4.3.2 B树的应用 4.3.3 B树中的查找 4.3.4 范围查询 4.3.5 B树的插入 4.3.6 B树的删除 4.3.7 B树的效率 习题 4.4 散列表 4.4.1 辅存散列表 4.4.2 散列表的插入 4.4.3 散列表的删除 4.4.4 散列表索引的效率 4.4.5 可扩展散列表 4.4.6 可扩展散列表的插入 4.4.7 线性散列表 4.4.8 线性散列表的插入 习题 4.5 小结 4.6 参考文献 第5章 多维索引 5.1 需要多维的应用 5.1.1 地理信息系统 5.1.2 数据立方体 5.1.3 SQL多维查询 5.1.4 使用传统索引执行范围查询 5.1.5 利用传统索引执行最邻近查询 5.1.6 传统索引的其他限制 5.1.7 多维索引结构综述 习题 5.2 多维数据的类散列结构 5.2.1 网格文件 5.2.2 网格文件的查找 5.2.3 网格文件的插入 5.2.4 网格文件的性能 5.2.5 分段散列函数 5.2.6 网格文件和分段散列的比较 习题 5.3 多维数据的类树结构 5.3.1 多键索引 5.3.2 多键索引的性能 5.3.3 kd树 5.3.4 kd树的操作 5.3.5 使kd树适合辅存 5.3.6 四叉树 5.3.7 R树 5.3.8 R树的操作 习题 5.4 位图索引 5.4.1 位图索引的诱因 5.4.2 压缩位图 5.4.3 游程长度编码位向量的操作 5.4.4 位图索引的管理 习题 5.5 小结 5.6 参考文献 第6章 查询执行 6.1 一种查询代数 6.1.1 并、交和差 6.1.2 选择操作符 6.1.3 投影操作符 6.1.4 关系的积 6.1.5 连接 6.1.6 消除重复 6.1.7 分组和聚集 6.1.8 排序操作符 6.1.9 表达式树 习题 6.2 物理查询计划操作符介绍 6.2.1 扫描表 6.2.2 扫描表时的排序 6.2.3 物理操作符计算模型 6.2.4 衡量代价的参数 6.2.5 扫描操作符的I/O 代价 6.2.6 实现物理操作符的迭代器 6.3 数据库操作的一趟算法 6.3.1 一次多元组操作的一趟算法 6.3.2 全关系的一元操作的一趟算法 6.3.3 二元操作的一趟算法 习题 6.4 嵌套循环连接 6.4.1 基于元组的嵌套循环连接 6.4.2 基于元组的嵌套循环连接的迭代器 6.4.3 基于块的嵌套循环连接算法 6.4.4 嵌套循环连接的分析 6.4.5 迄今为止的算法的小结 习题 6.5 基于排序的两趟算法 6.5.1 利用排序去除重复 6.5.2 利用排序进行分组和聚集 6.5.3 基于排序的并算法 6.5.4 基于排序的交和差算法 6.5.5 基于排序的一个简单的连接算法 6.5.6 简单排序连接的分析 6.5.7 一种更有效的基于排序的连接 6.5.8 基于排序的算法小结 习题 6.6 基于散列的两趟算法 6.6.1 通过散列划分关系 6.6.2 基于散列的消除重复算法 6.6.3 基于散列的分组和聚集算法 6.6.4 基于散列的并、交、差算法 6.6.5 散列连接算法 6.6.6 节省一些磁盘I/O 6.6.7 基于散列的算法小结 习题 6.7 基于索引的算法 6.7.1 聚簇和非聚簇索引 6.7.2 基于索引的选择 6.7.3 使用索引的连接 6.7.4 使用有排序索引的连接 习题 6.8 缓冲区管理 6.8.1 缓冲区管理结构 6.8.2 缓冲区管理策略 6.8.3 物理操作符选择和缓冲区管理的关系 习题 6.9 使用超过两趟的算法 6.9.1 基于排序的多趟算法 6.9.2 基于排序的多趟算法的性能 6.9.3 基于散列的多趟算法 6.9.4 基于散列的多趟算法的性能 习题 6.10 关系操作的并行算法 6.10.1 并行模型 6.10.2 一次一个元组的并行操作 6.10.3 全关系操作的并行算法 6.10.4 并行算法的性能 习题 6.10 小结 6.11 参考文献 第7章 查询编译器 7.1 语法分析 7.1.1 语法分析与语法分析树 7.1.2 SQL的一个简单子集的语法 7.1.3 预处理器 习题 7.2 用于改进查询计划的代数定律 7.2.1 交换律与结合律 7.2.2 涉及选择的定律 7.2.3 下推选择 7.2.4 涉及投影的定律 7.2.5 有关连接与积的定律 7.2.6 有关重复消除的定律 7.2.7 涉及分组与聚集的定律 习题 7.3 从语法分析树到逻辑查询计划 7.3.1 转换成关系代数 7.3.2 从条件中去除子查询 7.3.3 逻辑查询计划的改进 7.3.4 结合/分配运算符的分组 习题 7.4 操作代价的估计 7.4.1 中间关系大小的估计 7.4.2 投影大小的估计 7.4.3 选择大小的估计 7.4.4 连接大小的估计 7.4.5 多连接属性的自然连接 7.4.6 多个关系的连接 7.4.7 其他操作的大小估计 习题 7.5 基于代价的计划选择介绍 7.5.1 大小参数估计值的获取 7.5.2 统计量的增量计算 7.5.3 减少逻辑查询计划代价的启发式 7.5.4 枚举物理计划的方法 习题 7.6 连接顺序的选择 7.6.1 连接的左右变元的意义 7.6.2 连接树 7.6.3 左深连接树 7.6.4 通过动态编程来选择连接顺序和分组 7.6.5 带有更具体的代价函数的动态编程 7.6.6 选择连接顺序的贪婪算法 习题 7.7 物理查询计划选择的完成 7.7.1 选取选择方法 7.7.2 选取连接方法 7.7.3 流水线操作与物化 7.7.4 一元流水线操作 7.7.5 二元流水线操作 7.7.6 物理查询计划的符号 7.7.7 物理操作的顺序 习题 7.8 小结 7.9 参考文献 第8章 系统故障对策 8.1 可回复操作的问题和模型 8.1.1 故障模式 8.1.2 关于事务的进一步讨论 8.1.3 事务的正确执行 8.1.4 事务的原语操作 习题 8.2 undo日志 8.2.1 日志记录 8.2.2 undo日志规则 8.2.3 使用undo日志的恢复 8.2.4 检查点 8.2.5 非静止检查点 习题 8.3 redo日志 8.3.1 redo日志规则 8.3.2 使用redo日志的恢复 8.3.3 redo日志的检查点 8.3.4 使用带检查点的redo日志的恢复 习题 8.4 undo/redo日志 8.4.1 undo/redo规则 8.4.2 使用undo/redo日志的恢复 8.4.3 undo/redo日志的检查点 习题 8.5 防备介质故障 8.5.1 备份 8.5.2 非静止转储 8.5.3 使用备份和日志的恢复 习题 8.6 小结 8.7 参考文献 第9章 并发控制 9.1 串行调度和可串行化调度 9.1.1 调度 9.1.2 串行调度 9.1.3 可串行化调度 9.1.4 事务语义的影响 9.1.5 事务和调度的一种记法 习题 9.2 冲突可串行性 9.2.1 冲突 9.2.2 优先图及冲突可串行性判断 9.2.3 优先图测试发挥作用的原因 习题 9.3 使用锁的可串行性实现 9.3.1 锁 9.3.2 封锁调度器 9.3.3 两阶段封锁 9.3.4 两阶段封锁发挥作用的原因 习题 9.4 用多种锁方式的封锁系统 9.4.1 共享锁与排他锁 9.4.2 相容性矩阵 9.4.3 锁的升级 9.4.4 更新锁 9.4.5 增量锁 习题 9.5 封锁调度器的一种体系结构 9.5.1 插入锁动作的调度器 9.5.2 锁表 习题 9.6 数据库元素层次的管理 9.6.1 多粒度的锁 9.6.2 警示锁 9.6.3 幻象与插入的正确处理 习题 9.7 树协议 9.7.1 基于树的封锁的动机 9.7.2 访问树结构数据的规则 9.7.3 树协议发挥作用的原因 习题 9.8 使用时间戳的并发控制 9.8.1 时间戳 9.8.2 物理上不可实现的行为 9.8.3 脏数据的问题 9.8.4 基于时间戳调度的规则 9.8.5 多版本时间戳 9.8.6 时间戳与封锁 习题 9.9 使用有效性确认的并发控制 9.9.1 基于有效性确认的调度器的结构 9.9.2 有效性确认规则 9.9.3 三种并发控制机制的比较 习题 9.10 小结 9.11 参考文献 第10章 再论事务管理 10.1 读未提交数据的事务 10.1.1 脏数据问题 10.1.2 级联回滚 10.1.3 回滚的管理 10.1.4 成组提交 10.1.5 逻辑日志 习题 10.2 视图可串行性 10.2.1 视图等价性 10.2.2 多重图与视图可串行性的判断 10.2.3 视图可串行性的判断 习题 10.3 死锁处理 10.3.1 超时死锁检测 10.3.2 等待图 10.3.3 通过元素排序预防死锁 10.3.4 时间戳死锁检测 10.3.5 死锁管理方法的比较 习题 10.4 分布式数据库 10.4.1 数据的分布 10.4.2 分布式事务 10.4.3 数据复制 10.4.4 分布式查询优化 习题 10.5 分布式提交 10.5.1 分布式原子性的支持 10.5.2 两阶段提交 10.5.3 分布式事务的恢复 习题 10.6 分布式封锁 10.6.1 集中封锁系统 10.6.2 分布式封锁算法的代价模型 10.6.3 封锁多副本的元素 10.6.4 主副本封锁 10.6.5 局部锁构成的全局锁 习题 10.7 长事务 10.7.1 长事务的问题 10.7.2 saga(系列记载) 10.7.3 补偿事务 10.7.4 补偿事务发挥作用的原因 习题 10.8 小结 10.9 参考文献 第11章 信息集成 11.1 信息集成的方式 11.1.1 信息集成的问题 11.1.2 联邦数据库系统 11.1.3 数据仓库 11.1.4 Mediator 习题 11.2 基于Mediator系统的包装器 11.2.1 查询模式的模板 11.2.2 包装器生成器 11.2.3 过滤器 11.2.4 其他在包装器上进行的操作 习题 11.3 联机分析处理 11.3.1 OLAP应用 11.3.2 OLAP数据的多维视图 11.3.3 星型模式 11.3.4 切片和切块 习题 11.4 数据立方体 11.4.1 立方体操作符 11.4.2 通过物化视图实现立方体 11.4.3 视图的格 习题 11.5 数据挖掘 11.5.1 数据挖掘的应用 11.5.2 关联规则的挖掘 11.5.3 A-Priori算法 11.6 小结 11.7 参考文献
实现小型数据库可以通过以下步骤来进行: 1. 设计数据模型:确定数据库中需要存储的数据类型和数据结构,例如表格、行和列等。 2. 创建数据库:根据设计的数据模型,使用编程语言(如C)创建一个数据库文件,用于存储数据。 3. 实现数据存储功能:编写代码实现数据的插入、查询、更新和删除等操作。可以使用数据结构(如数组、链表等)来存储数据,并使用文件读写操作将数据写入数据库文件。 4. 设计索引结构:为了提高查询效率,可以为数据库添加索引结构。通过索引,可以快速定位到特定的数据行,而不需要遍历整个数据库文件。 5. 实现索引功能:编写代码实现索引的创建和维护,确保索引能够随着数据的插入和删除而更新。 6. 添加数据完整性约束:为了保证数据的完整性和一致性,可以在数据库中添加数据约束,例如主键、唯一约束、非空约束等。 7. 完善数据库功能:除了基本的增删改查操作,可以根据实际需求添加其他功能,例如事务处理、备份与恢复、权限管理等。 8. 测试与调试:编写测试用例,对数据库进行全面的测试和调试,确保数据库的稳定性和正确性。 9. 文档撰写:编写数据库的使用手册和开发文档,方便其他开发人员使用和维护数据库。 总结:通过以上步骤,我们可以使用C语言实现一个小型数据库,具备基本的存储、查询和索引等功能。这样的数据库可以满足一些简单的数据管理需求,但相对于成熟的商业数据库系统而言,功能较为有限。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值