1
0
mirror of https://github.com/MariaDB/server.git synced 2025-11-09 11:41:36 +03:00
Files
mariadb/bdb/docs/ref/transapp/inc.html
2001-03-04 19:42:05 -05:00

202 lines
7.9 KiB
HTML

<!--$Id: inc.so,v 1.6 2000/08/08 19:58:20 bostic Exp $-->
<!--Copyright 1997, 1998, 1999, 2000 by Sleepycat Software, Inc.-->
<!--All rights reserved.-->
<html>
<head>
<title>Berkeley DB Reference Guide: Atomicity</title>
<meta name="description" content="Berkeley DB: An embedded database programmatic toolkit.">
<meta name="keywords" content="embedded,database,programmatic,toolkit,b+tree,btree,hash,hashing,transaction,transactions,locking,logging,access method,access methods,java,C,C++">
</head>
<body bgcolor=white>
<table><tr valign=top>
<td><h3><dl><dt>Berkeley DB Reference Guide:<dd>Transaction Protected Applications</dl></h3></td>
<td width="1%"><a href="../../ref/transapp/put.html"><img src="../../images/prev.gif" alt="Prev"></a><a href="../../ref/toc.html"><img src="../../images/ref.gif" alt="Ref"></a><a href="../../ref/transapp/read.html"><img src="../../images/next.gif" alt="Next"></a>
</td></tr></table>
<p>
<h1 align=center>Atomicity</h1>
<p>The third reason listed for using transactions was atomicity. Consider
an application suite where multiple threads of control (multiple
processes or threads in one or more processes) are changing the values
associated with a key in one or more databases. Specifically, they are
taking the current value, incrementing it, and then storing it back into
the database.
<p>Such an application requires atomicity. Since we want to change a value
in the database, we must make sure that once we read it, no other thread
of control modifies it. For example, assume that both thread #1 and
thread #2 are doing similar operations in the database, where thread #1
is incrementing records by 3, and thread #2 is incrementing records by
5. We want to increment the record by a total of 8. If the operations
interleave in the right (well, wrong) order, that is not what will
happen:
<p><blockquote><pre>thread #1 <b>read</b> record: the value is 2
thread #2 <b>read</b> record: the value is 2
thread #2 <b>write</b> record + 5 back into the database (new value 7)
thread #1 <b>write</b> record + 3 back into the database (new value 5)</pre></blockquote>
<p>As you can see, instead of incrementing the record by a total of 8,
we've only incremented it by 3, because thread #1 overwrote thread #2's
change. By wrapping the operations in transactions, we ensure that this
cannot happen. In a transaction, when the first thread reads the
record, locks are acquired that will not be released until the
transaction finishes, guaranteeing that all other readers and writers
will block, waiting for the first thread's transaction to complete (or
to be aborted).
<p>Here is an example function that does transaction-protected increments
on database records to ensure atomicity.
<p><blockquote><pre>int
main(int argc, char *argv)
{
extern char *optarg;
extern int optind;
DB *db_cats, *db_color, *db_fruit;
DB_ENV *dbenv;
pthread_t ptid;
int ch;
<p>
while ((ch = getopt(argc, argv, "")) != EOF)
switch (ch) {
case '?':
default:
usage();
}
argc -= optind;
argv += optind;
<p>
env_dir_create();
env_open(&dbenv);
<p>
/* Open database: Key is fruit class; Data is specific type. */
db_open(dbenv, &db_fruit, "fruit", 0);
<p>
/* Open database: Key is a color; Data is an integer. */
db_open(dbenv, &db_color, "color", 0);
<p>
/*
* Open database:
* Key is a name; Data is: company name, address, cat breeds.
*/
db_open(dbenv, &db_cats, "cats", 1);
<p>
add_fruit(dbenv, db_fruit, "apple", "yellow delicious");
<p>
<b> add_color(dbenv, db_color, "blue", 0);
add_color(dbenv, db_color, "blue", 3);</b>
<p>
return (0);
}
<p>
<b>void
add_color(DB_ENV *dbenv, DB *dbp, char *color, int increment)
{
DBT key, data;
DB_TXN *tid;
int original, ret;
char buf64;
<p>
/* Initialization. */
memset(&key, 0, sizeof(key));
key.data = color;
key.size = strlen(color);
memset(&data, 0, sizeof(data));
data.flags = DB_DBT_MALLOC;
<p>
for (;;) {
/* Begin the transaction. */
if ((ret = txn_begin(dbenv, NULL, &tid, 0)) != 0) {
dbenv-&gt;err(dbenv, ret, "txn_begin");
exit (1);
}
<p>
/*
* Get the key. If it exists, we increment the value. If it
* doesn't exist, we create it.
*/
switch (ret = dbp-&gt;get(dbp, tid, &key, &data, 0)) {
case 0:
original = atoi(data.data);
break;
case DB_LOCK_DEADLOCK:
/* Deadlock: retry the operation. */
if ((ret = txn_abort(tid)) != 0) {
dbenv-&gt;err(dbenv, ret, "txn_abort");
exit (1);
}
continue;
case DB_NOTFOUND:
original = 0;
break;
default:
/* Error: run recovery. */
dbenv-&gt;err(
dbenv, ret, "dbc-&gt;get: %s/%d", color, increment);
exit (1);
}
if (data.data != NULL)
free(data.data);
<p>
/* Create the new data item. */
(void)snprintf(buf, sizeof(buf), "%d", original + increment);
data.data = buf;
data.size = strlen(buf) + 1;
<p>
/* Store the new value. */
switch (ret = dbp-&gt;put(dbp, tid, &key, &data, 0)) {
case 0:
/* Success: commit the change. */
if ((ret = txn_commit(tid, 0)) != 0) {
dbenv-&gt;err(dbenv, ret, "txn_commit");
exit (1);
}
return;
case DB_LOCK_DEADLOCK:
/* Deadlock: retry the operation. */
if ((ret = txn_abort(tid)) != 0) {
dbenv-&gt;err(dbenv, ret, "txn_abort");
exit (1);
}
break;
default:
/* Error: run recovery. */
dbenv-&gt;err(
dbenv, ret, "dbc-&gt;put: %s/%d", color, increment);
exit (1);
}
}
}</b></pre></blockquote>
<p>Any number of operations, on any number of databases, can be included
in a single transaction to ensure atomicity of the operations. There
is, however, a trade-off between the number of operations included in
a single transaction and both throughput and the possibility of
deadlock. The reason for this is because transactions acquire locks
throughout their lifetime, and do not release them until transaction
commit or abort. So, the more operations included in a transaction,
the more likely that a transaction will block other operations and that
deadlock will occur. However, each transaction commit requires a
synchronous disk I/O, so grouping multiple operations into a transaction
can increase overall throughput. (There is one exception to this. The
<a href="../../api_c/env_open.html#DB_TXN_NOSYNC">DB_TXN_NOSYNC</a> option causes transactions to exhibit the ACI
(atomicity, consistency and isolation) properties, but not D
(durability), avoiding the synchronous disk I/O on transaction commit
and greatly increasing transaction throughput for some applications.
<p>When applications do create complex transactions, they often avoid
having more than one complex transaction at a time, as simple operations
like a single <a href="../../api_c/db_put.html">DB-&gt;put</a> are unlikely to deadlock with each other
or the complex transaction, while multiple complex transactions are
likely to deadlock with each other as they will both acquire many locks
over their lifetime. Alternatively, complex transactions can be broken
up into smaller sets of operations, and each of those sets may be
encapsulated in a nested transaction. Because nested transactions may
be individually aborted and retried without causing the entire
transaction to be aborted, this allows complex transactions to proceed
even in the face of heavy contention, repeatedly trying the
sub-operations until they succeed.
<p>It is also helpful to order operations within a transaction, that is,
access the databases and items within the databases in the same order,
to the extent possible, in all transactions. Accessing databases and
items in different orders greatly increases the likelihood of operations
being blocked and failing due to deadlocks.
<table><tr><td><br></td><td width="1%"><a href="../../ref/transapp/put.html"><img src="../../images/prev.gif" alt="Prev"></a><a href="../../ref/toc.html"><img src="../../images/ref.gif" alt="Ref"></a><a href="../../ref/transapp/read.html"><img src="../../images/next.gif" alt="Next"></a>
</td></tr></table>
<p><font size=1><a href="http://www.sleepycat.com">Copyright Sleepycat Software</a></font>
</body>
</html>