FastDB 高效率的内存数据库系统

 

FastDB 高效率的内存数据库系统

简介

FastDB 是一个高效率的内存数据库系统,具有实时性能和方便的

C++ 接口。FastDB 并不支持客户端/ 服务器结构,所有使用FastDB

数据库的应用程序都必须运行在同一台主机上。FastDB 为具有主导读

取访问模式的应用程序作了优化。通过消除数据传输的开销和使用高

性能的锁工具实现了查询执行的高速度。数据库文件和使用该数据库

的每一个应用程序占用的虚拟内存空间相映射。所以查询在应用程序

的任务中执行,不需要进行任务切换和数据传输。在FastDB中,通过

原子指令来实现对数据库并发访问的同步,对查询处理几乎不增加任

何开销。FastDB 假设整个数据库都在当前内存中,并且在这个假设的

基础上优化查询算法和结构。另外,数据库缓存管理几乎不会给FastDB

增加任何开销,同时FastDB也不需要在数据库文件和缓冲池中进行数

据传送。这就是为什么FastDB 比将所有数据放在缓冲池中的传统数据

库明显速度快的原因。

FastDB 支持事务、在线备份和系统崩溃之后的自动恢复。事务提

交协议基于一个影子根页算法,对数据库执行原子更新操作。恢复操

作执行起来非常快,给关键应用程序提供了高效率。另外,它还取消

了事务日志,提高了系统的整体性能,并且能够更加有效地使用系统

资源。

FastDB是面向应用程序的数据库,使用应用程序的类信息来构建数据

库的表。FastDB 支持自动系统赋值,只允许你在一个地方——你的应用

程序的类中,改变它们的值。FastDB为从数据库中提取数据提供了一个灵

活而方便的接口。使用类似于SQL 的语言来书写查询语句。这些非原子

字段、嵌套数组、用户自定义类型和方法、直接指向对象内部的指针等后

关系性能,简化了数据库应用程序的设计,并且使得它们更加高效。

虽然FastDB的优化是基于整个数据库都存放在机器的物理内存的这

个假设上的,我们依然可以将FastDB使用在那些大小超过系统物理内存

的数据库上。最后,标准操作系统的交换机制将会起作用。但是所有的

FastDB 的算法和结构的优化都是基于数据存放在内存中这个假设上的,

所以数据交换的效率不会很高。
查询语言

FastDB支持一种语法和SQL 非常类似的查询语言。FastDB使用的符

号更加流行于面向对象的编程中,而不是面向关系型数据库的编程中。

将表行看作是对象的实例,表是这些对象的类。跟SQL 不一样,FastDB

是面向对象的,而并不跟SQL一样是工作在一张二维表上的。所以每一

个查询的执行结果就是一个类的一组对象。FastDB 查询语言和标准的

SQL 的主要区别如下:

1、 在多张表之间没有连接,也没有嵌套的子查询。通常,查询是

从一张表中返回一个对象集。

2、 表的最小的列单元是标准的C 类型。

3、 FastDB只有空指针,而没有NULL值。我完全同意C.J Date关

于三值逻辑的评论,也赞同他使用默认值的建议。

4、 结构和数组可以用作记录成员。提供了一个特定的exists引用来

定位数组中的元素,

5、 不仅可以用无参用户方法来定义记录成员,也可以用它来定义

表记录(对象)。

6、 应用程序可以定义仅带一个简单字符型或数值型参数的用户

函数。

7、 支持对象之间的指针,并且自动支持反向指针。

8、 start from follow by 的构造函数,实现了指针在递归记录之

间的移动。

9、 由于查询语言和C++类结合得比较紧密,关键字和语言的标识

符都区分大小写。

10、 不能进行整型和符点类型到字符串类型的隐式转换。如果需要

进行这种转换的话,必须显式地进行。

下面的和BNF 类似的符号规则规定了FastDB 查询语言的语法,显

示如下:

语法规则

elect-condition ::= ( expression ) ( traverse ) ( order )

expression ::= disjunction

disjunction ::= conjunction

| conjunction or disjunction

conjunction ::= comparison

| comparison and conjunction

comparison ::= operand = operand

| operand != operand

| operand <> operand

| operand < operand

| operand <= operand

