Berkeley DB的sql实现

SQLite 3.x 与 Berkeley DB:2款嵌入式数据库在 ARM 平台的 5 项关键指标对比 本文对比了SQLite 3.x和Berkeley DB两款嵌入式数据库在ARM平台上的性能表现,从内存占用、API设计、事务机制、并发性能和应用场景适配五个维度进行深度评测。通过实测数据和分析,为开发者提供了选型参考,特别是在资源受限的嵌入式系统中如何权衡性能与功能完整性。 阅读详情
 常常有人问 Oracle Berkeley DB “我如何在 Berkeley DB 中进行 ?"因此,我们此处将介绍如何在 Oracle Berkeley DB 中实施众多您喜爱的 SQL 功能。不是所有的 SQL 应用程序都应该在 Oracle Berkeley DB 实施( Oracle Berkeley DB 是一个开放源的嵌入式数据库引擎,提供了快速、可靠、本地的持久性,无需管理),但如果您有一系列相对固定的查询且很关心性能,Berkeley DB 将是很好的选择。

  让我们从头开始 (很好的起步点)。我们从 ABC 开始学阅读,在 Berkeley DB 中我们从术语开始。

  这里为坚定的 SQL 程序员提供了一个小型“翻译指南”:

SQL 术语

Oracle Berkeley DB 对应词汇

数据库

环境

表

数据库

字节组/行

键/数据对

主索引

键

次索引

次数据库

  选择一个应用程序域 — 传统员工数据库,经过一定的简化。我们进一步假定您需要所有 Berkeley DB 的全部额外特性:并发、事务、可恢复性等。

  创建数据库

  在 SQL 中,您可以执行以下命令

  CREATE DATABASE personnel

  在 Berkeley DB 中, 您想要创建一个放置所有应用程序数据的环境。在代码中,您将通过一个环境句柄来引用环境,该句柄类型为 DB_ENV。您将使用这一句柄来操作此环境。现在,将一些精妙的错误处理过程放在一边,来集中讨论 API。 

