1.LiteDB页的技术工作原理
LiteDB虽然是单个文件类型的数据库,但是数据库有很多信息,例如索引,集合,文件等。为了管理这些信息,LiteDB实现了数据库页的概念。页是一个拥有4096 字节的 存储相同信息的地址块。页也是操作磁盘文件(读写)的最小单元。LiteDB有6中页类型,类图如下:
1.1 BasePageBasePage是数据库页类型的父类,使用一个常量字段PAGE_SIZE定义了页的大小为4096个字节。
主要的属性说明如下:
PageID:一个uint类型的ID,在LiteDB数据库中,不管是哪种页类型,这个PageID都是不一样的。
PrevPageID:指向上一页的ID。如果指向一个uint的最大值(4294967295)即表示没有上一页。
NextPageID:指向下一页的ID。如果指向一个uint的最大值(4294967295)即表示没有页一页。从这里看页的结构有点像双向链表,但在实际存储中,页与页之间貌似并不是以链表形式存储(可能我这里也没太搞明白这两个ID的具体作用)。
PageType:一个页类型的枚举,根据这个枚举,可以将BasePage强制转换成相对应的页类型。
ItemCount:一个用于计算页中的特定数据大小的ItemCount,在DataPage和IndexPage中可以看到它的作用。
IsDirty:从名称我们可以大概猜出它的作用,在LiteDB中专门用来标记该页的数据是否完成Commit操作。
FreeBytes:这个属性需要各个子类重写,用于计算该页还有多少可用的字节。
1.2 CollectionPage一个CollectionPage就代表一张表,比如数据库中有Customer和Order两张表,那么在数据库中就存在两个CollectionPage分别存储这两张表的信息。CollectionPage之间用 Prev/Next 连接。
CollectionPage中有一个CollectionName代表了该表的名称,比如Customer和Order两张表就有两个名称分别为“Customer“和“Order”。DocumentCount属性标志着该表有多少条记录,比如向Customer表插入100条数据,那么DocumentCount就为100。
CollectionPage里面最主要的一个属性Indexes,这是一个自定义结构体CollectionIndex的数组,它代表了该表中的所有索引名称。比如Customer表中有三个字段分别“ID”、“Name”、“Age”,(特别强调,这三个字段都要作为索引)那么Indexes数组就分别包含了这三个字段。同时可以看到这个页中有个叫PK的属性,根据名称我们大致就知道它肯定是主键,比如上面Customer表中的“ID”。
1.3 HeaderPageHeaderPage存储了当前使用的LiteDB数据库的一些信息,包括头文件,数据库版本,可用的空余页ID,用户版本等。其中有个名为ChangeID的属性,这个是用于处理事务时,验证客户端中的ChangeID是否一致。
1.4 DataPageDataPage就是数据页,它的结构最简单,除了父类之外只包含一个名为DataBlocks的字典,这个字典Key是一个ushort数字,Value是一个DataBlock类。后面我们通过分析DataBlock这个结构体就大致能知道DataPage的作用。同时在数据页中还可以看到它重写FreeBytes属性就是可用字节减去字典长度,ItemCount就等于字典长度。
1.5 ExtendPageExtendPage是数据扩展页,如果插入的数据过大时,就讲超过Page的数据块放入ExtendPage中的Data中,同样它重写FreeBytes属性就是可用字节减去Data的长度。
1.6 IndexPageIndexPage就是索引页,它的结构和DataPage类似,只包含了一个名为Nodes的字典,这个字典中key是一个ushort数字,Value是一个IndexNode类,索引使用跳表的形式进行存储。
2. 数据可视化——掀开Page的面纱可能看完上面说明,你可能对数据页仍然是一头雾水,我在看源码的时候也是如此。后来经过我各种努力,想出了 一个办法,就是将页的信息实时展示出来,也就是常说的数据可视化。后面我会专门介绍如何把数据页的信息展示出来,这里大家先跟着往下看。
首先我们先创建一个数据库:
LiteEngine db = new LiteEngine(Path);