| operand > operand

| operand >= operand

| operand (not) like operand

| operand (not) like operand escape string

| operand (not) in operand

| operand (not) in expressions-list

| operand (not) between operand and operand

| operand is (not) null

operand ::= addition

additions ::= multiplication

| addition + multiplication

| addition || multiplication

| addition - multiplication

multiplication ::= power

| multiplication * power

| multiplication / power

power ::= term

| term ^ power

term ::= identifier | number | string

| true | false | null

| current | first | last

| ( expression )

| not comparison

| - term

| term [ expression ]

| identifier . term

| function term

| exists identifier : term

function ::= abs | length | lower | upper

| integer | real | string | user-function

string ::= ' { { any-character-except-quote } ('') } '

expressions-list ::= ( expression { , expression } )

order ::= order by sort-list

sort-list ::= field-order { , field-order }

field-order ::= [length] field (asc | desc)

field ::= identifier { . identifier }

traverse ::= start from field ( follow by fields-list )

fields-list ::= field { , field }

user-function ::= identifier

标识符是区分大小写的,以字符a-z, A-Z, '_' 或'$' 开始,只

能包含字符a-z, A-Z, 0-9, '_' 或 '$',并且不能和SQL 的保留字

重复。

也可以使用标准的ANSI注释。忽略双连字号到行结尾部分的所有

字符。

FastDB扩展了ANSI 标准的SQL操作符,支持位操作。操作符and

和or 不仅能够应用于布尔类型的操作数,而且能够应用于整型操作

数。把and 和or 操作符应用于整型操作数的计算结果是一个整型值,

该值的每一位都是由and和or 位操作得出的结果。位操作可以用来高

效率地设置数据的每一位。同样,FastDB也支持整形和浮点型的幂操

作(x^y)。
结构

FastDB将结构作为记录成员来接收。通过使用标准的点操作符,可

以访问结构字段:company.address.city。

可以建立结构字段的索引,并且可以将结构字段排序过后使用。结

构可以将其他结构作为它成员;对于嵌套的层数没有限制。

程序员可以定义结构的方法,在查询的时候可以使用调用结构

成员一样的语法来调用自定义的结构方法。这样的方法除了带有一

个指向方法所属的对象的指针外,应该没有其他参数,并且应该返

回一个原子类型的值(布尔类型、数值类型、字符串类型或指针类

型)。同时, 这个方法也不能改变对象的实例( 方法的不可改变

性)。如果方法返回一个string 类型,必须使用一个新的字符类型

的操作数来指向它,因为在复制完它的值之后,将会删除这个返回

的字符串。

所以用户定义的方法可以用来创建一个虚拟成员,这个虚拟成

员并不存储在数据库中,而是通过使用其他成员值计算出来的。例

如: F a s t D B 的d b D a t e T i m e 类型仅包含整型时间戳成员和

dbDateTime::year(), dbDateTime::month()...这类方法。所以在

传输的记录字段中含有dbDateTime 类型的应用程序中,可以使用

“delivery.year = 1999 ”这种特定的查询语句。方法只能够在定义

它们的应用程序的上下文任务中执行,而在其他应用程序和交互式

的SQL 中不可以使用。

数组

FastDB将动态数组作为记录的成员来看待。FastDB并不支持多维数

组,但是可以将数组的每一个元素定义成数组,即定义数组的数组。可

以根据数组字段的长度来给结果集中的记录排序。FastDB提供了一系列

特定的构造函数来处理数组:

1、 通过使用函数length()可以获得数组元素的数目。

2、 可以通过操作符[]来取得数组的成员。如果中括号中的数组元素

的下标超过了数组的范围,将会产生一个意外。

3、 可以使用操作符in 来确定在数组中是否包含in 操作符左边的

操作数。这个操作符号只能用在原子类型的数组中,即成员类型是:布

尔类型、数值类型、指针类型或字符串类型。

4、 通过操作符exists来实现数组元素的循环。关键字exists之后的

变量可以看作是exists 操作符后表达式中数组的下标序号。这个下标变

量将会叠代所有可能的数组下标值,直到表达式为真或者下标序号超出

了范围。如下面:

exists i: (contract.company.location = 'US')

