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

计算机三级考试嵌入式系统重点精简汇总

第一章 嵌入式系统概论 1.1.1嵌入式系统 (1)特点:(1)专用性;(2)隐蔽性;(3)资源受限;(4)高可靠性;(5)实时性;(6)软件固化。 (2)应用:(1)冰箱、洗衣机、电饭煲、空调、摄影机、DVD播放器等;(2)数控机床、飞机、汽车、电梯、X光机、血压计等;(3)电话机、传真机、打印机、pos机、GPS导航仪、路由器等。 1.1.2嵌入式系统组成与分类 (1)逻辑组成:硬件和软件,分为处理器、存储器、I|0设备与I|O接口,数据总线; (2)处理器:分为主处理器(CPU)和协处

chemkrik的博客 1万+

AGV路径规划实战:如何用Python+ROS实现激光SLAM导航(附避障代码)

本文提供了一份基于Python和ROS实现AGV激光SLAM导航的实战指南。内容涵盖从Docker环境搭建、激光雷达数据处理、Gmapping建图,到Move_Base全局/局部路径规划与TEB局部规划器配置的全流程。重点解析了如何通过参数调优实现稳定导航,并附带了紧急避障代码,帮助开发者快速构建具备自主路径规划与实时避障能力的AGV系统。

pepsi的博客 1234

Berkeley DB使用方法

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

iintsy的专栏 3090

SAP_SD模块-销售批次策略应用记录

SAP_SD模块-销售批次策略完整案例

风沙的博客 2610

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

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

aim73056的博客 180

【光伏风电功率预测】基于 ICEEMDAN + MSCNN-BiGRU-Attention 的并行预测模型

本文提出一种基于ICEEMDAN分解与多尺度深度学习的光伏/风电功率并行预测框架。针对多能源、多时间尺度、强非平稳特性带来的建模挑战,该模型首先通过改进的ICEEMDAN算法将原始功率序列分解为不同频段的IMF分量;随后为每个IMF构建包含多尺度CNN、双向GRU和注意力机制的子模型,实现局部特征提取与时序依赖建模;最后通过并行预测与重构获得最终功率输出。实验表明,该方法能有效解决模式混叠问题,提升预测精度,并支持光伏/风电的独立或联合预测部署,适用于风光联合调度等工程场景。

专注AI大模型,软件混淆,授权 154

两组相机(或三维点)对齐方法介绍及实现代码(求解相似变换,包含旋转R、平移t、尺度s)

  这个变换用于将生成的稀疏重建场景对齐到真值上。由于SfM(Structure from Motion,运动恢复结构)求解的结果是尺度未定的,所以重建场景与真值之间不仅会相差一个旋转与平移,还会存在尺度缩放。待求解的问题可以描述为:已知一组相机(个数为m)的位姿真值Rgt、tgt和预测位姿Rpred、tpred,现要将预测值对齐到真值上。它们之间会相差一个相似变换Msimilarity,包括尺度s、旋转量R、平移量t,可以根据如下步骤进行求解:    1 求解旋转R   首先计算所有m个相机的位姿差异总和

zeeq的博客 3926

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

【面试系列】软件架构师 高频面试题及详细解答

本文介绍了软件架构师岗位的初级、中级和高级面试题及详细解答,涵盖了系统设计、面向对象设计、分布式系统、云计算技术、性能优化等关键领域。通过这些问题和解答,候选人可以深入理解软件架构师的核心职责和技能要求。此外,文章总结了在面试中需要掌握的常考知识点,包括架构模式、分布式系统管理、安全性、数据管理和DevOps实践。通过掌握这些知识点,软件架构师能够有效设计和指导复杂系统的结构,确保系统的可扩展性、性能和维护性。

在路上的专栏 850

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万+

【异常】OpenClaw LLM请求超时故障全链路排查与解决方案 LLM request timed out. 网络连接超时了

摘要: OpenClaw LLM请求超时故障是高频网络交互问题,表现为客户端在预设时间内未收到服务端响应。根因分为四类:客户端配置缺陷(如超时阈值不合理)、网络链路异常(防火墙拦截等)、服务端问题(限流/宕机)及硬件瓶颈(算力不足)。解决方案分三步: 通用排查:检查网络连通性、关闭防火墙/代理校验,调整超时阈值至60-300秒; 分场景修复:云端API需核对密钥/限流规则,本地部署需验证服务状态与硬件资源; 兜底方案:重启程序、更新版本或更换服务端点,结合日志精准定位。修复后不影响OpenClaw数据完整性

奶爸的编程之路,也就一周冷个三天 2058

Spring AI Alibaba实战训练营-05模型输出质量评估

本文介绍了使用Spring AI Alibaba库评估AI模型输出质量的方案。项目通过5种评估器实现自动化评估:相关性评估、事实准确性评估、答案相关性评分、答案正确性评估和忠实度评估。开发步骤包括环境准备(Java 17、Maven、API Key获取)、创建Maven项目、配置Spring AI(通过环境变量安全设置API Key)以及实现RAG流程。重点强调了通过评估器自动检测AI输出的质量指标,帮助开发者优化AI应用性能。

program哲学 217

Berkeley DB 概述

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

小白的一生 2273

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

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

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

huanglei_hacker的博客 605

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

Berkeley DB学习(一)

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

weixin_40235225的博客 6555

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

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、付费专栏及课程。

余额充值