自己动手写数据库:实现数据库表的元数据管理(代码片段)

tyler_download tyler_download     2022-12-16     401

关键词:

数据库需要管理很多元数据,所谓元数据就是用来描述数据表结构信息的数据。例如在mysql中使用show tables命令,它会把所有表的名称显示出来,这里数据库表的名称就属于元数据。我们要实现的元数据管理包含四部分,分别为表元数据管理,视图元数据管理,索引元数据管理,和统计相关元数据管理。

首先我们在项目中创建文件夹metadata_management, 然后创建文件interface.go用于定义管理器的接口,首先我们定义的是数据表元数据管理器接口,代码如下:

package metadata_manager

import (
	rm "record_manager"
	"tx"
)

type TableManagerInterface interface 
	CreateTable(tblName string, sch *rm.Schema, tx *tx.Transation)
	GetLayout(tblName string, tx *tx.Transation) *rm.Layout

表管理器使用上面两个接口来创建数据库表,同时存储用于描述数据表的元数据。每个数据库表都会对应两个表用于存储其元数据,第一个表叫tblcat,它的记录包含两个字段,一个是字符串类型,字段名称为“tblename",用于存储它所描述的数据库表的名称,一个字段是整形,字段名为slotsize,用于描述目标数据库表一条记录的长度。第二个表叫fldcat,它用来存储所创建表每个字段的元数据,它包含5个字段,第一个字段名为tblname,类型是字符串,用来记录字段所在的表名字,第二个字段名为fldname,类型为字符串,用来记录字段的名称;第三个字段叫type,类型为int,记录字段类型;第四个字段名为length,记录字段数据长度,类型为int;第五个字段名为offset,记录字段在记录中的偏移,类型为int,这些信息在后续的代码实现中会变得清晰。

在table_manager.go中添加表管理器的实现代码:

package metadata_manager

import (
	rm "record_manager"
	"tx"
)

const (
	MAX_NAME = 16
)

type TableManager struct 
	tcatLayout *rm.Layout
	fcatLayout *rm.Layout


func NewTableManager(isNew bool, tx *tx.Transation) *TableManager 
	tableMgr := &TableManager
	tcatSchema := rm.NewSchema()
	//创建两个表专门用于存储新建数据库表的元数据
	tcatSchema.AddStringField("tblname", MAX_NAME)
	tcatSchema.AddIntField("slotsize")
	tableMgr.tcatLayout = rm.NewLayoutWithSchema(tcatSchema)

	fcatSchema := rm.NewSchema()
	fcatSchema.AddStringField("tblname", MAX_NAME)
	fcatSchema.AddStringField("fldname", MAX_NAME)
	fcatSchema.AddIntField("type")
	fcatSchema.AddIntField("length")
	fcatSchema.AddIntField("offset")
	tableMgr.fcatLayout = rm.NewLayoutWithSchema(fcatSchema)

	if isNew 
		//如果当前数据表是第一次创建,那么为这个表创建两个元数据表
		tableMgr.CreateTable("tblcat", tcatSchema, tx)
		tableMgr.CreateTable("fldcat", fcatSchema, tx)
	

	return tableMgr


func (t *TableManager) CreateTable(tblName string, sch *rm.Schema, tx *tx.Transation) 
	//在创建数据表前先创建tblcat, fldcat两个元数据表
	layout := rm.NewLayoutWithSchema(sch)
	tcat := rm.NewTableScan(tx, "tblcat", t.tcatLayout)
	tcat.Insert()
	tcat.SetString("tblname", tblName)
	tcat.SetInt("slotsize", layout.SlotSize())
	tcat.Close()
	fcat := rm.NewTableScan(tx, "fldcat", t.fcatLayout)
	for _, fldName := range sch.Fields() 
		fcat.Insert()
		fcat.SetString("tblname", tblName)
		fcat.SetString("fldname", fldName)
		fcat.SetInt("type", int(sch.Type(fldName)))
		fcat.SetInt("length", sch.Length(fldName))
		fcat.SetInt("offset", layout.Offset(fldName))
	
	fcat.Close()


func (t *TableManager) GetLayout(tblName string, tx *tx.Transation) *rm.Layout 
	//获取给定表的layout结构
	size := -1
	tcat := rm.NewTableScan(tx, "tblcat", t.tcatLayout)
	for tcat.Next() 
		//找到给定表对应的元数据表
		if tcat.GetString("tblname") == tblName 
			size = tcat.GetInt("slotsize")
			break
		
	
	tcat.Close()
	sch := rm.NewSchema()
	offsets := make(map[string]int)
	fcat := rm.NewTableScan(tx, "fldcat", t.fcatLayout)
	for fcat.Next() 
		if fcat.GetString("tblname") == tblName 
			fldName := fcat.GetString("fldname")
			fldType := fcat.GetInt("type")
			fldLen := fcat.GetInt("length")
			offset := fcat.GetInt("offset")
			offsets[fldName] = offset
			sch.AddField(fldName, rm.FIELD_TYPE(fldType), fldLen)
		
	
	fcat.Close()
	return rm.NewLayout(sch, offsets, size)