将会把所有坐落在US 的公司运输过来的货物信息选择出来,而查

询语句:

例子含义

expression 非终结

not 终结

| 非关联选择

(not) 可选择的部分

{1..9} 重复0 次或多次

abs and asc Between By

current desc escape Exists False

first follow from In Integer

Is length like Last Lower

not null or Real Start

string true upper

保留字列表

not exists i: (contract.company.location = 'US')

将会把所有坐落在US 之外的公司运输过来的货物信息选择出来。

嵌套的exists 子句等同于使用相应下标变量的嵌套循环。例如下面

的查询语句:

exists column: (exists row: (matrix[column][row] = 0))

将会选择出所有矩阵字段元素中包含0的记录,矩阵字段实际上就

是整型数组的数组类型。这句语句和下面的嵌套循环作用相同:

bool result = false;

for (int column = 0; column < matrix.length(); column++) {

for (int row = 0; row < matrix[column].length(); row++) {

if (matrix[column][row] == 0) {

result = true;

break;

}

}

}

不可以改变使用的变量的顺序。下面查询语句的执行结果和上面查

询语句的执行结果将会完全不同。

exists row: (exists column: (matrix[column][row] = 0))

最后,程序只有在空矩阵中死循环的时候才会挂起。

字符串

FastDB中的所有的字符串的长度都是可变的,程序员不需要担心字

符字段的最大长度的限制。所有数组可以进行的操作都可以应用在字符

串中。另外,字符串有自己的操作。首先,可以使用标准关系操作符来

比较字符串。目前FastDB 只支持ASCII 字符集(同c 中的字符型),字

符串的单字节比较忽略本地化设置。

可以使用关键字LIKE 和特定通配符'%' 和'_' 实现模式匹配,

来查找字符串。字符“_”用来代替任何单个字符,而“%”用来代替

0 个或多个字符。将like操作符的扩展模式和escape关键字结合起来使

用,可以将特定escape 字符后的字符'%' 和 '_' 按照正常字符的模

式来处理。

可以使用in 操作符在一个字符串中查找一个子串。对于所有color

字段包含“blue”的记录,表达式('blue' in color) 将返回真。如果,

被查找字符串的长度大于一些阈值(当前是512),将使用Boyer-Moore

字串查找算法,而不用直接向前查找的方法。

可以使用操作符+ 或 || 来连接字符串。添加后面一个操作符|| 是

为了和ANSI SQL 标准兼容。由于FastDB 不支持表达式中到字符串的

隐式转化,可以为字符串重新定义“+”的语义。

指针

可以通过使用和访问结构成员相同的点符号来改变指针的指向。如

下面的查询

company.address.city = 'Chicago'

将会访问一个Contract 记录的company成员所指向的记录,并且会

从Supplier 表指定记录的address 字段中提取成员city。

通过is null 或 is not null 可以检查指针是否为空。指针之间可以

进行比较,指针和null也可以比较。在取消null指针指向的时候,FastDB

会产生一个意外。

在查找一张表的时候,有一个关键字current可以指向当前的记录。

通常,使用关键字current 来将当前记录指针和其他指针进行比较,或

者用来在一个指针数组中定位。例如,下面的查询将在Concrete表中查

找所有有效的合同。 (假设字段canceledContracts 有一个dbArray<

dbReference<Contract> > 类型):

current not in supplier.canceledContracts

FastDB 为通过指针实现记录之间的递归提供了特殊的操作符:

start from root-references

( follow by list-of-reference-fields )

命令的第一部分用来指定根对象。非终端跟指针应该是一个指针变

量或指针类型的数组。在这里可以使用两个特定的关键字first 和 last

来定位表中第一或最后一条记录。如果你想检查指针数组指向的所有

记录或带有某个条件的指针字段,那么可以执行这个命令,但是不要

带by 部分。

如果你指定了by 部分, FastDB 将会递归访问表的记录。从根

指针开始,并通过指针字段列表在记录之间转换。指针字段列表应该

包含指针字段或指针类型的数组。这是一种深度优先访问,按照高-

左-右的顺序。 (首先,我们访问父亲节点,然后按从左到右的顺序

访问兄弟节点) 。当访问到一个空指针或指向一个已经访问过的记