DB_ENV *dbenv;
  int ret;
  /* Create the handle. */
  DB_ASSERT(db_env_create(&dbenv, 0) == 0);
  /*
  * If you wanted to configure the environment, you would do that here.
  * Configuraition might include things like setting a cache size,
  * specifying error handling functions, specifying (different)
  * directories in which to place your log and/or data files, setting
  * parameters to describe how many locks you'd need, etc.
  */
  /* Now, open the handle. */
  DB_ASSERT(dbenv->open(dbenv, "my_databases/personnel",
  DB_CREATE | DB_INIT_LOCK | DB_INIT_MPOOL | DB_INIT_TXN | DB_THREAD, 0644);

您现在创建和打开了一个环境。需要注意几项事情:

·                            开始前必须有 my_databases/personnel 目录。

·                            open 调用的最后一个参数就是作为此环境的一部分为您创建的文件的模式。

·                            此处指定的标记将允许您创建环境
(DB_CREATE)
,使用锁定 (DB_INIT_LOCK);有一个共享的内存缓存池 (DB_INIT_MPOOL);使用事务 (DB_INIT_TXN);并同时在不同的控制线程中使用得到的环境句柄 (DB_THREAD)。

  在 SQL 中,查询通常由单独的服务器处理,该服务器由数据库管理员配置以在您的系统上正常(或不正常)工作。因为 Berkeley DB 嵌入到了您的应用程序中,因此该应用程序可以执行许多配置工作。但这确实与数据库调优有关,我们将另文详述。

  现在创建了数据库,接下来创建一些表。在 Berkeley DB 中, 表由类型为 DB * 的句柄引用 。对于应用程序中的每个表,通常会打开一个句柄,然后在一或多个线程中使用该句柄。

  因此,在 SQL 中可能是  

CREATE TABLE employee
  (primary key empid int(8), last_name varchar(20), first_name varchar(15),
  salary numeric(10, 2) salary, street varchar (20), city varchar (15),
  state char(2), zip int(5))

  在我们讨论实施这一过程的 Berkeley DB 代码前,要记住在 SQL 中,数据库负责实施和解释数据模式,这很重要。在 Berkeley DB 中,这一解释由应用程序完成。在分析数据操作语言 (DML) 时这将变得更加有趣,但现在其很明显,因为在创建员工表时,Berkeley DB 只知道主键,不知道数据库中的不同域。

  首先,您需要创建一个数据库句柄来代表创建的的表。(我们再次略过错误处理。) 

DB *dbp;
  DB_ENV *dbenv;
  /* Let's assume we've used the code from above to set dbenv. */
  ASSERT(db_create(&dbp, dbenv, 0) == 0);
  /*
  * Like with the environment, tables can also be configured. You
  * can specify things like comparison functions, page-size, etc.
  * That would all go here.
  */
  /* Now, we'll actually open/create the primary table. */
  ASSERT(dbp->open(dbp, NULL, "employee.db", NULL, DB_BTREE,
  DB_AUTO_COMMIT | DB_CREATE | DB_THREAD, 0644) == 0).

  这一调用将使用 B-树作为主索引结构创建表。该表将在 my_databases/personnel 目录中物化,使用名称 employee.db。文件将包含一个表并具有由最后的参数 (0644) 指定的文件系统权限。指定的标记在事务中创建表,允许以后的事务操作

  (DB_AUTO_COMMIT);允许表不存在时创建表 (DB_CREATE);并指定可由多个控制线程同时使用得到的句柄 (DB_THREAD)。

  注意,您尚未指定具体由什么组成主键 (索引) 或数据字段好似存储于该表中。这都将是应用程序的任务,稍后接触到插入、选择和更新部分的内容时这将变得更清楚。

现在,我们来看看如果在 employee id 上有一个主索引同时对姓使用一个次索引时将会出现什么情况。

  您应该使用上述 SQL 查询并执行。

  CREATE INDEX lname ON employee (last_name)

  在 Berkeley DB 中,次索引就像是表。然后您可以联合表,使一个表成为另一个表的次索引。为实施这一功能,需要更深入地了解应用程序要使用的数据表示。

  假设应用程序要使用 C 结构以在我们的员工表中包含字节组。您可以按下面所示定义结构: 

typedef struct _emp_data {
  char lname[20];
  char fname[15];
  float salary;
  char street[20];
  char city[15];
  char state[2];
  int zip;
  } emp_data;

  假设员工 ID 为简单的整数:

  typedef int emp_key;

  在 Berkeley DB 中,操作键或数据项时,您使用 DBT 结构。DBT 包含了不透明的字节串,以指针和长度来表示。指针由 DBT 的数据字段引用,长度存储于 DBT 的大小字段中。如果希望操纵表示一个员工的键/数据对,您需要将一个 DBT 用于 emp_key,另一个用于 emp_data。  

DBT key_dbt, data_dbt;
  emp_key ekey;
  emp_data edata;
  memset(&key_dbt, 0, sizeof(key_dbt));
  memset(&data_dbt, 0, sizeof(data_dbt));
  /*
  * Now make the key and data DBT's reference the key and data
  * variables.
  */
  key_dbt.data = &ekey;
  key_dbt.size = sizeof(ekey);
  data_dbt.data = &edata;
  data_dbt.size = sizeof(edata);

  这里我们可以了解到 SQL 中的字节组由键/数据对表示,应用程序负责了解如何解释这些对。

有了这些作为背景后,我们返回到次索引的讨论。因为 Berkeley DB 不了解键/数据对中数据元素的模式和结构,所以需要应用程序的帮助来找到我们用作次索引的字段。应用程序以回调函数方式提供帮助。回调函数接受键/数据对输入并返回引用了用作次键的值的 DBT。

所以要创建 last_name 的次索引,必须编写一个回调函数,接受键/数据对输入并返回引用了数据项的 last_name 字段的 DBT。  

int
  lname_callback(DB *dbp, const DBT *key, const DBT *data, DBT *skey)
  {
  emp_data *edata;
  /*
  * We know that the opaque byte-string represented by the data DBT
  * represents one of our emp_data structures, so let's cast it
  * to one of those so that we can manipulate it.
  */
  edata = data->data;
  skey->data = edata->lname;
  skey->size = strlen((edata->lname);
  return (0);
  }

  写完回调函数后,可以指定次索引。次索引只是一个表,所以先创建一个表:  

DB *sdbp;
  ASSERT(db_create(&sdbp, dbenv, 0) == 0);
  /* Configure sdbp. */
  ASSERT(sdbp->open(sdbp, NULL, "emp_lname.db", NULL, DB_BTREE,
  DB_AUTO_COMMIT | DB_CREATE | DB_THREAD, 0644) == 0);

  再次使用 B 树结构对姓进行索引,保留以前使用的所有标记和模式。

  最后,您必须将次索引表与主表(员工表)相关联。请记住,dbp 是员工表的句柄, sdbp 是次索引表的句柄。

  ASSERT(dbp->associate(dbp, NULL, sdbp, lname_callback, flags) == 0);

  注意事项:

·                            可以了创建任意多的次索引。重要的问题是次索引降低了插入的速度 (因为您必须为每个次索引创建索引项),但如果使用次键值(如,姓)来查询和选择字节组, 它们将大大改进查询的性能。

·                            在任何时候更新主表时,只要打开并关联了次索引,次索引将始终保持最新。但如果忘记打开并关联次索引,然后更改基表,您会发现次索引不是最新的。

  应当避免这一情况。

  DDL 中的最后两个操作是丢弃命令:丢弃索引、表和数据库。

  如同在 SQL 中丢弃索引和删除表一样,您也能在 Berkeley DB 中完成这些操作。在 SQL 中,您可以执行以下命令

  DROP TABLE employee

  或者

  DROP INDEX lname

在 SQL 中丢弃一个表将丢弃与之关联的所有索引,在 Berkeley DB 中,您必须显式完成此任务。幸运的是,在 Berkeley DB 中丢弃表或索引是同样的操作。

  移除表前,必须关闭表上的所有数据库句柄。关闭表容易;假设我们要丢弃员工数据库的次索引。先关闭次索引:

  sdbp->close(sdbp, 0)

  在发出数据库句柄的关闭命令后,不能再使用句柄。

  关闭次索引表后,您可以使用 dbenv 句柄的 dbremove 方法将其移除:  

DB_ENV *dbenv;
  ASSERT(dbenv->dbremove(dbenv,
  NULL, "emp_lname.db", NULL, DB_AUTO_COMMIT) == 0);

  可以使用同一调用顺序 (closing 和 dbremoving) 来丢弃表。

  假设不想丢弃表,只想更改其名称。您也可完成这一操作。

  与移除一样,首先要关闭表句柄:

  dbp->close(dbp, 0);

  现在您可以更改表名称:  

DB_ENV *dbenv;
  ASSERT(dbenv->dbrename(dbenv,
  NULL, "employee.db", NULL, "newemp.db", DB_AUTO_COMMIT) == 0);

  最后,您可能想销毁数据库。在 SQL 中执行

  DROP DATABASE personnel

  这一命令在 Berkeley DB 中也有对应的命令。

  首先,必须关闭环境:

  ASSERT(dbenv->close(dbenv, 0) == 0);

  与关闭表句柄一样,当关闭环境句柄后,将不能使用该句柄。所以,为了丢弃表,您需要创建一个新句柄,然后使用该句柄移除数据库(环境)。 

ASSERT(db_env_create(&dbenv, 0) == 0);
  ASSERT(dbenv->remove(dbenv, "my_databases/personnel", 0) == 0);

  至此,我们完成了 SQL 的 DDL 到 Berkeley DB 的转换。接下来,我们将讨论如何完成 SQL DML 到 Berkeley DB 的转换。

  在 Berkeley DB 中执行 SQL DML 操作

  我们已经介绍了 SQL 的 DDL 和其在 Berkeley DB 中的实现,现在要将数据添加到数据库,讨论 SQL 的插入、更新和删除。

  在 SQL 中使用插入语句将数据插入表:  

INSERT INTO employees VALUES (00010002, "mouse", "mickey", 1000000.00,
  "Main Street", "Disney Land", "CA", 98765);

  SQL 插入都变成了数据库或游标句柄的 Berkeley DB“put”方法;我们先讨论数据库,然后再探讨游标。

  假设您已经打开了一个表,有一个数据库句柄 dbp 引用了员工表。现在,雇佣 Mickey Mouse。

DB *dbp;
  DBT key_dbt, data_dbt;
  emp_data edata;
  emp_key ekey;
  /* Put the value into the employee key. */
  ekey = 00010002;
  /* Initialize an emp_data structure. */
  strcpy(edata.lname, "Mouse");
  strcpy(edata.fname, "Mickey");
  edata.salary = 1000000.00;
  strcpy(edata.street, "Main Street");
  strcpy(edata.city, "Disney Land");
  strcpy(edata.state, "CA");
  edata.zip = 98765;
  /* Initialize DBTs */
  memset(&key_dbt, 0, sizeof(key_dbt));
  memset(&data_dbt, 0, sizeof(data_dbt));
  /* Now, assign key and data values to DBTs. */
  key->data = &ekey;
  key->size = sizeof(ekey);
  data->data = &edata;
  data->size = sizeof(edata);
  /* Finally, put the data into the database. */
  ASSERT(dbp->put(dbp, NULL, &key_dbt, &data_dbt, DB_AUTO_COMMIT) == 0);

  请注意,如果已经有将任何次索引与员工表相关联(如在 SQL 中),则在插入时将自动对其进行更新。

  现在,假设表中有些数据,您希望对这些数据进行更改。例如,要给 Mickey 涨工资!有多种完成方法。

 

第一个方法与上面的插入代码一样 — 如果您在一个表上使用 PUT 方法并且该键已存在 (且表不允许单键有相同的数值), PUT 将使用新版本代替旧版本。因此,以下步骤将使用新记录替换 Mickey 的记录,薪水将为 $2,000,000,而不是 $1,000,000。  

/* Put the value into the employee key. */
  ekey = 00010002;
  /* Initialize an emp_data structure. */
  strcpy(edata.lname, "Mouse");
  strcpy(edata.fname, "Mickey");
  edata.salary = 2000000.00;
  strcpy(edata.street, "Main Street");
  strcpy(edata.city, "Disney Land");
  strcpy(edata.state, "CA");
  edata.zip = 98765;
  /* Initialize DBTs */
  memset(&key_dbt, 0, sizeof(key_dbt));
  memset(&data_dbt, 0, sizeof(data_dbt));
  /* Now, assign key and data values to DBTs. */
  key->data = &ekey;
  key->size = sizeof(ekey);
  data->data = &edata;
  data->size = sizeof(edata);
  /* Finally, put the data into the database. */
  ASSERT(dbp->put(dbp, NULL, &key_dbt, &data_dbt, DB_AUTO_COMMIT) == 0);

请注意,该方法较麻烦,为完成它,您需要了解数据库中所有其他域的值。因此,不同于

  UPDATE employees SET salary = 2000000 WHERE empid = 000100002

  其中,您只需知晓 employee ID,而现在需要知晓所有信息。难道在 Berkeley DB 没有可用方法吗?答是是有。如果确切地知道要替换的数据项字节,您可以使用与等同于更新命令的方法。

  要使用此方法,您需要引入游标概念。游标表示表中的某个位置。它让您遍历表并保留当前项的情况,然后再对其操作。

  在 Berkeley DB 中创建游标很简单 — 它是数据库句柄的方法:  

DBC *dbc;
  DB *dbp;
  ASSERT(dbp->cursor(dbp, NULL, 0) == 0);

  有游标后,我们需要将其定位于 Mickey 的记录,以便能对其进行更新。这等同于 SQL 语句的 WHERE 部分。  

DBT key_dbt, data_dbt;
  emp_data *edata;
  emp_key ekey;
  /* We'd like to look up Mickey's key. */
  emp_key = 0010002;
  memset(&key_dbt, 0, sizeof(key_dbt));
  key_dbt.data = &emp_key;
  key_dbt.size = sizeof(emp_key);
  /*
  * We want the data returned, so we don't need to initialize the
  * employee data data structure.
  */
  memset(&data_dbt, 0, sizeof(data_dbt));
  /* Now, set the cursor to the record with the key emp_key. */
  dbc->c_get(dbc, &key_dbt, &data_dbt, DB_SET);

  接下来我们就可以更改薪水了 (处理子句的“SET salary=2000000”部分)  

/* Change the salary. */
  edata = data_dbt->data;
  edata.salary = 2000000;

  最后,应用 SQL 语句的 UPDATE 部分:

  dbc->c_put(dbc, &key_dbt, &data_dbt, DB_CURRENT);

在本例中,您事先不知道 Mickey 的记录内容,因此需要检索然后再更新。

  或者,甚至无需检索记录。DBT 上的 DB_DBT_PARTIAL 标记值指示您在获取/插入记录的一部分,所以 Berkeley DB 可以忽略除该部分外的所有内容。

  再试一次:  

emp_data edata;
  float salary;
  /* We'd like to look up Mickey's key. */
  emp_key = 0010002;
  memset(&key_dbt, 0, sizeof(key_dbt));
  key_dbt.data = &emp_key;
  key_dbt.size = sizeof(emp_key);

  不检索整个记录,不检索任何东西 — 即执行 PARTIAL 获取,指定您只需要 0 字节的数据项。  

/* We don't want the data, we just want to position the cursor. */
  memset(&data_dbt, 0, sizeof(data_dbt));
  data_dbt->flags = DB_DBT_PARTIAL;
  data_dbt->dlen = 0;
  /* Position the cursor on Mickey's record */
  dbc->c_get(dbc, &key_dbt, &data_dbt, DB_SET);
  /*
  * Now, prepare for a partial put. Note that the DBT has already
  * been initialized for partial operations. We need to specify
  * where in the data item we wish to place the new bytes and
  * how many bytes we'd like to replace.
  */
  salary = 2000000.00;
  /* The DBT contains just the salary information. */
  data_dbt->data = &salary;
  data_dbt->size = sizeof(salary);
  /*
  * dlen and doff tell Berkeley DB where to place this information
  * in the record. dlen indicates how many bytes we are replacing --
  * in this case we're replacing the length of the salary field in
  * the structure (sizeof(emp_data.salary)). doff indicates where
  * in the data record we will place these new bytes -- we need to
  * compute the offset of the salary field.
  */
  data_dbt->dlen = sizeof(emp_data.salary);
  data_dbt->doff = ((char *)&edata.salary - (char *)&edata);
  /* Now, put the record back with the new data. */
  dbc->c_put(dbc, &key_dbt, &data_dbt, DB_CURRENT);

数据检索

  了解如何向表插入数据后,现在学习如何检索它。让我们从最简单的方法开始:根据其主键查找值。

  SELECT * FROM employees WHERE id=0010002

  您已经知道如何使用游标来完成此任务:  

DBT key_dbt, data_dbt;
  emp_data *edata;
  emp_key ekey;
  /* We'd like to look up Mickey's key. */
  emp_key = 0010002;
  memset(&key_dbt, 0, sizeof(key_dbt));
  key_dbt.data = &emp_key;
  key_dbt.size = sizeof(emp_key);
  /*
  * We want the data returned, so we don't need to initialize the
  * employee data data structure.
  */
  memset(&data_dbt, 0, sizeof(data_dbt));
  /* Now, set the cursor to the record with the key emp_key. */
  dbc->c_get(dbc, &key_dbt, &data_dbt, DB_SET);

使用上面的游标操作,因为我们稍后要更新该记录。假如只想检索记录,则甚至不需要游标。所需要的是 dbp 句柄的 get 方法:  

DBT key_dbt, data_dbt;
  emp_data *edata;
  emp_key ekey;
  /* We'd like to look up Mickey's key. */
  emp_key = 0010002;
  memset(&key_dbt, 0, sizeof(key_dbt));
  key_dbt.data = &emp_key;
  key_dbt.size = sizeof(emp_key);
  /*
  * We want the data returned, so we don't need to initialize the
  * employee data data structure.
  */
  memset(&data_dbt, 0, sizeof(data_dbt));
  /* Now, use the dbp method. */
  dbp->get(dbp, NULL, &key_dbt, &data_dbt, 0);

  因此,这与上面的 SELECT 表达式一样。

  到目前为止,我们都是根据主键来查找记录。如果不知道主键,该怎么办?下面提供了几种方法:

使用次键值查找记录。

  遍历共享同一键的项目。

  遍历整个数据库。

  下面详述以上方法。

  使用次键

  与在 SQL 中一样,根据次键检索和使用主键非常类似。

  事实上,SQL 查询看起来相同,除了 where 子句:

  SELECT * FROM employees WHERE last_name = "Mouse"

  Berkeley DB 调用与其对等的主键调用类似。

  它使用的不是主键示例中的 dbp,而是使用 sdbp 根据次键来进行查找:  

DBT key_dbt, data_dbt;
  emp_data *edata;
  /* We'd like to look up by Mickey's last name. */
  memset(&key_dbt, 0, sizeof(key_dbt));
  key_dbt.data = "Mouse";
  key_dbt.size = strlen((char *)key_dbt.data);
  /*
  * We want the data returned, so we don't need to initialize the
  * employee data data structure.
  */
  memset(&data_dbt, 0, sizeof(data_dbt));
  /* Now, call the get method. */
  sdbp->get(sdbp, NULL, &key_dbt, &data_dbt, 0);

  data_dbt 中返回的内容非常有趣。返回的是主数据库中的数据 — 即在数据 DBT 中返回了同样的东西,不管您是使用主键还是次键查找。

  但是,您会发现按次键查找时,得到的结果与按主键检索或 SQL 语句得到的结果有所不同。主键丢失,因为没有位置来返回它。所以,上面的代码实际上实施  

SELECT last_name, first_name, salary, street, city, state, zip FROM
  employees WHERE last_name="Mouse"

  如果您需要主键,该如何做?答案是使用 dbp->pget 或 dbc->pget 方法。这两个方法与 get 方法一样,只是它们专门设计用于您需要返回主键时的次索引查询。因此,在这一情形中,结果中会包括主键、次键和数据元素:

DBT key_dbt, pkey_dbt, data_dbt;
  emp_data *edata;
  /* We'd like to look up by Mickey's last name. */
  memset(&key_dbt, 0, sizeof(key_dbt));
  key_dbt.data = "Mouse";
  key_dbt.size = strlen((char *)key_dbt.data);
  /* Set up the dbt into which to return the primary. */
  memset(&pkey_dbt, 0, sizeof(pkey_dbt));
  /*
  * We want the data returned, so we don't need to initialize the
  * employee data data structure.
  */
  memset(&data_dbt, 0, sizeof(data_dbt));
  /* Now, get the record and the primary key. */
  sdbp->pget(sdbp, NULL, &key_dbt, &pkey_dbt, &data_dbt, 0);

  该代码等同于 SQL 次选择:

  SELECT * FROM employees WHERE last_name="Mouse"

  遍历多个记录

  到现在为止,我们仅返回了一个记录。SQL 允许您返回多个记录 (换言之,姓为 Mouse 的所有员工)。如何在 Berkeley DB 中实现此目的?

  让我们考虑两种情形。第一个情形,按照键来查找项目。第二个情形,搜索数据库,按照不带键的字段查找项目。

  假设您希望查找所有姓 Mouse 的员工(假设有多个)。这意味着已经创建了 last_name 次索引,从而允许重复。打开数据库前,需要对它进行配置以支持重复:  

sdbp->set_flags(sdbp, DB_DUP);
  ASSERT(sdbp->open(sdbp, NULL, "emp_lname.db", NULL, DB_BTREE,
  DB_AUTO_COMMIT | DB_CREATE | DB_THREAD, 0644) == 0);

  现在,按次索引检索时,您可能想使用游标来完成。开始时使用前面使用的代码,可以添加一个循环以遍历共享同一次键的项目: 

DBT key_dbt, data_dbt;
  DBC *sdc;
  emp_data *edata;
  /* We'd like to look up by Mickey's last name. */
  memset(&key_dbt, 0, sizeof(key_dbt));
  key_dbt.data = "Mouse";
  key_dbt.size = strlen((char *)key_dbt.data);
  /*
  * We want the data and primary key returned, so we need only
  * initialize the DBTs for them to be returned.
  */
  memset(&data_dbt, 0, sizeof(data_dbt));
  memset(&pkey_dbt, 0, sizeof(pkey_dbt));
  /* Now, create a cursor. */
  sdbp->cursor(sdbp, NULL, &sdbc, 0);
  /* Now loop over all items with the specified key. */
  for (ret = sdbc->pget(sdbc, &key_dbt, &pkey_dbt, &data_dbt, DB_SET);
  ret == 0:
  ret = sdbc->pget(sdbc, &key_dbt, &pkey_dbt, &data_dbt, DB_NEXT_DUP) {
  /* Do per-record processing in here. */
  }

通过使用游标查找带指定键的第一个项目,完成游标的初始化,然后遍历数据库中带同一键的所有项目。

  另一个可能的根据键进行遍历的形式为查询形式,如

  SELECT * FROM employees WHERE id >= 1000000 AND id < 2000000

  再次使用游标来遍历,但这一次您想创建一个起始和结束点。Berkeley DB 使得起始点的建立非常简单,结束点由应用程序来完成。  

DBT key_dbt, data_dbt;
  DBC *dc;
  emp_key ekey;
  /* Set the starting point. */
  memset(&key_dbt, 0, sizeof(key_dbt));
  ekey = 1000000;
  key_dbt.data = &ekey;
  key_dbt.size = sizeof(ekey);
  key_dbt.flags = DB_DBT_USERMEM;
  key_dbt.ulen = sizeof(ekey);
  memset(&data_dbt, 0, sizeof(data_dbt));
  /* Now, create a cursor. */
  dbp->cursor(dbp, NULL, &dbc, 0);
  /* Now loop over items starting with the low key. */
  for (ret = dbc->get(dbc, &key_dbt, &data_dbt, DB_SET_RANGE);
  ret == 0:
  ret = dbc->get(dbc, &key_dbt, &data_dbt, DB_NEXT)) {
  /* Check if we are still in the range. */
  if (ekey >= 2000000)
  break;
  /* Do per-record processing in here. */
  }

  要注意两点:1) 以 DB_SET_RANGE 标记作为循环的开始,这会将游标定位于第一个大于或等于指定键的项目; 2) 应用程序必须检查循环内范围的终点。

还要注意您在 key_dbt 中设置了 DB_DBT_USERMEM 标记,指示检索的键应置于用户指定的内存中。这可以让您使用 ekey 变量来检查该键。

  在选择部分内容的最后,我们来讨论一个查询,该查询将返回一个或多个其评判标准不是带键的字段的项。假设

  SELECT * FROM employees WHERE state=ca

  因为 state 字段上没有键,您只能遍历整个数据库。即意味着执行一个简单的游标遍历循环。  

DBC *dbc;
  DBT key_dbt, data_dbt;
  emp_data *edata;
  dbp->cursor(dbp, &key_dbt, &data_dbt, &dbc, 0);
  memset(&key_dbt, 0, sizeof(key_dbt));
  memset(&data_dbt, 0, sizeof(data_dbt));
  for (ret = dbc->get(dbc, &key_dbt, &data_dbt, DB_FIRST);
  ret == 0;
  ret = dbc->get(dbc, &key_dbt, &data_dbt, DB_NEXT)) {
  /* See if the state field is "ca". */
  edata = data_dbt->data;
  if (strcmp(edata->state, "ca") == 0)
  /* Keep this record. */
  }

这似乎没有效率,如果没有字段的索引,您别无它法,而且,实际上这就是当您指定一个匹配不带键的字段的查询时 SQL 数据库内部的工作过程。

  移除数据

  您现在已经了解了如何插入和更改数据以及如何对其进行检索。最后要讨论的是如何移除数据。基本上,有两种不同的方法可用于从数据中删除字节组:如果您知道要移除的项的键(且该项不是该键的众多重复项之一),则可以使用键式删除。如果不知道键,您可以遍历和使用游标删除。从简单情形开始,解雇 Mickey Mouse。

  DELETE FROM employees WHERE id= 0010002

  这看起来像检索,但我们将使用 del 方法。  

DBT key_dbt;
  emp_key ekey;
  ekey = 0010002;
  memset(&key_dbt, 0, sizeof(key_dbt));
  key_dbt.data = &ekey;
  key_dbt.size = sizeof(ekey);
  dbp->del(dbp, NULL, &key_dbt, 0);

  您可以选择解雇所有姓 Mouse 的员工。可以用同一方法,因为您有姓的次索引:  

DELETE FROM employees WHERE last_name = "Mouse"
  DBT key_dbt;
  memset(&key_dbt, 0, sizeof(key_dbt));
  key_dbt.data = "Mouse";
  key_dbt.size = strlen(key_dbt.data);
  dbp->del(dbp, NULL, &key_dbt, 0);

  但这有点过于无情。假如您不想解雇 Mickey,而只想解雇 Minnie Mouse。有可以方便地解雇 Minnie 的方法吗?换言之,如何完成这一任务:

  DELETE FROM employees where last_name = "Mouse" AND first_name = "Minnie"

  毫不为奇,您要使用游标来遍历数据项目,然后选择要删除的项目。  

DBT key_dbt, data_dbt;
  DBC *sdbc;
  sdbp->cursor(sdbp, NULL, &sdbc, 0);
  memset(&key_dbt, 0, sizeof(key_dbt));
  key_dbt.data = "Mouse";
  key_dbt.size = strlen(key_dbt.data);
  for (ret = sdbc->get(sdbc, &key_dbt, &data_dbt, DB_SET);
  ret == 0;
  ret = sdbc->get(sdbc, &key_dbt, &data_dbt, DB_NEXT_DUP)) {
  edata = data_dbt->data;
  if (strcmp(edata->first_name, "Minnie") == 0) {
  /* OK, this is a record we want to delete. */
  sdbc->del(sdbc, 0);
  }
  }
  dbp->del(dbp, NULL, &key_dbt, 0);

  现在您应该已经大致了解了如何创建 Oracle Berkeley DB 函数来执行基本的 SQL 命令。Berkeley DB 也有众多的选项和配置,可以为您提供更为精妙的功能。此处只探讨一个话题:如何将数据库操作纳入事务中。

管理事务

  先了解事务在(多数) SQL 实施中是如何工作的。当在 SQL 中执行 DML 语句时,它将成为当前事务的一部分。随后的每个语句都将作为该事务的一部分运行。当 SQL 会话结束或应用程序执行 COMMIT 语句时,当前事务将进行提交操作。任何事后时候都可同工作执行 ROLLBACK 语句中断事务。

  许多 SQL 实施还包括 AUTOCOMMIT 特性,该特性将每个 DML 语句都视为其自己的事务。当启用了 AUTOCOMMIT 模式后,以下次序

  statement 1
  
COMMIT
  
statement 2
  
COMMIT
  
statement 3
  COMMIT

  等同于

  statement 1
  
statement 2
  statement 3

  Berkeley DB 也允许您将数据库操作封装入事务中。与 SQL 不同,您可以不用事务运行 Berkeley DB。事实上,除非显示请求事务,否则运行时不会用到它们。因此,如何告知 Berkeley DB 您要使用事务呢?

  请记住,在打开环境时您可以指定一些标记:  

DB_ASSERT(dbenv->open(dbenv, "my_databases/personnel",
  DB_CREATE | DB_INIT_LOCK | DB_INIT_MPOOL | DB_INIT_TXN | DB_THREAD,
  0644);

  那些标记将针对您的应用程序配置 Berkeley DB。在本例中,您通过指定 DB_INIT_TXN 标记启用了事务。如果省略标记,则应用程序将无需通过事务运行。

  Berkeley DB 提供了与 SQL 的 AUTOCOMMIT 类似的特性。 您可以配置整个数据库 (环境) 始终使用环境句柄的 set_flags 方法自动提交:

  dbenv->set_flags(dbenv, DB_AUTOCOMMIT, 1);

  或者,也可以在一个打开的数据库上指定 DB_AUTOCOMMIT,致使您不会向其显式传递事务的所有后续操作将在事务中运行。

  假如您不想自动提交,希望应用程序可以将操作组合成为一个逻辑事务。例如,您想添加 Mickey Mouse 并指定他为经理。  

INSERT INTO employees VALUES (00010002, "mouse", "mickey", 1000000.00,
  "Main Street", "Disney Land", "CA", 98765);
  INSERT INTO manages(00000001, 000100002)
  COMMIT

  (上面表明 id=00000001 的员工将管理 Mickey。)

假设您知道如何执行数据操作,因此我们集中讨论如何指定事务。

  首先,必须显式开始一个事务 (与在 SQL 中不同)。创建事务是一个环境操作,因此它是环境句柄的一个方法。该方法将创建一个事务句柄 (DB_TXN)  

DB_TXN *txn;
  dbenv->txn_begin(dbenv, NULL, &txn, 0);

  具备事务句柄后,将其传入任何您希望加入事务中的数据库操作:  

emp_dbp->put(emp_dbp, txn, &key, &data, 0);
  man_dbp->put(man_dbp, txn, &key, &data, 0);

  然后,您可以调用事务句柄的适当方法来提交或中断事务。

  提交事务:

  txn->commit(txn, 0);

  中断事务:

  txn->abort(txn);

  两个方法都为破坏性方法,使得事务句柄不可用。

  与 SQL 不同, Berkeley DB 也以通过事务来保护 DDL 操作。因此,您可以将 DB_TXN 句柄传送到操作,如 dbenv->dbremove、dbenv->dbrename 和 dbp->open(¡­ DB_CREATE ¡­)。在这些情形中,DDL 操作将在指定事务的环境中执行,这意味着与其它事务一样可以对其进行提交或中止。

  结论

  Oracle Berkeley DB 提供的功能与 SQL 数据库的相同,但以完全不同的程序包提供。您可编写程序调用 API,并且整个数据库被直接“嵌入”到应用程序中;即它们运行于同一地址空间。这通常会将性能提升一个数量级。为取得这一成绩将增加应用程序的负担。当应用程序要求非常高的性能或应用程序操纵的数据本身不是关系型时,这通常是最有用的。

Berkeley DB概况 说实话,在我进入Oracle之前一个月,也是拿到Oracle的Offer 5个月之后,我才得到我未来经理的电话通知(我不是怪他通知的晚,事实上我非常感谢他给我机会进入这个产品组---这纯属机缘巧合,让我来到一个崭新的世界),说我在Berkeley DB产品组,这之前我从来没听说过它,我的第一印象,Berkeley的DB不是postgresl吗?仔细一查,才知道它是Oracle的嵌入式数据库,而嵌入 阅读详情

相关推荐

Berkeley DB的c语言接口

像MySQL这类基于C/S结构的关系型数据库系统虽然代表着目前数据库应用的主流,但却并不能满足所有应用场合的需要。有时我们需要的可能只是一个简单的基于磁盘文件的数据库系统。这样不仅可以避免安装庞大的数据库服务器,而且还可以简化数据库应用程序的设计。Berkeley DB正是基于这

aican_yu的专栏 822

bdb数据库

SQL 开发人员 Oracle Berkeley DB 指南 作者:Margo Seltzer Oracle Berkeley DB 中常用 SQL 函数使用指南。 2007 年 9 月发表 常常有人问 Oracle Berkeley DB “我如何在 Berkeley DB 中进行 ?"因此,我们此处将介绍如何在 Oracle Berkeley DB 中实施众多您喜爱的 ...

malixxx 796

Berkeley db 数据库

简介 它是由sleepcat开发,后被oracle收购,并提升了它在数据库行业中的知名度。 Berkeley DB (DB)是一个高性能的,嵌入数据库编程库,和C语言,C++,Java,Perl,Python,PHP,Tcl以及其他很多语言都有绑定。Berkeley DB可以保存任意类型的键/值对,而且可以为一个键保存多个数据。Berkeley DB可以支持数千的并发线程同时操作数据库,支

zhaojinjia的专栏 7926

berkeley db java sql,Berkeley DB 数据库的使用示例,je

import java.io.File;import java.io.UnsupportedEncodingException;import java.util.ArrayList;import com.sleepycat.je.Cursor;import com.sleepycat.je.CursorConfig;import com.sleepycat.je.Database;import c...

weixin_42510835的博客 310

Python Cookbook-7.8 使用 Berkeley DB 数据库

你想将一些数据做持久化处理,而且也想体验一下BerkeleyDB数据库的简洁和高效。

weixin_52297428的博客 1606

深入浅出数据库操作:Berkeley DB与PostgreSQL实践指南

本文深入探讨了Berkeley DB和PostgreSQL数据库的操作实践,包括数据的增删查改、游标的使用以及数据库的创建和管理。通过代码示例和操作指南,读者能够掌握如何在Linux环境下使用这些工具高效地进行数据处理。

weixin_31139479的博客 377

Oracle Berkeley DB XML 系统调研

Oracle Berkeley DB XML 系统调研 Oracle Berkeley DB XML 是一个可嵌入的开源 XML 数据库(EmbeddedNative Xml Database),可基于 XQuery 访问存储在容器中的文档,并对其内容进行索引。Oracle Berkeley DB XML 构建于 Oracle Berkeley DB 之上,并继承了其丰富的特性和属性(包

开心 2435

Socket与MySQL的联合编程实践

在进行网络编程前,理解网络通信的基础概念至关重要。计算机网络是将不同地理位置的计算机通过通信介质连接起来,实现数据交换的一种方式。网络通信协议是网络中计算机之间进行通信时所遵守的规则和约定。在众多网络通信协议中,TCP/IP协议族被广泛使用,其中传输控制协议(TCP)和用户数据报协议(UDP)是最常见的传输层协议。Socket,又称作套接字,是一种计算机网络通信的端点。套接字可以看作是计算机网络中的一个抽象概念,它提供了一种让程序能够通过网络进行数据交换的接口。

weixin_33205138的博客 768

berkeley db java sql,C#编程开发 Berkeley DB SQL使用指南

Berkeley DB来自于Oracle,是一个高性能、嵌入式的数据库。Berkeley DB支持多种语言操作如c、c++、c#、java、perl、python、tcl等。支持同时数千线程访问操作数据库,最大256TB存储数据,广泛应用于linux、window以及其他平台。传统的Berkeley DB只支持键/值对的存储方式,而不支持关系数据库。这在一定程度上限制了Berkeley DB的应用...

weixin_39746382的博客 101

GNSS数据处理(一)——开源软件RTKLIB与G-Nut/Anubis实战入门

本文详细介绍了GNSS数据处理的开源软件RTKLIB与G-Nut/Anubis的实战入门指南。从软件安装、数据获取到静态短基线解算,逐步讲解操作技巧与常见问题排查,帮助测绘与地质监测人员快速掌握GNSS数据处理技术,提升工作效率。

weixin_30733003的博客 133

PCIe设备内存访问实战:从ioremap到DMA映射的完整流程解析

本文深入解析了PCIe设备内存访问的完整流程,涵盖从CPU通过ioremap访问设备寄存器,到设备通过DMA映射高效读写主机内存的核心机制。详细探讨了虚拟地址、物理地址与总线地址的转换关系,对比了一致性与流式DMA映射的适用场景及同步要点,并阐述了IOMMU在安全与效率中的作用,为驱动开发者提供了实战代码与关键陷阱分析。

weixin_29201933的博客 505

Berkeley DB使用方法

Berkeley DB是Sleepcat开发的开源的嵌入式数据库。具有轻巧、可扩展的优点。但是它不支持SQL语句,而且操作起来相对比较复杂。下面讲讲在VC下为了使其能够存储数据结构做的封装。在google搜索Berkely DB 进入sleepycat下载zip安装包,编译。建立工程TestBerkeleyDB,拷贝libdb43.dll和libdb43.lib到当前工程下,添加libdb43.d

iintsy的专栏 3090

OpenWRT端口转发与NAT环回冲突的排查与解决

本文详细解析了OpenWRT路由器中端口转发与NAT环回功能冲突导致的内网访问故障。通过生活化类比阐明两者工作原理,提供从基础诊断到内核参数调整、LuCI选项配置及手动编写iptables规则的多套解决方案,并给出进阶排查思路,帮助用户彻底解决内网无法通过公网地址访问服务的常见问题。

weixin_29243279的博客 506

Oracle Berkeley DB 在java中的复合条件查询

ava后台代码中有实体类给予@Entity注释,需要作为查询条件的字段也有对应的@PrimaryKey,@SecondaryKey注释现在我需要给项目中用OracleBerkeleyDB记录的数据给予查询功能如在数据库中SQL语句如下:select*fromtabletwheret.id=123andt.date>='2013-07-07'andt....

aim73056的博客 180

Matlab 曲面平滑与点云处理

曲面平滑是一种常见的数学和计算机图形学技术,可以用来消除噪声、填补数据间隙,以及改善曲面的外观。在点云处理中,曲面平滑也被广泛应用于重建、拟合和可视化等领域。本文将介绍如何使用 Matlab 进行曲面平滑和点云处理,并提供相应的源代码。通过以上步骤,我们可以使用 Matlab 进行曲面平滑和点云处理。你可以根据具体需求调整参数和算法,以获得理想的曲面平滑效果。接下来,我们可以使用平滑算法对三角化的网格进行曲面平滑。以上代码将绘制原始点云和重建后的点云,并在图形中显示坐标轴和图例。表示平滑的迭代次数。

QfcaLinux的博客 752

RTCA DO-160G标准详解:航空电子设备的全面环境适应性测试指南

第二章的主要目的是提供标准中使用的术语和定义。了解这些术语对于准确理解标准的要求至关重要,特别是在技术实施过程中。RTCA DO-160G 涉及多个学科的知识,包括航空、电子、电磁学和机械工程,因此需要确保使用的术语在所有学科中具有一致的定义。为了确保测试人员和工程师在执行测试时对所有条件和要求的理解一致,RTCA DO-160G 标准中定义了大量术语。环境应力:指的是设备在操作过程中可能遇到的外部物理或化学条件,包括温度、湿度、振动等。电磁干扰(EMI)

qq_41854911的博客 5276

python数据存储用什么,使用Python进行基本数据存储

I need to store basic data of customer's and cars that they bought and payment schedule of these cars. These data come from GUI, written in Python. I don't have enough experience to use a database sys...

weixin_35934603的博客 91

利用SIMULINK搭建一个16QAM调制解调收发系统

1发送模块设计 根据前面介绍的理论知识,我们可以知道16QAM的基本结构,首先我来设计QAM发送段的SIMULINK仿真模块。 图1 16QAM的发送端模块 他的基本工作流程为:将信号通过两路升余弦滤波器,得到滤波以后的信号,滤波器的作用就是为了防止码间干扰。然后将滤波以后的信号和SIN,COS相乘得到调制信号,将调制 下面一一介绍每个模块的具体参数设置以及各个部分信号的波形图。 图2 产生随机数据的信号源 第一部分信号是产生一组随机的数字,作为信源,第二个模块将信号...

FPGA/MATLAB学习教程/源码/项目合作开发 4776

Berkeley DB基础教程

一、Berkeley DB的介绍 (1)Berkeley DB是一个嵌入式数据库,它适合于管理海量的、简单的数据。如Google使用其来保存账户信息,Heritrix用其来保存froniter. (2)key/value是Berkeley DB用来管理数据的基础,每个key/value对代表一条记录。 (3)Berkeley DB在底层实现采用B树,可以看成能够存储大量数据的HashMa

jediael_lu的专栏 2万+

Vue - 简约大气 404 页面

效果图 实现代码 复制即可。 <template> <div> <div class="error"> <!-- 图片可保存到本地 --> <img src="https://img-blog.csdnimg.cn/20210715111039922.png"> <p>抱歉!你访问的页面不存在或已删除 :( </p> <span class="back" @click="go(

🏆 前端领域优质创作者 3370

从业务角色到工作台:一步步创建 SAP Fiori Launchpad Space 与 Page

SAP S/4HANA Launchpad 配置优化指南:从业务角色出发构建高效工作台 本文针对S/4HANA项目中用户普遍反映的"入口难找"问题,提出基于Spaces和Pages的系统化解决方案。通过"以工作岗位为中心"的设计理念,将Launchpad信息架构重构为:一个业务角色对应一个Space,每个Space包含1-5个Page,Page内按任务划分2-5个Section,每个Section放置3-7个Tiles。文章详细阐述了从Business Role入手配

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 224

Berkeley DB 概述

Berkeley DB 概述Berkeley DB是由美国Sleepycat Software公司开发的一套开放源码的嵌入式数据库的程序库(database library),它为应用程序提供可伸缩的、高性能的、有事务保护功能的数据管理服务。Berkeley DB为数据的存取和管理提供了一组简洁的函数调用API接口。 全文>>> 数据库巨人Oracle在2006年2月14日宣布已经收购

小白的一生 2273

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

嵌入式数据库BERKELEY DB 之dubbo实战

berkeley db 时oracle旗下的一款嵌入式数据库。。。当然,在互联网业界,他并不火,但是它确实很省内存,,对于一些想要替换redis的解决方案--可以考虑。。想要学习它。甚至在一些主流的网站上看不到它的相关资料。。。此时我们可以结合官网来进行学习和实践。本文集合dubbo+berkeley 来阐述下小编的实战以及需要注意的问题。。。。 1.Berkeley DB简介 Berkeley DB是一个开源的文件数据库,介于关系数据库与内存数据库之间,使用方式与内存数据库类似,它提供的是一系列直接访

huanglei_hacker的博客 605

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Berkeley DB学习(一)

一、简介 定义:Berkeley DB是一个开源的文件数据库,介于关系数据库与内存数据库之间,使用方式与内存数据库类似,它提供的是一系列直接访问数据库的函数,而不是像关系数据库那样需要网络通讯、SQL解析等步骤。 1)开源的kv类型数据库 2)文件数据库: 优点:数据保存在单一文件中,部署以及发布简单,使用内嵌在应用程序中。 缺点:数据库打开时,文件会被加载到内存,因为数据库不宜过大。 3)嵌入式...

weixin_40235225的博客 6555

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

Oracle Berkeley DB Java 版

Oracle Berkeley DB Java 版是一个开源的、可嵌入的事务存储引擎,是完全用 Java 编写的。它充分利用 Java 环境来简化开发和部署。Oracle Berkeley DB Java 版的架构对于读密集型和写密集型的负载提供非常高的性能和并发性。您可以根据需要选择 Berkeley DB Java 版的 Direct Persistence Layer (DPL)、Persi

成长是一个持续的过程 1885
上一篇: 最便捷的序列化方式
下一篇: [linux proc] undefined reference to `ECPGget_sqlca' 问题解决
inter999
博客等级 码龄19年 18粉丝 9原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值