在上面代码中,TableManager对象在创建时会先创建两张数据库表分别名为tblcat和tdlcat,同时使用前面实现的接口来设置这两个表的记录结构和字段信息,以后TableManager每创建一个新表时,就会把新表的元数据存储在这两张表中。从代码也可以看出数据库表的元数据其实对应两部分信息,一部分是表所包含的字段信息,一部分是表对应的schema信息,这些信息会作为tblcat和tdlcat这两张表的记录存储起来。

下面我们看看如何小于TableManager提供的接口,在main.go中输入代码如下:

func main() 
	file_manager, _ := fm.NewFileManager("recordtest", 400)
	log_manager, _ := lm.NewLogManager(file_manager, "logfile.log")
	buffer_manager := bmg.NewBufferManager(file_manager, log_manager, 3)

	tx := tx.NewTransation(file_manager, log_manager, buffer_manager)
	sch := record_mgr.NewSchema()
	sch.AddIntField("A")
	sch.AddStringField("B", 9)

	tm := mm.NewTableManager(true, tx)
	tm.CreateTable("MyTable", sch, tx)
	layout := tm.GetLayout("MyTable", tx)
	size := layout.SlotSize()
	sch2 := layout.Schema()
	fmt.Printf("MyTable has slot size: %d\\n", size)
	fmt.Println("Its fields are: ")
	for _, fldName := range sch2.Fields() 
		fldType := ""
		if sch2.Type(fldName) == record_mgr.INTEGER 
			fldType = "int"
		 else 
			strlen := sch2.Length(fldName)
			fldType = fmt.Sprintf("varchar( %d )", strlen)
		
		fmt.Printf("%s : %s", fldName, fldType)
	

	tx.Commit()

在代码中,我们首先创建一个表名为MyTable,它的记录包含两个字段分别是整形字段,名称为A,以及字符串字段,名称为B。在使用CreateTable创建表时,这些信息就已经被写入到tblcat和tdlcat两张表中。

然后代码调用TableManager的GetLayout接口获取表MyTable的结构信息,由于这些信息已经写入两张元数据库表,因此这些信息只要从表里面读取即可。在GetLayout的实现中,它首先根据表的名称"MyTable"在tblcat表中查询这个表一条记录的字节大小,然后从tldcat表中查询它记录所包含的字段信息,这些信息获取后再调用NewLayout接口把MyTable表的Layout结构创建出来。

上面代码运行后输出结果如下:

MyTable has slot size: 33
Its fields are: 
A : int
B : varchar( 9 )
A : int
B : varchar( 9 )
transation 1  committed

从输出可以看到,所谓元数据管理其本质就是创建两个特定的表名为tblcat,tldcat,将新创建的数据表记录的长度以及字段信息分别存储在这两个表中,以后在实现表的管理时,从这两张表中再去查询给定表的layout信息,代码下载链接: https://pan.baidu.com/s/1B6tjAdUsIDXdufdTcRv1pw
提取码: d2iv,
更多内容请在B站搜索Coding迪斯尼.

java单链表的实现自己动手写一个单链表(代码片段)

...储数据的存储单元,指针就是连接每个结点的地址数据。自己手动写一个单链表:首先,定义一个节点类:packagecom.wei;publicclassLinkpublicintdata;//存放数据pub 查看详情

自己动手写一个单链表(代码片段)

一、概述单向链表(单链表)是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始。链式存储结构的线性表将采用一组任意的存储单元存放线性表中的数据元素。由于不需要按顺序存储,... 查看详情

自己动手写数据库:实现表扫描(代码片段)

在上一节我们实现了记录管理,本节我们看看记录的读取实现,也就是所谓的表扫描。我们将实现一个名为TableScan的类,它把表的记录当做数组来读取,通过挪到一个记录指针来遍历表的记录,它的作用类似... 查看详情

自己动手写数据库:实现表扫描(代码片段)

在上一节我们实现了记录管理,本节我们看看记录的读取实现,也就是所谓的表扫描。我们将实现一个名为TableScan的类,它把表的记录当做数组来读取,通过挪到一个记录指针来遍历表的记录,它的作用类似... 查看详情

动手写数据库:实现记录管理(代码片段)