录,递归结束。例如,下面的查询将会按TLR顺序搜索一棵 weight

大于1 的树。

"weight > 1 start from first follow by left, right"

对于下面的树:

A:1.1

B:2.0 C:1.5

D:1.3 E:1.8 F:1.2 G:0.8

查询执行结果如下:

('A', 1.1), ('B', 2.0), ('D', 1.3), ('E', 1.8), ('C', 1.5), ('F', 1.2)

在前面,我们已经提过,FastDB 通过对象来操作,并不支持连接。

所以我们可以通过指针来实现连接。考虑一下典型的Supplier-Shipment-

Detail(供应商- 运输- 细目)例子:

struct Detail {

char const* name;

double weight;

TYPE_DESCRIPTOR((KEY(name, INDEXED), FIELD(weight)));

};

struct Supplier {

char const* company;

char const* address;

TYPE_DESCRIPTOR((KEY(company, INDEXED), FIELD(address)));

};

struct Shipment {

dbReference<Detail> detail;

dbReference<Supplier> supplier;

int4 price;

int4 quantity;

dbDateTime delivery;

TYPE_DESCRIPTOR((KEY(detail, HASHED), KEY(supplier, HASHED),

FIELD(price), FIELD(quantity), FIELD(delivery)));

};

我们希望从一些混凝土供应商那儿得到混凝土的一些具体送货信

息。在关系型数据库中,该查询书写如下:

select from Supplier,Shipment,Detail where

Supplier.SID = Shipment.SID and Shipment.DID = Detail.

DID

and Supplier.company like ? and Supplier.address like ?

and Detail.name like ?

在FastDB 中,该查询书写如下:

dbQuery q = "detail.name like",name,"and supplier.company like",

company,

"and supplier.address like",address,"order by price";

FastDB 首先在表Detail 中根据索引号和查找条件进行查找。然后,

它会根据另一个索引号来定位找到的具体运输的信息。再后,就用顺序

查找来检查查询语句的剩下部分。

函数

FastDB允许用户定义自己的函数和操作符。函数含有至少一个但不

超过三个的参数,这些参数必须是字符串型, 整型, 布尔型, 指针型

或用户自定义类型(原始二进制)。返回值的类型必须是整型,实型,字

符串型或布尔型。

应该通过宏USER_FUNC(f) 来注册用户函数,这个宏创建类

dbUserFunction 的一个静态对象,将函数指针和函数名结合起来。

在程序中有两种方法来实现这些函数。第一种方式只适用带有一

个参数的函数。这个参数必须是int8, real8, char* 类型的。函数

的返回类型必须是int8, real8, char* or bool 。如果函数多于一个

参数,或者可以接收其他类型的参数(具有多态性),那么就要将参

数作为指针传递给结构dbUserFunctionArgument。这个结构包含类型

字段,在执行函数的时候可以用这个值来检测传递过来的函数的类

型,并且能够合并参数值。 下面的表格包含参数类型到取得的参数值

之间的映射。

例如,下面的语句使得我们能够在SQL 语句中使用sin 函数:

#include <math.h>

...

USER_FUNC(sin);

函数只可以在定义了它们的程序中使用。函数不能被其他应用

程序和交互SQL 访问。如果函数返回string 类型,通过new 操作

能复制返回的字符串,这是因为在复制完毕后FastDB 将会调用destructor



在FastDB中,函数参数可以被扩在圆括号中(但不是必需的)。所

以下面两个表达式都是正确的:

'$' + string(abs(x))

length string y

带有两个参数的函数可以看作是操作符。考虑一下下面的例子,定

义了一个大小写敏感查找字串的函数:

bool contains(dbUserFunctionArgument& arg1, dbUserFunctionArgument&

arg2) {

assert(arg1.type == dbUserFunctionArgument::atString

&& arg2.type == dbUserFunctionArgument::atString);

return stristr(arg1.u.strValue, arg2.u.strValue) != NULL;

}

USER_FUNC(contains);

dbQuery q1, q2;

q1 = "select * from TestTable where name contains 'xyz'";

q2 = "select * from TestTable where contains(name, 'xyz')";

在这个例子中,查询q1 和 q2 是等价的。

C++ 接口

