(本文内容主要是通过学习官网、博客及阅读官网demo做出的总结)
FastDB是一个内存数据库,通过把数据加载到内存中实现对数据的操作,相比于传统的数据库,操作的速度更快,但是存在一个缺点就是使用FastDB数据库的应用程序都必须运行在同一台主机上。
1 简单介绍
2 访问类型
3 使用流程
4 遇到的问题及解决办法
5 代码实例
6 总结
一、 简单介绍
1 将数据完全加载到内存,在内存中实现对数据的管理;
2 只读模式线程、单个更改模式线程和多个只读模式线程间并发执行;
3 最小单位块:分配量子(16字节);
4 事物提交协议基于一个影子根页算法,对数据库执行原子更新操作,恢复效率高;
影子根页算法:数据库中每条对象都具有唯一标识符用作一个数组(对象索引)的下标,元素值表示对象的一个句柄。(FastDB中存在两个索引:当前索引和影子索引)
5 内存数据结构组织:HASH、T树。
HASH:具有最高查找效率(不适用于插入操作中,键值冲突发生频率较高的场景);
T树:仅次于HASH(不适用于有频繁的添加、删除动作的场景);
在FastDB中,声明为HASH的KEY,采用HASH结构,声明为INDEXED的KEY采用T树结构。
二、 访问类型
1 一共四种访问类型:dbDatabase::dbReadOnly、dbDatabase::dbAllAccess、dbDatabase::dbConcurrentRead、dbDatabase::dbConcurrentUpdate;
2 FastDB不支持客户端/服务器结构(内存数据库,不能跨服务器);
3 dbDatabase::dbReadOnly:默认是这种模式;
4 dbDatabase::dbAllAccess:如果该进程使用了insert、update、delete等修改数据库的操作,其他访问该库的进程的所有操作(包括open、select)都会被阻塞,直到该操作提交或回滚。(该模式我自己在写代码时还没有用到过);
5 dbDatabase::dbConcurrentUpdate:如果某进程对数据进行修改性操作,同时另外的进程使用 dbDatabase::dbReadOnly 或者 dbDatabase::dbConcurrentRead 读数据,不会出现阻塞情况。
但是 dbDatabase::dbReadOnly 会把未提交的脏数据读出来,而dbDatabase::dbConcurrentRead不会;
6 多个进程使用 dbDatabase::dbConcurrentUpdate 实际效果和 dbDatabase::dbAllAccess 一样(阻塞);
7 不要把 dbDatabase::dbConcurrentUpdate 和 dbDatabase::dbConcurrentRead 模式混用,不能同时启动两个线程,其中一个用 dbConcurrentUpdate 模式打开数据库,另一个用 dbConcurrentRead 模式;
8 在 dbDatabase::dbConcurrentUpdate 模式下不要使用 dbDatabase::precommit方法。
三、 使用流程(Linux)
1 在fastdb官网下载安装包进行安装;
2 在服务器上安装fastdb,可以参考这个博主的文章(fastdb安装配置);
3 编写测试文件,或者直接在下载的fastdb安装包中找一个example文件进行测试;
4 编写Makefile文件,注意添加以下内容:-I/usr/local/include/fastdb -L/usr/local/lib -lrt -lz -lfastdb (上面的内容根据自己的实际安装路径进行调整)。
5 编译代通过,运行代码。
四、 遇到的问题及解决办法
1 undefined reference to dbDatabase::~ 等报错
原因 :没有成功引入相关的头文件或者.so文件;
解决办法:编写Makefile文件时没有添加相关的依赖(参考上面的第4点内容);
2 Incompatibility between headers and library:6 vs. 4
原因:fastdb会假设绝大多数Linux是64-bit的,如果测试的是32-bit的,就会报错;
解决办法:根据安装目录,找到fastdb目录下的config.h头文件,将以下代码注释即可。(运行的环境是64-bit的,依旧报错,感觉不是这个原因,但是按照这种方法可以解决,很迷~)
//#if!defined(_WIN32)||defined(_WIN64)//most unixes are now 64-bit,while 32-bit windows is still quite popular
//#define LARGE_DATABASE_SUPPORT
//#endif
3 当运行的数据大概达到一千万条以上的时候,即生成的文件内容达到2G之后,程序就会抛出异常,自动停止,这个问题找了很久一直也没有解决。
五、 代码实例
下面的代码只是一个简单的实例,包含了表关联(删除一个表的数据,另一个表相关的记录也会被删除),数组的使用,插入数据、查询数据、删除数据。
(ps:下面的代码是手撕的(不是copy的服务器上面的代码),没有实际测过,可能会有一些小小的问题,欢迎指出,不过大体上是没有什么问题的。)
#define _LARGEFILE_SOURCE
#define _LARGEFILE64_SOURCE
#define _FILE_OFFSET_BITS 64
#include "fastdb.h"
#include "database.h"
#include <iostream>
#include <stdio.h>
#include <sys/time.h>
#include <sring>
using namespace std;
USE_FASTDB_NAMESPACE
dbDatabase db;
#define random(a,b) (rand()%(b-a))+a+1 //生成随机数
#define TeacherNum 5 //老师个数
#define StudentNum 20 //学生个数
class Student;
class Teacher
{
public:
const char* teacher_name;
dbArray<dbReference<Student>> std_code;
TYPE_DESCRIPTOR((KEY(teacher_name,INDEXED),OWNER(std_code,tech_code)));
};
class Student
{
public:
const char* std_name;
dbArray<int4> scores; //Chinese English Math
dbReference<Teacher> tech_code;
TYPE_DESCRIPTOR((KEY(std_name,INDEXED),
FIELD(scores),
RELATION(tech_code,std_code)));
};
REGISTER(Teacher); //注册Teacher表
REGISTER(Student); //注册Student表
//生成老师的编号
void getRandomCharTeach(char * randnum)
{
int num = random(0,10);
snprintf(randnum,sizeof(randnum),"%d",num); //int类型转换为char *
}
//生成学生的编号
void getRandomCharStu(char * randnum)
{
int num = random(20,50);
snprintf(randnum,sizeof(randnum),"%d",num); //int类型转换为char *
}
//生成0-100之间的随机数
int4 getRandomIntSocre()
{
int num = random(0,100);
return num;
}
//查询老师编号
void selectTeachName()
{
cout<<"***** selectTeachName *****"<<endl;
dbCursor<Teacher> cursorTeach; //只读游标
int n = cursorTeach.select(); //查询
cout<<"Teacher的数量为:"<<n<<endl;
if(0<n)
{
do
{
cout<<"teacher_name = "<<cursorTeach->teacher_name<<" ."<<endl;
}while(cursorTeach.next()); //游标向后滚
}
}
//查询学生信息
void selectStuInfo()
{
cout<<"***** selectStuInfo *****"<<endl;
dbCursor<Student> cursorStu; //只读游标
int n = cursorStu.select(); //查询
cout<<"Student的数量为:"<<n<<endl;
if(0<n)
{
do
{
cout<<"std_name = "<<cursorStu->std_name<<" , "<<
"Chinese = "<<cursorStu->scores[0]<<" , "<<
"English = "<<cursorStu->scores[1]<<" , "<<
"Math = "<<cursorStu->scores[2]<<" ."<<endl;
}while(cursorStu.next()); //游标向后滚
}
}
//删除所有数据
void removeAllRecord()
{
cout<<"***** removeAllRecord *****"<<endl;
dbCursor<Teacher> cursorTeach(dbCursorForUpdate); //写游标对象
cursorTeach.removeAll();
dbCursor<Student> cursorStu(dbCursorForUpdate); //写游标对象
cursorStu.removeAll();
}
int main()
{
Teacher techer;
Student student;
if(db.open(_T("test")))
{
cout<<"Start inserting data..."<<endl;
for(int i = 0; i < TeacherNum; i++)
{
char tech_num[2];
getRandomCharTeach(tech_num);
techer.teacher_name = tech_num;
dbReference<Teacher> techer_class = insert(techer);
cout<<"Insert "<<techer.teacher_name<<" to Teacher."<<endl;
db.commit();
//插入多个学生信息对应当前老师
for(int j = 0; j < StudentNum; j++)
{
char std_num[2];
getRandomCharStu(std_num);
student.std_name = std_num;
int Chinese = getRandomIntSocre(); //语文成绩
int English = getRandomIntSocre(); //英语成绩
int Math = getRandomIntSocre(); //数学成绩
student.scores(3); //数组大小为3
student.scores.putat(0,Chinese); //把各科成绩放到数组中
student.scores.putat(1,English);
student.scores.putat(2,Math);
student.tech_code = techer_class; //把当前学生的信息与对应老师相关联
insert(student);
cout<<"Insert std_name : "<<student.std_name<<" , "<<
"Chinese : "<<Chinese<<" , "<<
"English : "<<English<<" , "<<
"Math : "<<Math<<" ."<<endl;
}
db.commit();
}
//查询、删除的实例可以在open表之后直接调就可以了,这里就不写了
}
db.commit();
return 0;
}
六、 总结
fastDB确地有它的优点,但是现有的相关资料比较少,所以使用起来会有难度,重点是文件超过2G的抛异常的问题没有找到相关的解决方案,如果有朋友有相关解决方案,可以一起交流学习。
----------如有侵权,联系删除!