数据管理概述,《数据管理技术》
资料库
我们已经知道如何使用文件来存储数据,那么我们为什么要使用数据库呢?很简单。在某些情况下,数据库特性提供了解决问题的更好方法。使用数据库比存储文件更好有两个原因:
我们可以存储大小变化的数据记录,这对于普通的非结构化文件来说是很难实现的。
数据库存储和数据读取使用索引。最大的好处是这个索引不一定是简单的记录号,在普通文件中容易使用,而是一个字符串。
Dbm数据库
的所有Linux版本以及大多数Unix变体都将附带一个名为dbm data的基本但非常有效的例行数据存储集。Dbm数据库非常适合存储相对静态的索引数据。一些数据库纯化论者会认为dbm根本不是数据,只是一个简单的索引文件存储系统。然而,X/Open规范使用dbm作为数据库,所以在我们的书中也会考虑到它。
dbm简介
尽管MySQL和PostgreSQL等免费关系数据库引起了人们的兴趣,但dbm数据库仍然在Linux中扮演着非常重要的角色。RPM的发布版本如RedHat有SUSE,dbm作为安全包信息的底层存储介质。LDAP是Open LDAP的一个开源实现,它也使用dbm作为存储机制。dbm的优点是很容易构建成二进制包,因为没有安装单独的服务器,不安装底层库也没有风险。
数据库dbm允许我们使用索引来存储不同大小的数据结构,然后使用索引或简单地搜索数据库来获取数据。对于经常访问但很少更新的数据,Dbm是最好的选择,因为它的特点是创建实体很慢,但读取起来相当快。
在这一点上,我们遇到了一个问题:近年来,出现了几个具有不同API和特性的dbm数据库变体。有原来的dbm集,新的dbm集,叫做ndbm,还有GNU实现,gdbm。GNU实现可以同时模拟旧的dbm和ndbm接口,但本质上它有着与其他实现完全不同的接口。不同的Linux版本自带不同版本的dbm库,虽然大部分都选择使用gdbm库,因为它可以模拟另外两种接口类型。
在这里,我们将重点关注ndbm接口,因为它是由X/Open标准化的,也因为它比gdbm实现更容易使用。
获取dbm
如果我们对其他dbm实现感兴趣,在ftp://ftp.cs.berkeley.edu/ucb/4bsd/或http://www.openbsd.org有BSD授权版本。Sleepycat软件(http://www.sleepycat.com)有开源产品,Berkeley Data也支持dbm/ndbm接口。GNU版本可以在http://www.gnu.org获得。
修复故障并重新安装dbm。
本章是在假设我们已经安装了与X/OPEN兼容的dbm版本的基础上编写的。如果我们在编译这些例子的时候遇到问题,或者因为没有ndbm.h的头文件,编译器会抱怨没有定义dbm,或者编译器的链接过程失败——这时候我们可以尝试安装dbm库的GNU版本的更新版本来解决这些问题。
检查我们拥有的dbm版本最简单的方法就是找到文件gdbm.h、ndbm.h和dbm.h,我们会发现后两者位于一个子目录中,比如/usr/include/gdbm,这意味着底层实现是gdbm,但是已经为我们安装了一个兼容的库文件。如果在我们的系统上找不到ndbm.h文件,我们可以自己安装GNU gdbm接口。首先,创建一个临时目录。然后去http://www.gnu.org网站搜索最新版本的gdbm库。文件名可能是gdbm_?_?tar.gz的形式。将文件下载到我们的临时目录,并使用“tar zxvf filename”解压文件。然后阅读自述文件,它将告诉我们如何编译和安装库文件。通常我们首先运行。/configure命令来检测我们的系统是如何配置的,然后我们运行make命令来编译程序。最后,我们运行make install和make install-compat来安装基本文件和其他兼容文件。也许我们需要root权限来运行安装步骤,但是首先运行带有-n选项的make命令(例如,make -n install)来检测将要执行的操作通常是个好主意。
现在我们应该有了ndbm的X/OPEN兼容版本,通常位于我们系统的/usr/local部分。默认情况下,我们的编译器设置可能不会搜索这些位置。在这种情况下,我们需要在gcc命令后添加-I/usr/local/include选项来查找头文件,并添加-L/usr/local/lib选项来查找库文件:
$ gcc-I/usr/local/include program . c-L/usr/local/lib-o program-LG DBM
我们的系统已经有了ndbm.h文件,但是在/usr/include子目录中,例如/usr/include/gdbm,我们可能需要在编译后的行中添加-I/usr/include/gdbm:
$ gcc-I/usr/include/gdbm program . c-o program-LG DBM
例行数据库管理
类似于第6章讨论的curses,dbm utility由一个头文件和一个库文件组成,当程序被编译时,它们必须被链接。库简称dbm,所以我们在编译命令行添加-ldbm(或者-lgdbm)进行链接。头文件是ndbm.h
在我们试图解释这些功能之前,理解dbm数据库试图实现什么是非常重要的。一旦我们理解了这一点,我们将对如何使用dbm函数有更好的理解。
dbm数据库的基本元素是要存储的数据块和作为读取数据的键值的数据块。对于要存储的每个数据块,每个dbm数据库都必须有一个唯一的键值。对键值或数据没有严格的限制,也没有为使用过大的数据和键值定义错误。规范允许实现将键值/数据的大小限制为1023字节,但通常没有限制,因为实现比他们需要的更灵活。键值是存储数据的索引。
为了将这些块作为数据进行操作,ndbm.h包含的文件定义了一个名为datum的新类型。此类型的实际内容与实现相关,但它必须至少具有以下成员:
void * dptr
size_t尺寸
Datum是由typedef定义的类型。同时,ndbm.h文件中有一个类型定义dbm,是一个访问数据的结构,类似于访问文件的文件。dbm类型的内部是与实现相关的,不应该使用。
当我们使用dbm库引用一个数据块时,必须声明一个datum,设置dptr指向数据的开头,设置dsize包含它的大小。要存储的数据和用于访问数据的索引可以通过数据类型来引用。
类型dbm的思想与文件类型的思想非常相似。当我们打开一个dbm数据库时,系统会创建两个物理文件,一个带有。pag扩展和另一个。目录扩展名。并返回一个dbm指针,用于访问这两个文件。这两个不能直接读写,因为它们只能通过dbm例程访问。
如果我们使用本地gdbm库,那么这两个文件已经被合并,因此只创建了一个文件。
如果我们熟悉SQL数据,那么我们会注意到dbm数据中没有关联的表或列。这些结构不是必需的,因为dbm不会对要存储的每个数据项施加固定的大小,也不需要数据的内部结构。dbm库对非结构化二进制数据进行操作。
Dbm访问功能
现在我们已经介绍了dbm库的基本工作,可以详细了解dbm函数了。的主要dbm函数原型如下:
#包含ndbm.h
DBM * DBM _ open(const char * filename,int file_open_flags,mode _ t file _ mode);
int DBM _ store(DBM *数据库_描述符,基准键,基准内容,int store _ mode);
基准DBM _ fetch(DBM *数据库_描述符,基准键);
void DBM _ close(DBM *数据库_描述符);
dbm_open
这个函数可以用来打开一个现有的数据库或者创建一个新的数据库。filename参数是一个不带。目录或。pag扩展。
其余参数与我们已经在第3章中介绍过的open函数的第二个和第三个函数相同。我们也可以使用相同的#define定义。第二个参数可以控制数据是可读、可写还是读/写。如果我们想要创建一个新的数据,标签必须是O_READ和o _ create以允许文件创建。第三个参数指定要创建的文件的初始权限。
返回一个指向Dbm类型的指针。这个指针将在所有后续的数据库访问中使用。如果失败,它将返回(DBM *)0。
数据库管理存储
我们使用这个函数在数据库中存储数据。正如我们前面提到的,所有数据都必须用唯一的索引存储。为了定义我们想要存储的数据和用于引用的索引,我们必须设置两种数据类型:一种指向索引,另一种指向实际数据。最后一个参数store_mode控制使用现有索引存储数据时会发生什么。如果设置为dbm_insert,则存储失败,dbm_store返回1。如果设置为dbm_replace,新数据将覆盖现有数据,dbm_store将返回0。如果是另一个错误,dbm_store将返回负值。
dbm_fetch
dbm_fetch函数用于从数据库中读取数据。该函数将前面的dbm_open调用返回的指针和必须设置为指向索引的数据类型作为参数,并将返回一个数据类型。如果在数据库中成功搜索到与所用索引相关的数据,返回的数据结构将设置dptr和dsize的值,以指向返回的数据。如果索引搜索不成功,dptr将被设置为null。
需要记住的一点是,dbm_fetch只会返回一个指向数据的指针。实际数据仍将存储在dbm库中的本地存储空间中,并且应该在调用其他dbm函数之前复制到程序变量中。
dbm_close
这个函数将关闭用dbm_open打开的数据库,并将之前调用dbm_open返回的dbm指针作为参数传递给它。
实验-一个简单的dbm数据库
现在我们知道了dbm数据库的基本功能,我们知道了足够的知识来编写我们的第一个dbm程序:dbm1.c在这个程序中,我们将使用一个名为test_data的结构。
1.在程序的开始,会有#include、#define、main函数和test_data结构的声明:
#包括unistd.h
#包含stdlib.h
#包含stdio.h
#包含fcntl.h
#包含ndbm.h
#包含字符串. h
#定义测试数据库文件"/tmp/dbm1测试"
#定义项目_已用3
结构测试数据{
char misc _ chars[15];
int any _ integer
char more _ chars[21];
};
int main()
{
2在主函数中,我们设置items_to_store和items_received结构、键值字符串和数据类型:
struct test _ data ITEMS _ to _ store[ITEMS _ USED];
结构测试_数据项_已检索;
char key _ to _ use[20];
int i,result
datum key _ datum
基准数据_数据;
DBM * DBM _ ptr;
3在声明了一个指向功率类型结构的指针之后,我们现在可以打开我们的测试数据库进行读写操作,如果需要则要创建这个数据库:
dbm_ptr=dbm_open(TEST_DB_FILE,O_RDWR O_CREAT,0666);
如果(!dbm_ptr) {
fprintf(stderr,"未能打开数据库/n ");
退出(退出_失败);
}
四现在我们向物品_到_商店结构添加一些数据:
memset(items_to_store,/0 ,sizeof(items _ to _ store));
strcpy(items_to_store[0]).杂项字符,"第一!");
物品商店[0]。any _ integer=47
strcpy(items_to_store[0]).more_chars," foo ");
strcpy(items_to_store[1]).misc_chars," bar ");
items_to_store[1]。any _ integer=13
strcpy(items_to_store[1]).更多_字符,"不吉利?");
strcpy(items_to_store[2]).杂项字符,"第三");
项目_到_商店[2].any _ integer=3;
strcpy(items_to_store[2]).more_chars," baz ");
memset(items_to_store,/0 ,sizeof(items _ to _ store));
strcpy(items_to_store[0]).杂项字符,"第一!");
物品商店[0]。any _ integer=47
strcpy(items_to_store[0]).more_chars," foo ");
strcpy(items_to_store[1]).misc_chars," bar ");
items_to_store[1]。any _ integer=13
strcpy(items_to_store[1]).更多_字符,"不吉利?");
strcpy(items_to_store[2]).杂项字符,"第三");
项目_到_商店[2].any _ integer=3;
strcpy(items_to_store[2]).more_chars," baz ");
5对于每一个记录,我们需要为将来的引用构建一个键值。这是每一个字符串和整数的第一个字符。这个键值然后会使用关键数据进行榱,而数据_数据指向物品_到_商店记录。然后我们在数据库中存储这些数据。
for(I=0;一、物品_已用;i ) {
sprintf(key_to_use," %c%c%d ",
要存储的项目[i].misc_chars[0],
要存储的项目[i].more_chars[0],
要存储的项目[i].any _ integer);
关键数据。dptr=(void *)key _ to _ use;
关键数据。dsize=strlen(key _ to _ use);
数据_数据。dptr=(void *)items _ to _ store[I];
数据_数据。dsize=sizeof(struct test _ data);
result=dbm_store(dbm_ptr,key_datum,data_datum,DBM _ REPLACE);
如果(结果!=0) {
fprintf(stderr," dbm_store在关键字%s/n上失败“,key _ to _ use);
出口(2);
}
}
6 接下来我们要测试我们是否可以取得这些新数据,最后,我们必须关闭数据库:
sprintf(key_to_use," bu%d ",13);
key _ datum.dptr=密钥使用
关键数据。dsize=strlen(key _ to _ use);
data_datum=dbm_fetch(dbm_ptr,key _ datum);
if (data_datum.dptr) {
printf("数据检索/n ");
memcpy( item_retrieved,data_datum.dptr,data_datum。dsize);
printf("检索到的项目- %s %d %s/n ",
item_retrieved.misc_chars,
item_retrieved.any_integer,
项目_已检索。更多_字符);
}
否则{
printf("未找到关键字%s/n的数据“,key _ to _ use);
}
DBM _克洛斯(DBM _ ptr);
退出(退出_成功);
}
当我们编译并且运行这个程序时,我们可以得到下面的简单输出:
$ gcc-o DBM 1-I/usr/include/gdbm DBM 1。c-LG DBM
$ ./dbm1
检索的数据
找回物品-栏13不吉利?
如果我们的器是以兼容模式安装的,我们就会得上面的输出结果。如果编译失败,我们就需要按照我们前面所描述的来安装GNU gdbm库兼容文件,并且/或是在我们编译时指定额外的目录:
$ gcc-I/usr/local/include-L/usr/local/lib-o DBM 1 DBM 1。c-ldbm
如果仍然编译失败,试着将-ldbm部分替换为-lgdbm:
$ gccI/usr/local/includeL/usr/local/lib-o DBM 1 DBM 1。DBM LG公司
工作原理
首先,我们打开数据库,如果需要,则要创建数据库。然后我们需要填充我们用作测试数据的物品_到_商店的三个成员。对于这三个成员的每一个,我们创建了一个索引键值。为了使其简单,我们使用两个字符串的第一个字符,加上存储的整数作为键值。
然后我们建立两个数据结构,一个用于键值,另一个用于存储数据。在数据库中存储这三项之后,我们组织一个新的键值并设置一个数据结构指向它。然后我们使用这个键值从数据库中读取数据。我们通过检测返回数据中的dptr不为空来确保成功。如果不为空,我们可以将获取的数据(存储在dbm库中)复制到我们自己的结构中。在这里,我们应该小心地使用dbm_fetch来返回大小(如果我们不这样做,并且正在使用不同大小的数据,我们将尝试复制不存在的数据)。最后,我们打印出获得的数据,以表明我们正确地读取了数据。