FastDB 的主要目标之一就是提供一个灵活而方便的应用程序

语言接口。任何一个使用过ODBC 或类似的SQL 接口的人都会

明白我在说些什么。在FastDB 中,可以通过下面的方法用C++ 来

写一个查询语句,具体如下:

dbQuery q;

dbCursor<Contract> contracts;

dbCursor<Supplier> suppliers;

int price, quantity;

q = "(price >=",price,"or quantity >=",quantity,

" and delivery.year=1999";

// 输入价格和数量的值。

if (contracts.select(q) != 0) {

do {

printf("%s\n", suppliers.at(contracts->supplier)->company);

} while (contracts.next());

}



FastDB 中的数据存储在表中,这个表和C++ 中的类相对应,表的

记录对应于类的实例。下面的C++ 类型将会作为FastDB 记录的一个原

子成员被接收:

除了上面表格中指定的类型外,FastDB记录还可以包含是带有这些

成员的嵌套的结构。FastDB 并不支持使用无符号类型来简化查询,这

样做可以消除由于带符号/不带符号的比较导致的错误和减少数据库引

擎的大小。

不幸的是在运行的时候,C++并不提供方法来获得类的元信息。 (并

不是所有的编译器都支持RTTI ,并且RTTI没有提供足够的信息)。所

以,程序员要清楚地列举出包含在数据表中的类的字段。(这也可以更加

灵活地实现类和表格之间的对应。)。为了使这些映射更加方便,FastDB

提供了一组宏和类。

每一个数据库中要使用的每个C++类或结构,都要包含一个描述它

们字段的特殊方法。宏TYPE_DESCRIPTOR(field_list) 将会构建这个方

法。小圆括号中的是这个宏的单个参数,是一个类字段描述的列表。如

果你想为你的类定义一些方法,并且在数据库中使用这些方法,你就要

使用宏C L A S S _ D E S C R I P T O R ( n a m e , f i e l d _ l i s t ) ,而不用

TYPE_DESCRIPTOR。需要使用类名来获得成员函数的指针。

可以使用下面的宏来构造字段描述符:

FIELD(name)

带有指定名字的非索引字段。

KEY(name, index_type)

索引字段。index_type 应该是HASHED 和 INDEXED 状态的

结合。如果指定了HASHED 状态,FastDB 将会为把这个字段

作为关键字段的表创建一张hash 表。如果指定了INDEXED 状

态,FastDB将会为把这个字段作为关键字段的表创建一棵(一

种特殊的索引)T- 树。

UDT(name, index_type, comparator)

用户定义原始二进制类型。数据库处理这种类型的方法和处理

指定大小的字节序列同。可以在查询中使用这个字段(和查询

参数要具有相同的类型),可以建立索引或通过子句排序序使

用。通过程序员提供的比较函数来实现比较。比较函数接收到

三个参数:两个指向原始二进制对象的指针和二进制对象的大

小。index_type 的语义和宏KEY 一样。

RAWKEY(name, index)

带有预定义比较函数的原始二进制类型。这个宏只能使用于将

memcmp 看作是比较函数的宏UDT 的特定版本。

RAWFIELD(name)

UDT 宏的另一条规则,针对带有预定义比较函数memcmp 的二

进制字段,不带索引。

SUPERCLASS(name)

指定当前类的基类(当前类的父类)的信息。

RELATION(reference, inverse_reference)

在类(表)之间指定是一对一,一对多,还是多对多关系。ref

erence 和 inverse_reference 字段必须是指针或指针数组。

inverse_reference 是一个指向包含到当前表的反向指针的字段。

FastDB会自动更新反向指针,并用反向指针来优化查询。 (参照

反向指针).

OWNER(reference, inverse_reference)

在自身-成员类型的类(表)之间定义一对多或多对多关系。

当删除owner 记录的时候,所有的指定的成员记录也被删除

(串联删除)。如果成员记录指向了owner 类,那就要用宏RE

LATION 来声明。

METHOD(name)

指定类的一个方法。这个方法无参成员函数的实例,返回值的

类型是,布尔型、数值型、指针型或字符串型。方法必须在所

有类的属性之后定义。

虽然索引只能建立在原子字段上,可以为结构指定索引类型。只要

