Berkeley DB: Proceedings of The FREENIX Track
Berkeley DB: Proceedings of The FREENIX Track
Berkeley DB
1999 by The USENIX Association All Rights Reserved Rights to individual papers remain with the author or the author's employer. Permission is granted for noncommercial reproduction of the work for educational or research purposes. This copyright notice must be included in the reproduced paper. USENIX acknowledges all trademarks herein. For more information about the USENIX Association: Phone: 1 510 528 8649 FAX: 1 510 548 5738 Email: office@[Link] WWW: [Link]
Berkeley DB
Michael A. Olson Keith Bostic Margo Seltzer Sleepycat Software, Inc. Abstract
Berkeley DB is an Open Source embedded database system with a number of key advantages over comparable systems. It is simple to use, supports concurrent access by multiple users, and provides industrial-strength transaction support, including surviving system and disk crashes. This paper describes the design and technical features of Berkeley DB, the distribution, and its license.
1. Introduction
The Berkeley Database (Berkeley DB) is an embedded database system that can be used in applications requiring high-performance concurrent storage and retrieval of key/value pairs. The software is distributed as a library that can be linked directly into an application. It provides a variety of programmatic interfaces, including callable APIs for C, C++, Perl, Tcl and Java. Users may download Berkeley DB from Sleepycat Softwares Web site, at [Link]. Sleepycat distributes Berkeley DB as an Open Source product. The company collects license fees for certain uses of the software and sells support and services.
distributes, and supports Berkeley DB and supporting software and documentation. Sleepycat released version 2.1 of Berkeley DB in mid-1997 with important new features, including support for concurrent access to databases. The company makes about three commercial releases a year, and most recently shipped version 2.8.
1.1. History
Berkeley DB began as a new implementation of a hash access method to replace both hsearch and the various dbm implementations (dbm from AT&T, ndbm from Berkeley, and gdbm from the GNU project). In 1990 Seltzer and Yigit produced a package called Hash to do this [Selt91]. The rst general release of Berkeley DB, in 1991, included some interface changes and a new B+tree access method. At roughly the same time, Seltzer and Olson developed a prototype transaction system based on Berkeley DB, called LIBTP [Selt92], but never released the code. The 4.4BSD UNIX release included Berkeley DB 1.85 in 1992. Seltzer and Bostic maintained the code in the early 1990s in Berkeley and in Massachusetts. Many users adopted the code during this period. By mid-1996, users wanted commercial support for the software. In response, Bostic and Seltzer formed Sleepycat Software. The company enhances,
operations on behalf of clients. Compared to using a standalone database management system, Berkeley DB is easy to understand and simple to use. The software stores and retrieves records, which consist of key/value pairs. Keys are used to locate items and can be any data type or structure supported by the programming language. The programmer can provide the functions that Berkeley DB uses to operate on keys. For example, B+trees can use a custom comparison function, and the Hash access method can use a custom hash function. Berkeley DB uses default functions if none are supplied. Otherwise, Berkeley DB does not examine or interpret either keys or values in any way. Values may be arbitrarily long. It is also important to understand what Berkeley DB is not. It is not a database server that handles network requests. It is not an SQL engine that executes queries. It is not a relational or object-oriented database management system. It is possible to build any of those on top of Berkeley DB, but the package, as distributed, is an embedded database engine. It has been designed to be portable, small, fast, and reliable.
include managing access control lists, storing user keys in a public-key infrastructure, recording machine-tonetwork-address mappings in address servers, and storing conguration and device information in video postproduction software. Finally, Berkeley DB is a part of many other Open Source software packages available on the Internet. For example, the software is embedded in the Apache Web server and the Gnome desktop.
2. Access Methods
In database terminology, an access method is the diskbased structure used to store data and the operations available on that structure. For example, many database systems support a B+tree access method. B+trees allow equality-based lookups (nd keys equal to some constant), range-based lookups (nd keys between two constants) and record insertion and deletion. Berkeley DB supports three access methods: B+tree, Extended Linear Hashing (Hash), and Fixed- or Variable-length Records (Recno). All three operate on records composed of a key and a data value. In the B+tree and Hash access methods, keys can have arbitrary structure. In the Recno access method, each record is assigned a record number, which serves as the key. In all the access methods, the value can have arbitrary structure. The programmer can supply comparison or hashing functions for keys, and Berkeley DB stores and retrieves values without interpreting them. All of the access methods use the host lesystem as a backing store.
2.1. Hash
Berkeley DB includes a Hash access method that implements extended linear hashing [Litw80]. Extended linear hashing adjusts the hash function as the hash table grows, attempting to keep all buckets underfull in the steady state. The Hash access method supports insertion and deletion of records and lookup by exact match only. Applications may iterate over all records stored in a table, but the order in which they are returned is undened.
2.2. B+tree
Berkeley DB includes a B+tree [Come79] access method. B+trees store records of key/value pairs in leaf pages, and pairs of (key, child page address) at internal nodes. Keys in the tree are stored in sorted order,
where the order is determined by the comparison function supplied when the database was created. Pages at the leaf level of the tree include pointers to their neighbors to simplify traversal. B+trees support lookup by exact match (equality) or range (greater than or equal to a key). Like Hash tables, B+trees support record insertion, deletion, and iteration over all records in the tree. As records are inserted and pages in the B+tree ll up, they are split, with about half the keys going into a new peer page at the same level in the tree. Most B+tree implementations leave both nodes half-full after a split. This leads to poor performance in a common case, where the caller inserts keys in order. To handle this case, Berkeley DB keeps track of the insertion order, and splits pages unevenly to keep pages fuller. This reduces tree size, yielding better search performance and smaller databases. On deletion, empty pages are coalesced by reverse splits into single pages. The access method does no other page balancing on insertion or deletion. Keys are not moved among pages at every update to keep the tree well-balanced. While this could improve search times in some cases, the additional code complexity leads to slower updates and is prone to deadlocks. For simplicity, Berkeley DB B+trees do no prex compression of keys at internal or leaf nodes.
by their application. For example, when an application opens a database, it can declare the degree of concurrency and recovery that it requires. Simple stand-alone applications, and in particular ports of applications that used dbm or one of its variants, generally do not require concurrent access or crash recovery. Other applications, such as enterpriseclass database management systems that store sales transactions or other critical data, need full transactional service. Single-user operation is faster than multi-user operation, since no overhead is incurred by locking. Running with the recovery system disabled is faster than running with it enabled, since log records need not be written when changes are made to the database. In addition, some core subsystems, including the locking system and the logging facility, can be used outside the context of the access methods as well. Although few users have chosen to do so, it is possible to use only the lock manager in Berkeley DB to control concurrency in an application, without using any of the standard database services. Alternatively, the caller can integrate locking of non-database resources with Berkeley DBs transactional two-phase locking system, to impose transaction semantics on objects outside the database.
2.3. Recno
Berkeley DB includes a xed- or variable-length record access method, called Recno. The Recno access method assigns logical record numbers to each record, and can search for and update records by record number. Recno is able, for example, to load a text le into a database, treating each line as a record. This permits fast searches by line number for applications like text editors [Ston82]. Recno is actually built on top of the B+tree access method and provides a simple interface for storing sequentially-ordered data values. The Recno access method generates keys internally. The programmers view of the values is that they are numbered sequentially from one. Developers can choose to have records automatically renumbered when lower-numbered records are added or deleted. In this case, new keys can be inserted between existing keys.
a time. The order of the records returned during sequential scans depends on the access method. B+tree and Recno databases return records in sort order, and Hash databases return them in apparently random order. Similarly, Berkeley DB denes simple interfaces for inserting, updating, and deleting records in a database.
directly into the Berkeley DB pages, writes cannot be permitted. Otherwise, changes could bypass the locking and logging systems, and software errors could corrupt the database. Read-only applications can use Berkeley DBs memory-mapped le service to improve performance on most architectures.
3.8. Cursors
In database terminology, a cursor is a pointer into an access method that can be called iteratively to return records in sequence. Berkeley DB includes cursor interfaces for all access methods. This permits, for example, users to traverse a B+tree and view records in order. Pointers to records in cursors are persistent, so that once fetched, a record may be updated in place. Finally, cursors support access to chains of duplicate data items in the various access methods.
3.9. Joins
In database terminology, a join is an operation that spans multiple separate tables (or in the case of Berkeley DB, multiple separate DB les). For example, a company may store information about its customers in one table and information about sales in another. An application will likely want to look up sales information by customer name; this requires matching records in the two tables that share a common customer ID eld. This combining of records from multiple tables is called a join. Berkeley DB includes interfaces for joining two or more tables.
3.10. Transactions
Transactions have four properties [Gray93]:
performance.
They are atomic. That is, all of the changes made in a single transaction must be applied at the same instant or not at all. This permits, for example, the transfer of money between two accounts to be accomplished, by making the reduction of the balance in one account and the increase in the other into a single, atomic action. They must be consistent. That is, changes to the database by any transaction cannot leave the database in an illegal or corrupt state. They must be isolatable. Regardless of the number of users working in the database at the same time, every user must have the illusion that no other activity is going on. They must be durable. Even if the disk that stores the database is lost, it must be possible to recover the database to its last transaction-consistent state.
This combination of properties atomicity, consistency, isolation, and durability is referred to as ACIDity in the literature. Berkeley DB, like most database systems, provides ACIDity using a collection of core services. Programmers can choose to use Berkeley DBs transaction services for applications that need them.
3.10.3. Checkpoints
Berkeley DB includes a checkpointing service that interacts with the recovery system. During normal processing, both the log and the database are changing continually. At any given instant, the on-disk versions of the two are not guaranteed to be consistent. The log probably contains changes that are not yet in the database. When an application makes a checkpoint, all committed changes in the log up to that point are guaranteed to be present on the data disk, too. Checkpointing is moderately expensive during normal processing, but limits the time spent recovering from crashes. After an application or operating system crash, the recovery system only needs to go back two checkpoints1 to start rolling the log forward. Without checkpoints, there is no way to be sure how long restarting after a crash will take. With checkpoints, the restart interval can be xed by the programmer. Recovery processing can be guaranteed to complete in a second or two. Software crashes are much more common than disk failures. Many developers want to guarantee that software bugs do not destroy data, but are willing to restore from tape, and to tolerate a day or two of lost work, in the unlikley event of a disk crash. With Berkeley DB, programmers may truncate the log at checkpoints. As long as the two most recent checkpoints are present, the recovery system can guarantee that no committed transactions are lost after a software crash. In this case, the recovery system does not require that the log and the data be on separate devices, although separating them can still improve performance by spreading out writes.
Berkeley DB can lock entire database les, which correspond to tables, or individual pages in them. It does no record-level locking. By shrinking the page size, however, developers can guarantee that every page holds only a small number of records. This reduces contention. If locking is enabled, then read and write operations on a database acquire two-phase locks, which are held until the transaction completes. Which objects are locked and the order of lock acquisition depend on the workload for each transaction. It is possible for two or more transactions to deadlock, so that each is waiting for a lock that is held by another. Berkeley DB detects deadlocks and automatically rolls back one of the transactions. This releases the locks that it held and allows the other transactions to continue. The caller is notied that its transaction did not complete, and may restart it. Developers can specify the deadlock detection interval and the policy to use in choosing a transaction to roll back. The two-phase locking interfaces are separately callable by applications that link Berkeley DB, though few users have needed to use that facility directly. Using these interfaces, Berkeley DB provides a fast, platform-portable locking system for general-purpose use. It also lets users include non-database objects in a database transaction, by controlling access to them exactly as if they were inside the database. The Berkeley DB two-phase locking facility is built on the fastest correct locking primitives that are supported by the underlying architecture. In the current implementation, this means that the locking system is different on the various UNIX platforms, and is still more different on Windows NT. In our experience, the most difcult aspect of performance tuning is nding the fastest locking primitives that work correctly on a particular architecture and then integrating the new interface with the several that we already support. The world would be a better place if the operating systems community would uniformly implement POSIX locking primitives and would guarantee that acquiring an uncontested lock was a fast operation. Locks must work both among threads in a single process and among processes.
3.11. Concurrency
Good performance under concurrent operation is a critical design point for Berkeley DB. Although Berkeley DB is itself not multi-threaded, it is thread-safe, and runs well in threaded applications. Philosophically, we view the use of threads and the choice of a threads
package as a policy decision, and prefer to offer mechanism (the ability to run threaded or not), allowing applications to choose their own policies. The locking, logging, and buffer pool subsystems all use shared memory or other OS-specic sharing facilities to communicate. Locks, buffer pool fetches, and log writes behave in the same way across threads in a single process as they do across different processes on a single machine. As a result, concurrent database applications may start up a new process for every single user, may create a single server which spawns a new thread for every client request, or may choose any policy in between. Berkeley DB has been carefully designed to minimize contention and maximize concurrency. The cache manager allows all threads or processes to benet from I/O done by one. Shared resources must sometimes be locked for exclusive access by one thread of control. We have kept critical sections small, and are careful not to hold critical resource locks across system calls that could deschedule the locking thread or process. Sleepycat Software has customers with hundreds of concurrent users working on a single database in production.
superior, as an embedded database system, to any other solution available. Most database systems trade off simplicity for correctness. Either the system is easy to use, or it supports concurrent use and survives system failures. Berkeley DB, because of its careful design and implementation, offers both simplicity and correctness. The system has a small footprint, makes simple operations simple to carry out (inserting a new record takes just a few lines of code), and behaves correctly in the face of heavy concurrent use, system crashes, and even catastrophic failures like loss of a hard disk.
4. Engineering Philosophy
Fundamentally, Berkeley DB is a collection of access methods with important facilities, like logging, locking, and transactional access underlying them. In both the research and the commercial world, the techniques for building systems like Berkeley DB have been wellknown for a long time. The key advantage of Berkeley DB is the careful attention that has been paid to engineering details throughout its life. We have carefully designed the system so that the core facilities, like locking and I/O, surface the right interfaces and are otherwise opaque to the caller. As programmers, we understand the value of simplicity and have worked hard to simplify the interfaces we surface to users of the database system. Berkeley DB avoids limits in the code. It places no practical limit on the size of keys, values, or databases; they may grow to occupy the available storage space. The locking and logging subsystems have been carefully crafted to reduce contention and improve throughput by shrinking or eliminating critical sections, and reducing the sizes of locked regions and log entries. There is nothing in the design or implementation of Berkeley DB that pushes the state of the art in database systems. Rather, we have been very careful to get the engineering right. The result is a system that is
5.1.1. Documentation
The distributed system includes documentation in HTML format. The documentation is in two parts: a UNIX-style reference manual for use by programmers, and a reference guide which is tutorial in nature.
always prior to release. The result is a much more reliable system by the time it reaches beta release.
To preserve the Open Source heritage of the older Berkeley DB code, we drafted a new license governing the distribution of Berkeley DB 2.x. We adopted terms from the GPL that make it impossible to turn our Open Source code into proprietary code owned by someone else. Briey, the terms governing the use and distribution of Berkeley DB are:
your application must be internal to your site, or your application must be freely redistributable in source form, or you must get a license from us.
For customers who prefer not to distribute Open Source products, we sell licenses to use and extend Berkeley DB at a reasonable cost. We work hard to accommodate the needs of the Open Source community. For example, we have crafted special licensing arrangements with Gnome to encourage its use and distribution of Berkeley DB. Berkeley DB conforms to the Open Source denition [Open99]. The license has been carefully crafted to keep the product available as an Open Source offering, while providing enough of a return on our investment to fund continued development and support of the product. The current license has created a business capable of funding three years of development on the software that simply would not have happened otherwise.
7. Summary
Berkeley DB offers a unique collection of features, targeted squarely at software developers who need simple, reliable database management services in their applications. Good design and implementation and careful engineering throughout make the software better than many other systems. Berkeley DB is an Open Source product, available at [Link] for download. The distributed system includes everything needed to build and deploy the software or to port it to new systems. Sleepycat Software distributes Berkeley DB under a license agreement that draws on both the UC Berkeley copyright and the GPL. The license guarantees that Berkeley DB will remain an Open Source product and provides Sleepycat with opportunities to make money to fund continued development on the software.
8. References
[Come79] Comer, D., The Ubiquitous B-tree, ACM Computing Surveys Volume 11, number 2, June 1979. [Gray93] Gray, J., and Reuter, A., Transaction Processing: Concepts and Techniques, Morgan-Kaufman Publishers, 1993. [IEEE96] Institute for Electrical and Electronics Engineers, IEEE/ANSI Std 1003.1, 1996 Edition. [Litw80] Litwin, W., Linear Hashing: A New Tool for File and Table Addressing, Proceedings of the 6th International Conference on Very Large Databases (VLDB), Montreal, Quebec, Canada, October 1980. [Open94] The Open Group, Distributed TP: The XA+ Specication, Version 2, The Open Group, 1994. [Open99] [Link], Open Source Denition, [Link]/[Link], version 1.4, 1999. [Raym98] Raymond, E.S., The Cathedral and the Bazaar, [Link]/esr/writings/cathedralbazaar/[Link], January 1998. [Selt91] Seltzer, M., and Yigit, O., A New Hashing Package for UNIX, Proceedings 1991 Winter USENIX Conference, Dallas, TX, January 1991. [Selt92] Seltzer, M., and Olson, M., LIBTP: Portable Modular Transactions for UNIX, Proceedings 1992 Winter Usenix Conference, San Francisco, CA, January 1992. [Ston82] Stonebraker, M., Stettner, H., Kalash, J., Guttman, A., and Lynn, N., Document Processing in a Relational Database System, Memorandum No. UCB/ERL M82/32, University of California at Berkeley, Berkeley, CA, May 1982.