在数据库中,数据以”记录“作为一个单元来存储,例如一个表的“一行”就对应一条记录。假设我们有一个表叫STUDENT,其中有name,age,sex,class等字段,那么一条记录的信息就由这四个字段对应的信息合成。一条记... 查看详情

自己动手写数据库:并发管理组件lock_table的原理和实现(代码片段)

...基本原理。其中一个重要原则就是“序列化”,也就数据库引擎要对交易提交的请求进行调度,调度的结果要使得每个交易就好像独占了引擎那样。要实现这样的效果就必须进行相应的加锁。但是加锁必然会降低高并发... 查看详情

自己动手写数据库:并发管理组件lock_table的原理和实现(代码片段)

...基本原理。其中一个重要原则就是“序列化”,也就数据库引擎要对交易提交的请求进行调度,调度的结果要使得每个交易就好像独占了引擎那样。要实现这样的效果就必须进行相应的加锁。但是加锁必然会降低高并发... 查看详情

自己动手写数据库:并发管理器的实现,以及并发交易流程的分析(代码片段)

在上一节中我们实现了并发管理的核心组件那就是lock_table,它的原理是对给定的区块加锁,如果区块被读取,那么就加上共享锁,也就是多个线程能同时读取,但是不允许任何线程写入,如果有线程要写入࿰... 查看详情

自己动手写数据库:并发管理器的实现,以及并发交易流程的分析(代码片段)

在上一节中我们实现了并发管理的核心组件那就是lock_table,它的原理是对给定的区块加锁,如果区块被读取,那么就加上共享锁,也就是多个线程能同时读取,但是不允许任何线程写入,如果有线程要写入࿰... 查看详情

自己动手实现java数据结构链表

1.链表介绍  前面我们已经介绍了向量,向量是基于数组进行数据存储的线性表。今天,要介绍的是线性表的另一种实现方式---链表。  链表和向量都是线性表,从使用者的角度上依然被视为一个线性的列表结构。但是,链... 查看详情

自己动手写数据库:视图元数据管理,统计元数据管理(代码片段)

...时构建出来。跟数据库表一样,视图同样需要进行元数据管理。跟上节相同我们定义一个ViewManager来创建视图,同时创建一个viewcat数据库表来存储视图的元数据,这个表有两个字段分别是ViewName,他是字符串类型,... 查看详情

自己动手写数据库:视图元数据管理,统计元数据管理(代码片段)

...时构建出来。跟数据库表一样,视图同样需要进行元数据管理。跟上节相同我们定义一个ViewManager来创建视图,同时创建一个viewcat数据库表来存储视图的元数据,这个表有两个字段分别是ViewName,他是字符串类型,... 查看详情

自己动手写个数据库系统:磁盘的基本原理和数据库底层文件系统实现(代码片段)

...;同时也完成过一个具体而微的编译器,接下来就剩下数据库了。事实上数据库的难度系数要大于编译器,复杂度跟操作系统差不多,因此我一直感觉不好下手。随着一段时间的积累,我感觉似乎有了入手的方向&#x... 查看详情

自己动手写数据库:实现交易对象和恢复管理器(代码片段)

前面一节我们完成了用于实现系统恢复的日志,本节我们看看如何基于日志内容实现系统恢复。我们将设计一个系统恢复管理器,它在系统启动时读取日志内容,根据读到的日志对数据进行恢复,由于所谓“恢复... 查看详情

自己动手撸一个linkedlist(代码片段)

自己动手撸一个LinkedList1.原理LinkedList是基于双链表的动态数组,数据添加删除效率高,只需要改变指针指向即可,但是访问数据的平均效率低,需要对链表进行遍历。因此,LinkedList善于进行一些插入、删除操作,不利于进行检... 查看详情

自己动手写数据库:sql查询处理的基础准备工作(代码片段)

使用过关系型数据库的同学都会了解SQL语言,它是数据库查询模块的一部分,这门语言能够描述用户希望获取哪些数据,对数据进行怎样的加工等。SQL语言基于一种叫关系代数的逻辑,这种逻辑基于三种底层操作&... 查看详情

自己动手写数据库:并发管理的基本原理(代码片段)

一个好的数据库,其特点必然是吞吐量高,也就是它能在高并发请求压力下保证数据的准确性和安全性,由此并发管理是不可或缺的一环。事实上并发管理是一个相当复杂的计算机科学领域的课题,它几乎可以自... 查看详情

从零动手写数据库系统:数据库系统的日志模块实现

...开一个模块,那就是”日志“。既然我们要开发一个数据库系统,那么它必然要有自己的日志模块。日志通常用于记录系统的运行状态,有点类似于快照,一旦系统出现异常,那么管理员或者它的代码本身可... 查看详情