在结构的索引类型特征码上指定了这种索引,就能够在结构成员上建立

索引。这就允许程序员记录结构规则的基础上,建立或取消结构字段上

的索引。

下面的例子显示了在头文件中创建类型描述符的方法:

class dbDateTime {

int4 stamp;

public:

int year() {

return localtime((time_t*)&stamp)->tm_year + 1900;

}

...

CLASS_DESCRIPTOR(dbDateTime,

(KEY(stamp,INDEXED|HASHED),

METHOD(year), METHOD(month), METHOD(day),

METHOD(dayOfYear), METHOD(dayOfWeek),

METHOD(hour), METHOD(minute), METHOD

(second)));

};

class Detail {

public:

char const* name;

char const* material;

char const* color;

real4 weight;

dbArray< dbReference<Contract> > contracts;

TYPE_DESCRIPTOR((KEY(name, INDEXED|HASHED),

KEY(material, HASHED),

KEY(color, HASHED),

KEY(weight, INDEXED),

RELATION(contracts, detail)));

};

class Contract {

public:

dbDateTime delivery;

int4 quantity;

int8 price;

dbReference<Detail> detail;

dbReference<Supplier> supplier;

TYPE_DESCRIPTOR((KEY(delivery, HASHED|INDEXED),

KEY(quantity, INDEXED),

KEY(price, INDEXED),

RELATION(detail, contracts),

RELATION(supplier, contracts)));

};

应该为数据库中所要使用的所有的类定义类型描述符。除了定

义类型描述符外,也必须要建立C++ 类和数据库表之间的映射。

宏REGISTER(name)将完成这个功能。和宏TYPE_DESCRIPTOR

不同,不应该在头文件中使用宏 REGISTER,而应该在执行文件

中使用。结合这个类给表创建一个描述符。如果你在一个应用程序

中要使用多个数据库,可以通过宏REGISTER_IN(name,database

</CODE)在具体的数据库中登记一张表。这个宏的参数database

应该是一个指向对象dbDatabase 的指针。你可以按如下方法在数据

库中登记表:

REGISTER(Detail);

REGISTER(Supplier);

REGISTER(Contract);

在同一时间一张表(和对应的类)只能被一个数据库使用。在你打

开一个数据库的时候,FastDB 将应用程序定义的所有类引入数据库

中。如果在数据库中已经存在了同名的类,存储在数据库中的描述符

将和应用程序中的描述符进行比较。如果类定义不同,FastDB尽量将

表中的记录转换为新的格式。允许数值类型之间地任何转换(整型到

是实型,实型到整型,扩展或四舍五入)。同样,可以非常方便地处

理新字段。但是只有在空表中才能删除字段(这样做是为了避免破坏

数据)。

在装载了所有的类描述符后,FastDB 检查应用程序类描述器指定

的索引有没有都出现在数据库中,构建新的索引,删除没有用的索引。

改变表的格式、添加删除索引只有在单个应用程序访问数据库的时候才

能进行。所以第一个添加到数据库中的应用程序,可以改变数据库。其

他应用程序只能向这个数据库中添加新的类。

有一个特殊的内部数据库Metatable ,它包含数据库中其他表的信

息。C++ 程序员不需要访问这张表,因为数据库表的格式是由C++ 类

确定的。但是在一个交互的SQL程序中,可能需要通过检查这张表来得

到记录字段的信息。

从2.30 版开始, FastDB 支持autoinvrement 字段 (该字段的值是唯

一的,并由数据库自动赋值并增加)。为了能够使用它们,你应该:

1. 使用状态标志-DAUTOINCREMENT_SUPPROT 重新编译你的

FastDB 和你的应用程序(将-DAUTOINCREMENT_SUPPROT 这个标

志添加到FastDB makefile 文件的DEFS 变量中去)。

注意: 由FastDB 创建的数据库文件如果不带这个参数编译,将不

能和由DAUTOINCREMENT_SUPPORT 编译的FastDB 兼容。

2 . 如果你想使用0 以外的初始记数值, 你应该将值赋给

dbTableDescriptor::initialAutoincrementCount。所有的表都将共享它,这

样,所有的表将会有相同值的自动增加计数器。

3. Autoinvrement 字段应该是int4 类型,并且在声明的时候应该

