数据库我们已经了解了如何使用文件来存储数据,所以为什么我们应该使用数据库呢?非常简单,在某些环境下,数据库特性提供了更好的方法来解决问题。使用数据库要好于存储文件,有下面两个理由:
我们可以存储尺寸上变化的数据记录,而这使用普通,无结构的文件是难于实现的。
数据库的存储与数据的读取使用索引。最大的好处就在于这个索引不必是一个简单的记录号,这在普通文件中是很容易使用的,而是字符串。
dbm数据库所有的Linux版本,以及大多数的Unix变种,都会带有一个基本,但是十分有效的例程数据存储集合,名为dbm数据。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实现感兴趣,有一个BSD授权的版本可以在ftp://ftp.cs.berkeley.edu/ucb/4bsd/或是http://www.openbsd.org处得到。Sleepycat软件(http://www.sleepycat.com)有一个开源产品,伯克利数据也支持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>'来解开文件。然后阅读README文件,这会告诉我们如何编译与安装库文件。通常我们首先运行./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 -lgdbm
我们的系统已经拥有了ndbm.h文件,但是在/usr/include子目录中,例如/usr/include/gdbm,我们也许需要在编译行添加-I/usr/include/gdbm:
$ gcc -I/usr/include/gdbm program.c -o program -lgdbm
dbm例程与我们在第六章所讨论的curses类似,dbm实用程序由程序在编译时必须链接的一个头文件与一个库文件所组成。库文件简单的称之为dbm,所以我们在编译命令行中添加-ldbm(或-lgdbm)来进行链接。头文件为ndbm.h。
在我们试着解释这些函数之前,理解dbm数据库所要实现在的目标是十分重要的。一旦我们理解这些,我们就会更好的理解如何使用dbm函数。
dbm数据库的基本元素是一个要存储的数据块,以及一个作为读取数据的键值的数据块。每一个dbm数据库对于要存储的每一个数据块必須具有唯一的键值。对于键值或是数据并没有严格的限制,对于使用过大的数据与键值也没有定义任何错误。规范允许实现限制键值/数据的大小为1023字节,但是通常而言并没有任何限制,因为实现要比他们所需要的更为灵活。键值的作用是所存储数据的索引。
要将这些块作为数据进行操作,ndbm.h包含文件定义了一个名为datum的新类型。这个类型的实际内容是与实现相关的,但是他至少要具有下列成员:
void *dptr;
size_t dsize
datum是一个由typedef定义的类型。同时在ndbm.h文件中还有一个类型定义dbm,他是一个用于访问数据的结构,与访问文件的FILE相类似。dbm类型的内部是与实现相关的,并且绝不应被使用。
当我们使用dbm库时要引用一个数据块,我们必须声明一个datum,设置dptr指向数据起始处,并且设置dsize包含其尺寸。要存储的数据以及访问数据所用的索引都可以通过datum类型来引用。
dbm类型与FILE类型的思想最为类似。当我们打开一个dbm数据库时,系统就会创建两个物理文件,一个以.pag为扩展名,另一个以.dir为扩展名。并且返回一个dbm指针,使用这个指针来访问这两个文件。这两个不要进行直接的读写操作,因为他们的本意是只通过dbm例程来访问的。
如果我们使用本地的gdbm库,那么这两个文件已经进行合并,从而只创建一个文件。
如果我们熟悉SQL数据,那么我们就会注意到在dbm数据中并没有相关联的表或列。这些结构不是必须的,因为dbm并不会在数据的每一个要存储的数据项目上强制固定的尺寸,也不会要求数据的内部结构。dbm库在非结构化的二进制数据上进行操作。
dbm访问函数现在我们已经介绍了dbm库工作的基础,我们可以详细的了解一下dbm函数了。主要的dbm函数原型如下:
#include <ndbm.h>
DBM *dbm_open(const char *filename, int file_open_flags, mode_t file_mode);
int dbm_store(DBM *database_descriptor, datum key, datum content, int store_mode);
datum dbm_fetch(DBM *database_descriptor, datum key);
void dbm_close(DBM *database_descriptor);
dbm_open这个函数可以用来打开一个已经存在的数据库,也可以用来创建一个新的数据库。filename参数是一个基础文件名,没有.dir或是.pag扩展名。
其余的参数与open函数的第二个和第三个函数相同,这我们已经在第三章进行了介绍。我们也可以使用相同的#define定义。第二个参数可以控制数据是否可以进行读,写或是读写操作。如果我们要创建一个新的数据,标记必须为O_READ与O_CREAT来允许创建文件。第三个参数指定了将要创建的文件的初始权限。
dbm_open返回一个指向DBM类型的指针。这个指针会用在接下来的所有数据库访问中。如果失败,就会返回(DBM *)0。
dbm_store我们使用这个函数来向数据库中存入数据。正如我们在前面所提到的,所有的数据必须使用唯一的索引进行存储。要定义我们希望存储的数据以及用于引用的索引,我们必须设置两个datum类型:一个指向索引,而另一个指向实际的数据。最后一个参数,store_mode,控制当使用已经存在的索引存储数据时会出现什么情况。如果设置为dbm_insert,则会存储失败,并且dbm_store返回1。如果设置为dbm_replace,新数据就会覆盖已经存在的数据,并且dbm_store返回0。如果是其他错误,dbm_store就会返回一个负值。
dbm_fetch
dbm_fetch函数用于由数据库中读取数据。这个函数使用前面dbm_open调用返回的指针,以及一个必须设置指向索引的datum类型作为参数,并且会返回一个datum类型。如果与所用索引相关的数据在数据库中查找成功,返回的datum结构就会将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结构的声明:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <ndbm.h>
#include <string.h>
#define TEST_DB_FILE “/tmp/dbm1_test”
#define ITEMS_USED 3
struct test_data {
char misc_chars[15];
int any_integer;
char more_chars[21];
};
int main()
{
2 在main函数中,我们设置items_to_store以及items_received结构,键值字符串,以及datum类型:
struct test_data items_to_store[ITEMS_USED];
struct test_data item_retrieved;
char key_to_use[20];
int i, result;
datum key_datum;
datum data_datum;
DBM *dbm_ptr;
3 在声明了一个指向dbm类型结构的指针之后,我们现在可以打开我们的测试数据库进行读写操作,如果需要则要创建这个数据库:
dbm_ptr = dbm_open(TEST_DB_FILE, O_RDWR | O_CREAT, 0666);
if (!dbm_ptr) {
fprintf(stderr, “Failed to open database\n”);
exit(EXIT_FAILURE);
}
4 现在我们向items_to_store结构添加一些数据:
memset(items_to_store, ‘\0’, sizeof(items_to_store));
strcpy(items_to_store[0].misc_chars, “First!”);
items_to_store[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].more_chars, “unlucky?”);
strcpy(items_to_store[2].misc_chars, “Third”);
items_to_store[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].misc_chars, “First!”);
items_to_store[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].more_chars, “unlucky?”);
strcpy(items_to_store[2].misc_chars, “Third”);
items_to_store[2].any_integer = 3;
strcpy(items_to_store[2].more_chars, “baz”);
5 对于每一个记录,我们需要为将来的引用构建一个键值。这是每一个字符串和整数的第一个字符。这个键值然后会使用key_datum进行榱,而data_datum指向items_to_store记录。然后我们在数据库中存储这些数据。
for (i = 0; i < ITEMS_USED; i++) {
sprintf(key_to_use, “%c%c%d”,
items_to_store[i].misc_chars[0],
items_to_store[i].more_chars[0],
items_to_store[i].any_integer);
key_datum.dptr = (void *)key_to_use;
key_datum.dsize = strlen(key_to_use);
data_datum.dptr = (void *)&items_to_store[i];
data_datum.dsize = sizeof(struct test_data);
result = dbm_store(dbm_ptr, key_datum, data_datum, DBM_REPLACE);
if (result != 0) {
fprintf(stderr, “dbm_store failed on key %s\n”, key_to_use);
exit(2);
}
}
6 接下来我们要测试我们是否可以取得这些新数据,最后,我们必须关闭数据库:
sprintf(key_to_use, “bu%d”, 13);
key_datum.dptr = key_to_use;
key_datum.dsize = strlen(key_to_use);
data_datum = dbm_fetch(dbm_ptr, key_datum);
if (data_datum.dptr) {
printf(“Data retrieved\n”);
memcpy(&item_retrieved, data_datum.dptr, data_datum.dsize);
printf(“Retrieved item - %s %d %s\n”,
item_retrieved.misc_chars,
item_retrieved.any_integer,
item_retrieved.more_chars);
}
else {
printf(“No data found for key %s\n”, key_to_use);
}
dbm_close(dbm_ptr);
exit(EXIT_SUCCESS);
}
当我们编译并且运行这个程序时,我们可以得到下面的简单输出:
$ gcc -o dbm1 -I/usr/include/gdbm dbm1.c -lgdbm
$ ./dbm1
Data retrieved
Retrieved item - bar 13 unlucky?
如果我们的gdbm是以兼容模式安装的,我们就会得上面的输出结果。如果编译失败,我们就需要按照我们前面所描述的来安装GNU gdbm库兼容文件,并且/或是在我们编译时指定额外的目录:
$ gcc -I/usr/local/include -L/usr/local/lib -o dbm1 dbm1.c -ldbm
如果仍然编译失败,试着将-ldbm部分替换为-lgdbm:
$ gcc –I/usr/local/include –L/usr/local/lib -o dbm1 dbm1.c –lgdbm
工作原理
首先,我们打开数据库,如果需要,则要创建数据库。然后我们需要填充我们用作测试数据的items_to_store的三个成员。对于这三个成员的每一个,我们创建了一个索引键值。为了使其简单,我们使用两个字符串的第一个字符,加上存储的整数作为键值。
然后我们设置两个datum结构,一个用于键值,另一个用于要存储的数据。在数据库中存储这三个项目之后,我们组织一个新的键值,并且设置一个datum结构来指向他。然后我们使用这个键值由数据库中读取数据。我们通过检测返回的datum中的dptr不为null来确保成功。如果其不为空,我们就可以将所获取的数据(存储在dbm库中)拷贝到我们自己的结构中,在这里要小心使用dbm_fetch把返回的尺寸(如果我们没有这样做,并且正在使用变化尺寸的数据,我们就会试着拷贝本不存在的数据)。最后,我们打印出所取得的数据来显示我们正确的读取了数据。
分享到:
相关推荐
智慧养老、居家养老管理服务系统是基于老龄数据管理、业务受理、安全监护管理、商家管理、系统报表、基础数据管理六个模块的综合管理系统。以下是该系统的详细介绍: 1. 系统概述 智慧养老、居家养老管理服务系统...
用友主数据管理解决方案 用友主数据管理解决方案是企业级的主数据管理解决方案,旨在帮助企业解决主数据管理问题,提高业务效率和决策效率。该解决方案通过集成多个系统和数据库,提供统一的主数据视图,帮助企业...
本资源摘要信息将从华为制造业数据治理、数据管理与数据中台架构解决方案PPT.pptx中提取相关知识点,总结了数据治理、数据管理、数据中台架构等方面的重要概念和技术。 一、数据治理 * 数据治理是指对组织内所有...
### DAMA2数据管理知识体系解析 #### 一、数据管理的重要性及背景 在当今数字化时代,数据已经成为企业和组织的核心资产。随着技术的发展,我们能够测量各种事件和活动,从宇宙大爆炸的影响到人类的心跳,甚至能够...
二、数据资产管理活动职能 (一) 数据模型管理 (二) 数据标准管理 (三) 数据质量管理 (四) 主数据管理 (五) 数据安全管理 (六) 元数据管理 (七) 数据开发管理 (八) 数据资产流通 (九) 数据价值评估 (十) 数据资产运营...
### 数据管理知识体系(第二版)—— DAMA-DMBOK #### 一、引言与背景 在当今数字化时代,数据已经成为企业运营的核心资产之一。随着云计算、大数据、人工智能等新兴技术的发展,数据管理的重要性日益凸显。企业...
六、数据管理执行组 * 数据管理执行组的组成和职责 * 负责全行数据管理工作的具体执行 * 数据质量管理岗、数据架构管理岗、数据需求管理岗和数据管理岗的职责 七、企业数据架构管理 * 企业数据架构管理的定义和...
数据质量管理平台需求文档是数据质量管理平台的建设需求文档,涵盖了数据质量管理的概念、六要素、项目背景、项目目标、业务方案、系统架构、整体要求、功能设计等方面的内容。本文档将对这些内容进行详细的解释和...
数据质量的评估维度包括完整性、准确性、一致性、唯一性、及时性和有效性等六个方面。完整性是指数据在生产和加工过程中无缺失和遗漏;准确性是指数据反映真实世界的实体;一致性是指遵循统一的数据标准记录和传递...
### XX银行元数据管理细则制度知识点解析 #### 一、元数据管理的背景与目标 - **背景**:为了响应《XX银行数据管理制度(暂行)》的要求,XX银行制定了元数据管理细则,旨在规范全行各系统的元数据管理工作。 - **...
### 主数据管理办法相关知识点 #### 一、主数据管理办法概览 **主数据管理办法**旨在规范中国联通内部关于供应商基础数据的管理方式,确保供应链管理系统的完善性和数据的准确性。此办法着重强调了供应商主数据的...
#### 六、主数据管理与数据仓库的区别与联系 虽然主数据管理系统与数据仓库系统在某些方面具有相似性,如减少数据冗余、提升数据洞察力、依赖相似的技术手段等,但它们也有明显的区别: - **处理类型**:MDM系统偏...
本PPT着重介绍了数据质量的六个关键评价标准,这些标准有助于评估和提升数据的质量,从而优化业务流程和决策制定。 1. **完整性**:完整性是指数据集是否包含所有必要的信息,没有遗漏或缺失的数据。完整性检查通常...
根据提供的信息,我们可以了解到这是一篇关于“房产管理系统的数据流图”的文章。该系统旨在通过数据流图和数据字典来实现对房产的有效管理。接下来,我们将详细解析标题、描述以及部分给出的内容中所涉及的关键知识...
【资源说明】 1、该资源包括项目的全部源码,下载可以直接使用! 2、本项目适合作为计算机...游乐园管理系统专业版源码+项目说明(主要包括购卡管理、售票管理、超市管理、餐饮管理、财务管理、数据清空六大部分).zip
#### 六、元数据管理最佳实践 1. **建立元数据管理体系**:明确元数据管理的目标、范围和职责。 2. **制定元数据标准**:定义统一的数据分类、命名规则等标准。 3. **实施元数据质量管理**:定期检查元数据的完整性...
系统功能概要设计中,报告展示了系统的模块化结构,包括用户管理、图书管理、销售管理、查询、报表查询和数据管理六个模块。每个模块都详细描述了其功能,如用户管理涉及登录和用户信息管理,图书管理负责图书信息的...
### eBay数据仓库实践:元数据管理及应用 #### 一、eBay的诞生与成长 eBay成立于1995年,由皮埃尔·奥米迪亚(Pierre Omidyar)创立,最初源于一个简单的想法——拍卖一个坏掉的激光指示器。这一简单尝试迅速发展...