带状态标志 AUTOINCREMENT:

4. class Record {

5. int4 rid;

6. char const* name;

7. ...

8.

9. TYPE_DESCRIPTOR((KEY(rid,

AUTOINCREMENT|INDEXED), FIELD(name), ...));

10. }

11. 当将一个带有autoincrement 字段的记录插入数据库中的时候,

不需要指定这个字段的值(它将会被忽略)。在成功插入这条记录之后,

这个字段将会被赋予特定的值(能够确保该表前面没有使用过这个值)。

12. 记录 rec;

13. // 不需要指定 rec.rid 的值

14. rec.name = "John Smith";

15. insert(rec);

16. // rec.rid 被赋予唯一的值

17. int newRecordId = rec.rid; // 可以用来确定该记录

18. 在记录被删除之后,这个值不能再次被使用。在取消事务之

后,表的自动增加计数器也会自动回滚。

Query

类query 有两种用途:

1. 建立一个查询,并且绑定查询参数。

2. 缓存编译好的查询。

FastDB 提供了对操作数'=' 和 ',' C++ 操作数的重载,来建

立带参数的查询语句。参数可以在使用它们的地方直接被指定,可以

减少参数位置和C变量之间的映射。在下面的查询例子中,指向参数

price(价格)和quantity (数量)的指针存放在查询中,所以可以用

不同的参数值多次执行这个查询。C++重载过的函数能够自动决定参

数的类型,不需要程序员提供额外的信息(这样可以减少程序出错的

可能性)。

dbQuery q;

int price, quantity;

q = "price >=",price,"or quantity >=",quantity;

由于char* 类型可以用来指定查询的一部分(例如: "price >="

也可以用来指定一个字符串类型的参数, FastDB使用一个特定的规则来

解决这种混淆。这种规则基于一个假设,假设不将一个查询文本分成两

个字符串,如("price ",">=" 或者指定多于一个参数 ("color=",color,

color)。所以FastDB 假设首字符串是查询文本的一部分,之后转换为

operand mode (操作数模式)。在operand mode(操作数模式)中,

FastDB 将参数 char* 看作是查询参数,并且变成查询文本模式,等

等....可以不使用"syntax sugar" 和通过方法dbQuery::append

(dbQueryElement::ElementType type, void const* ptr) 显式地构建查询

元素。必须通过方法dbQuery::reset() ('operator=' does it automatically).

来重设查询。

不能使用C++ 的数值常量来作为查询参数,因为参数是通过指针

来访问的。但是可以使用字符串常量,因为字符串是值传递的。在查

询中指定字符串参数有两种方法:使用字符串缓存或指向数阻止真的

指针:

dbQuery q;

char* type;

char name[256];

q = "name=",name,"and type=",&type;

scanf("%s", name);

type = "A";

cursor.select(q);

...

scanf("%s", name);

type = "B";

cursor.select(q);

...

查询变量不能够作为一个函数的参数来传递,也不能够分配给另外

一个变量。当FastDB 编译查询语句的时候,它将编译过的树保存在这

个对象中。下一次使用这个查询的时候,不需要进行编译,并且可以使

用已经存在的编译树。它节省了查询编写的时间。

FastDB 提供了两种方案来集成数据库中的用户定义的类型。第一

——类方法的定义——早已经提过了。另外一种方法和查询结构有关。

程序员应该定义方法,这个方法并不真正计算出结果,而是返回一个表

达式(和预定义的数据库类型相一致),这个表达式执行必要的计算。最

好通过例子来描述这一点。FastDB没有内嵌的日期时间类型。程序员可

以使用通用的C++ 的类 dbDateTime 来替代它。这个类可以定义方法,

指定按顺序排列的特定的日期时间类型,并可以使用关系操作符来比较

两个日期的大小:

class dbDateTime {

int4 stamp;

public:

...

dbQueryExpression operator == (char const* field) {

dbQueryExpression expr;

expr = dbComponent(field,"stamp","=",stamp;

return expr;

}

dbQueryExpression operator != (char const* field) {

dbQueryExpression expr;

expr = dbComponent(field,"stamp","<>",stamp;

return expr;

}

dbQueryExpression operator < (char const* field) {

dbQueryExpression expr;

expr = dbComponent(field,"stamp",">",stamp;

return expr;

}

dbQueryExpression operator <= (char const* field) {

dbQueryExpression expr;

expr = dbComponent(field,"stamp",">=",stamp;

return expr;

}

dbQueryExpression operator > (char const* field) {

dbQueryExpression expr;

expr = dbComponent(field,"stamp","<",stamp;

return expr;

}

dbQueryExpression operator >= (char const* field) {

dbQueryExpression expr;

expr = dbComponent(field,"stamp","<=",stamp;

return expr;

}

friend dbQueryExpression between(char const* field, dbDateTime& from,

dbDateTime& till)

{

dbQueryExpression expr;

expr=dbComponent(field,"stamp","between",from.stamp,"and",till.

stamp;

return expr;

}

friend dbQueryExpression ascent(char const* field) {

dbQueryExpression expr;

expr=dbComponent(field,"stamp");

return expr;

}

friend dbQueryExpression descent(char const* field) {

dbQueryExpression expr;

expr=dbComponent(field,"stamp"),"desc";

return expr;

}

};

所有这些方法都接收记录的一个字段名作为参数。使用这个名字可

以缩减整条记录的名字。这个可以通过类dbComponent来实现,这个构

造函数接收结构字段和结构成员名并返回以点符号表示的一个结构成

员。使用类dbQueryExpression 来收集表达项。表达式自动扩在圆括号

中,取消和优先操作符的冲突。

因此,假设包含类型是dbDateTime的字段记录,可以象下面一样来

构建查询:

dbDateTime from, till;

q1 = between("delivery", from, till),"order by",ascent("delivery");

q2 = till >= "delivery";

除了这些方法之外,通过这种途径还可以定义某些类的特定方法,

例如:region类型方法交接。这种方案的好处是,数据库引擎可以作用

于预定义类型,并且可以使用索引和其他一些优化机制来处理这样的查

询。从另一方面来说,保护了对类实现的包装,所以当类表现改变的时

候,程序员不需要重新书写查询。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
概述FastDB是一个高效率内存数据库系统,具有实时性能和方便的C++接口。 FastDB并不支持客户端/服务器结构,所有使用FastDB数据库的应用程序都必须运行在同一台主机上。FastDB为具有主导读取访问模式的应用程序作了优化。通过消除数据传输的开销和使用高性能的锁工具实现了查询执行的高速度。数据库文件和使用该数据库的每一个应用程序占用的虚拟内存空间相映射。所以查询在应用程序的任务中执行,不需要进行任务切换和数据传输。在FastDB中,通过原子指令来实现对数据库并发访问的同步,对查询处理几乎不增加任何开销。FastDB假设整个数据库都在当前内存中,并且在这个假设的基础上优化查询算法和结构。另外,数据库缓存管理几乎不会给FastDB增加任何开销,同时FastDB也不需要在数据库文件和缓冲池中进行数据传送。这就是为什么FastDB比将所有数据放在缓冲池中的传统数据库明显速度快的原因。   FastDB支持事务、在线备份和系统崩溃之后的自动恢复。事务提交协议基于一个影子根页算法,对数据库执行原子更新操作。恢复操作执行起来非常快,给关键应用程序提供了高效率。另外,它还取消了事务日志,提高了系统的整体性能,并且能够更加有效地使用系统资源。   FastDB是面向应用程序的数据库,使用应用程序的类信息来构建数据库的表。FastDB支持自动系统赋值,只允许你在一个地方——你的应用程序的类中,改变它们的值。FastDB为从数据库中提取数据提供了一个灵活而方便的接口。使用类似于SQL的语言来书写查询语句。这些非原子字段、嵌套数组、用户自定义类型和方法、直接指向对象内部的指针等后关系性能,简化了数据库应用程序的设计,并且使得它们更加高效。   虽然FastDB的优化是基于整个数据库都存放在机器的物理内存的这个假设上的,我们依然可以将FastDB使用在那些大小超过系统物理内存数据库上。最后,标准操作系统的交换机制将会起作用。但是所有的FastDB的算法和结构的优化都是基于数据存放在内存中这个假设上的,所以数据交换的效率不会很高。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值