Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

LevelDB User Guide

Generated June 2026 from google/leveldb at 7ee830d

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

LevelDB is a fast, embeddable key-value storage library written at Google. It provides an ordered mapping from arbitrary string (byte-array) keys to arbitrary string values, persisted to the local filesystem. You link it directly into your C++ or C application — there is no server, no daemon, and no network protocol.

This guide is written for developers who embed LevelDB in their own programs. It covers building and installing the library, every public operation with copy-paste-ready examples, performance tuning, the bundled leveldbutil command-line tool, troubleshooting, and a reference of every option and status code.

Note

LevelDB is the storage engine that inspired RocksDB and underpins many systems. It is intentionally small and focused: keys/values in, keys/values out, sorted by key. If you need SQL, relations, or a network server, LevelDB is a building block, not a finished product — see Limitations.


Table of Contents

  1. Quick Start
  2. Installation
  3. Core Concepts
  4. Everyday Usage
  5. Iteration
  6. Atomic Updates with WriteBatch
  7. Snapshots
  8. Advanced Usage
  9. Performance Tuning
  10. Using the C API
  11. The leveldbutil Command-Line Tool
  12. Troubleshooting
  13. Frequently Asked Questions
  14. Reference

1. Quick Start

The fastest path from zero to a working key-value store. The following program opens (creating if needed) a database, writes a value, reads it back, and closes cleanly.

#include <cassert>
#include <iostream>
#include <string>

#include "leveldb/db.h"

int main() {
  leveldb::DB* db;
  leveldb::Options options;
  options.create_if_missing = true;

  // Open (or create) the database stored in the directory /tmp/testdb.
  leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);
  assert(status.ok());

  // Write a key/value pair.
  status = db->Put(leveldb::WriteOptions(), "name", "leveldb");
  assert(status.ok());

  // Read it back.
  std::string value;
  status = db->Get(leveldb::ReadOptions(), "name", &value);
  if (status.ok()) {
    std::cout << "name = " << value << std::endl;
  }

  // Close the database by deleting the handle.
  delete db;
  return 0;
}

Compile and run (assuming LevelDB is installed — see Installation):

c++ -std=c++17 quickstart.cc -lleveldb -o quickstart
./quickstart
name = leveldb

Important

A LevelDB "database" is a directory on disk, not a single file. The name you pass to DB::Open (/tmp/testdb above) is the path to that directory. LevelDB owns every file inside it.

Tip

Always check the returned leveldb::Status. Almost every operation that can fail returns one. The idiom if (!status.ok()) { ... } should appear throughout your code — see Core Concepts.


2. Installation

LevelDB is distributed as source and built with CMake. It has no required third-party dependencies; compression and CRC acceleration libraries are detected and linked automatically if present.

2.1 Requirements

Requirement Detail
C++ compiler C++17 required (CMAKE_CXX_STANDARD 17)
CMake 3.22 or newer (cmake_minimum_required(VERSION 3.22))
Build threads POSIX threads (Threads::Threads) — found automatically
Platforms POSIX (Linux, macOS) and Windows are the tested, supported platforms

Optional libraries, auto-detected at configure time, that improve LevelDB if installed:

Library Effect when present
snappy Enables Snappy block compression (the default compression)
zstd Enables Zstandard block compression (kZstdCompression)
crc32c Hardware-accelerated CRC32C checksums
tcmalloc Faster memory allocation

Note

If snappy is not installed, LevelDB still builds and runs — blocks are simply stored uncompressed. The Options::compression default of kSnappyCompression becomes a no-op at runtime when Snappy support is absent.

2.2 Getting the source

The repository uses git submodules (GoogleTest and Google Benchmark, used only for tests and benchmarks). Clone recursively:

git clone --recurse-submodules https://github.com/google/leveldb.git
cd leveldb

2.3 Building

Linux / macOS

mkdir -p build && cd build
cmake -DCMAKE_BUILD_TYPE=Release .. && cmake --build .

Windows (Visual Studio)

mkdir build
cd build
cmake -G "Visual Studio 15" ..

For a 64-bit build, generate with the Win64 generator instead:

cmake -G "Visual Studio 15 Win64" ..

Then build from the command line or open leveldb.sln in Visual Studio:

devenv /build Debug leveldb.sln

A successful build produces the static library (libleveldb.a / leveldb.lib) plus the leveldbutil tool. Expected tail of output:

[100%] Built target leveldb
[100%] Built target leveldbutil

2.4 Build options

These CMake cache options control what gets built. Pass them as -D<OPTION>=<VALUE> to cmake.

Option Default Purpose
LEVELDB_BUILD_TESTS ON Build the unit-test suite
LEVELDB_BUILD_BENCHMARKS ON Build the db_bench benchmark programs
LEVELDB_INSTALL ON Generate install rules for the headers + library
BUILD_SHARED_LIBS OFF Build a shared library instead of a static one

Example: build a release shared library, no tests or benchmarks:

cmake -DCMAKE_BUILD_TYPE=Release \
      -DBUILD_SHARED_LIBS=ON \
      -DLEVELDB_BUILD_TESTS=OFF \
      -DLEVELDB_BUILD_BENCHMARKS=OFF ..
cmake --build .

Tip

Disabling LEVELDB_BUILD_TESTS and LEVELDB_BUILD_BENCHMARKS removes the need for the GoogleTest and Benchmark submodules and noticeably speeds up the build when you only need the library itself.

2.5 Installing

With LEVELDB_INSTALL=ON (the default), install the headers, library, and CMake package config:

cmake --build . --target install

This installs:

  • The library to your platform's library directory.
  • The public headers to <prefix>/include/leveldb/.
  • A CMake package (leveldbConfig.cmake) so downstream projects can find_package(leveldb).

2.6 Using LevelDB from your CMake project

Once installed, link against the imported target leveldb::leveldb:

find_package(leveldb REQUIRED)

add_executable(myapp main.cc)
target_link_libraries(myapp PRIVATE leveldb::leveldb)

Caution

Include only the public headers under include/leveldb/. Headers elsewhere in the source tree (e.g. db/, table/, util/) are internal and may change without warning between releases.


3. Core Concepts

A handful of small types appear throughout the API. Understanding them up front makes every later example obvious.

3.1 DB — the database handle

leveldb::DB is an abstract class. You obtain a concrete instance from the static DB::Open factory and you destroy it with delete. A DB is safe for concurrent use by multiple threads with no external locking.

3.2 Slice — a pointer + length

leveldb::Slice is a lightweight, non-owning view over a byte array: just a pointer and a size. It is how LevelDB avoids copying potentially large keys and values. Because LevelDB keys and values are arbitrary byte arrays (they may contain '\0'), the API uses Slice rather than C strings.

A Slice is constructible from both std::string and C strings, and convertible back:

leveldb::Slice s1 = "hello";          // from a C string
std::string str("world");
leveldb::Slice s2 = str;              // from a std::string
std::string back = s1.ToString();     // back to std::string

Warning

A Slice does not own its data. The caller must keep the backing byte array alive for as long as the Slice is used. This is buggy:

leveldb::Slice slice;
if (cond) {
  std::string str = ComputeKey();
  slice = str;        // slice points into str
}                     // str is destroyed here
Use(slice);           // slice now dangles — undefined behavior

3.3 Status — success or error

leveldb::Status is the universal return type for fallible operations. Check ok(), and classify errors with the Is* predicates.

leveldb::Status s = db->Get(leveldb::ReadOptions(), "missing", &value);
if (s.ok()) {
  // found
} else if (s.IsNotFound()) {
  // key does not exist
} else {
  std::cerr << s.ToString() << std::endl;   // some other error
}

The error categories are: IsNotFound(), IsCorruption(), IsIOError(), IsNotSupportedError(), and IsInvalidArgument(). See the Status reference.

3.4 Options, ReadOptions, WriteOptions

Behavior is configured with three plain structs:

  • Options — passed once to DB::Open; controls the whole database (cache, compression, comparator, etc.).
  • ReadOptions — passed per read; controls checksum verification, caching, and snapshots.
  • WriteOptions — passed per write; controls durability (the sync flag).

All three have sensible defaults; you only set the fields you care about. Full tables are in the Reference.


4. Everyday Usage

The three basic operations are Put, Get, and Delete. All keys and values are Slices.

4.1 Writing a value — Put

leveldb::Status s = db->Put(leveldb::WriteOptions(), key, value);

Put inserts or overwrites the entry for key.

4.2 Reading a value — Get

std::string value;
leveldb::Status s = db->Get(leveldb::ReadOptions(), key, &value);
if (s.ok()) {
  // value now holds the stored bytes
} else if (s.IsNotFound()) {
  // no such key
}

4.3 Deleting a value — Delete

leveldb::Status s = db->Delete(leveldb::WriteOptions(), key);

It is not an error to delete a key that does not exist; Delete still returns OK.

4.4 A common pattern: move a value between keys

std::string value;
leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value);
if (s.ok()) s = db->Put(leveldb::WriteOptions(), key2, value);
if (s.ok()) s = db->Delete(leveldb::WriteOptions(), key1);

Warning

The code above is not atomic. If the process dies after the Put of key2 but before the Delete of key1, the value is left under both keys. For atomicity, use a WriteBatch.

4.5 Closing the database

There is no Close() method. You close a database by deleting its handle:

delete db;

Important

Delete all iterators, snapshots, and other handles obtained from the DB before you delete the DB itself.


5. Iteration

A LevelDB iterator yields key/value pairs in sorted key order. Get one with NewIterator, and always delete it when done.

5.1 Forward iteration over the whole database

leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions());
for (it->SeekToFirst(); it->Valid(); it->Next()) {
  std::cout << it->key().ToString() << ": " << it->value().ToString() << "\n";
}
assert(it->status().ok());   // check for errors found during the scan
delete it;

5.2 Iterating a key range [start, limit)

for (it->Seek(start);
     it->Valid() && it->key().ToString() < limit;
     it->Next()) {
  // process it->key(), it->value()
}

5.3 Reverse iteration

for (it->SeekToLast(); it->Valid(); it->Prev()) {
  // process entries from largest key to smallest
}

Note

Reverse iteration may be somewhat slower than forward iteration, by design.

Caution

The Slices returned by it->key() and it->value() are only valid until the next mutation of the iterator. Copy them with ToString() if you need to keep them.

The full iterator interface — Valid(), SeekToFirst(), SeekToLast(), Seek(), Next(), Prev(), key(), value(), status() — is in the Reference.


6. Atomic Updates with WriteBatch

A WriteBatch collects a sequence of Put and Delete operations and applies them to the database atomically — all or nothing.

#include "leveldb/write_batch.h"

std::string value;
leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value);
if (s.ok()) {
  leveldb::WriteBatch batch;
  batch.Delete(key1);
  batch.Put(key2, value);
  s = db->Write(leveldb::WriteOptions(), &batch);
}

Operations within a batch are applied in order. In the example, Delete(key1) is ordered before Put(key2) so that even if key1 == key2, the value is preserved rather than dropped.

WriteBatch has two benefits:

  1. Atomicity — partial application can never be observed, even across a crash.
  2. Throughput — bundling many mutations into one batch is faster than issuing them individually, because the cost of the write (and an optional sync) is amortized.
leveldb::WriteBatch batch;
for (const auto& [k, v] : pending) {
  batch.Put(k, v);
}
db->Write(leveldb::WriteOptions(), &batch);

Tip

To make a bulk update both atomic and durable, set write_options.sync = true on the single Write call. The sync cost is paid once for the whole batch.


7. Snapshots

A snapshot is a consistent, read-only view of the entire database at a point in time. Reads against a snapshot ignore any writes made after the snapshot was taken.

leveldb::ReadOptions options;
options.snapshot = db->GetSnapshot();

// ... apply some updates to db ...

leveldb::Iterator* iter = db->NewIterator(options);
// ... iter sees the DB exactly as it was when the snapshot was created ...
delete iter;

db->ReleaseSnapshot(options.snapshot);

If ReadOptions::snapshot is nullptr (the default), each read operates on an implicit snapshot of the state at the moment the read begins.

Important

Always call DB::ReleaseSnapshot when you are finished with a snapshot. Holding a snapshot prevents LevelDB from discarding superseded data, so a long-lived snapshot increases disk usage.


8. Advanced Usage

8.1 Custom comparators

By default, keys are ordered lexicographically by byte value (BytewiseComparator). To impose a different order, subclass leveldb::Comparator and pass it via Options::comparator.

class TwoPartComparator : public leveldb::Comparator {
 public:
  // Sort by first number, break ties by second number.
  int Compare(const leveldb::Slice& a, const leveldb::Slice& b) const override {
    int a1, a2, b1, b2;
    ParseKey(a, &a1, &a2);
    ParseKey(b, &b1, &b2);
    if (a1 != b1) return a1 < b1 ? -1 : +1;
    if (a2 != b2) return a2 < b2 ? -1 : +1;
    return 0;
  }
  const char* Name() const override { return "TwoPartComparator"; }
  void FindShortestSeparator(std::string*, const leveldb::Slice&) const override {}
  void FindShortSuccessor(std::string*) const override {}
};
TwoPartComparator cmp;
leveldb::Options options;
options.create_if_missing = true;
options.comparator = &cmp;
leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);

Caution

The comparator's Name() is recorded when the database is created and verified on every open. Changing the name makes existing databases unopenable. Change the name only when the new ordering is genuinely incompatible with old data. To evolve a key format safely, keep the name and embed a version byte in your keys.

8.2 Bloom filters

A FilterPolicy reduces the number of disk reads per Get. The built-in Bloom filter is the common choice.

#include "leveldb/filter_policy.h"

leveldb::Options options;
options.filter_policy = leveldb::NewBloomFilterPolicy(10);  // 10 bits per key
leveldb::DB* db;
leveldb::DB::Open(options, "/tmp/testdb", &db);
// ... use the database ...
delete db;
delete options.filter_policy;   // delete AFTER the db is closed

NewBloomFilterPolicy(10) keeps ~10 bits per key in memory and cuts unnecessary disk reads for point lookups by roughly 100×. More bits per key = fewer disk reads, at the cost of more memory.

Tip

Set a filter policy if your working set does not fit in memory and you do many random reads. It is one of the highest-impact tuning knobs.

Warning

If you use a custom comparator that ignores parts of keys (e.g. trailing spaces), you must not use NewBloomFilterPolicy directly — supply a matching custom filter policy that ignores the same parts.

8.3 Custom environments (Env)

Every filesystem and OS interaction goes through a leveldb::Env. The default, Env::Default(), is suitable for normal use. Advanced clients can subclass Env (or EnvWrapper, which forwards all calls to another Env) to intercept I/O — for rate limiting, fault injection, or in-memory storage.

class SlowEnv : public leveldb::EnvWrapper {
 public:
  explicit SlowEnv(leveldb::Env* base) : leveldb::EnvWrapper(base) {}
  // override only the methods you want to change
};

SlowEnv env(leveldb::Env::Default());
leveldb::Options options;
options.env = &env;
leveldb::DB::Open(options, "/tmp/testdb", &db);

Note

The library ships an in-memory Env helper (helpers/memenv) that backs a database entirely in RAM — useful for tests. It is not part of the stable public interface.

8.4 Estimating storage size

GetApproximateSizes returns the approximate on-disk bytes used by one or more key ranges.

leveldb::Range ranges[2];
ranges[0] = leveldb::Range("a", "c");
ranges[1] = leveldb::Range("x", "z");
uint64_t sizes[2];
db->GetApproximateSizes(ranges, 2, sizes);
// sizes[0] = approx bytes for [a, c); sizes[1] = approx bytes for [x, z)

Because the sizes reflect compressed file-system space, they will be smaller than the raw user-data size when compression is effective.

8.5 Querying database properties

GetProperty exposes internal statistics as human-readable strings.

std::string stats;
if (db->GetProperty("leveldb.stats", &stats)) {
  std::cout << stats << std::endl;
}
Property Returns
leveldb.num-files-at-level<N> Number of files at level <N> (e.g. leveldb.num-files-at-level0)
leveldb.stats Multi-line table of per-level statistics
leveldb.sstables Multi-line description of all SSTables in the DB
leveldb.approximate-memory-usage Approximate bytes of memory in use by the DB

GetProperty returns false for an unknown property name.

8.6 Manual compaction

LevelDB compacts in the background automatically. You rarely need to trigger it, but CompactRange forces compaction of a key range, discarding deleted/overwritten versions and reorganizing data for faster access.

db->CompactRange(nullptr, nullptr);   // compact the entire database

begin == nullptr means "before all keys"; end == nullptr means "after all keys."

Note

Manual compaction is an advanced operation intended for callers who understand the storage layout. Most applications should rely on automatic background compaction.

8.7 Destroying and repairing a database

These free functions operate on a closed database directory:

#include "leveldb/db.h"

leveldb::Status s = leveldb::DestroyDB("/tmp/testdb", leveldb::Options());
leveldb::Status s = leveldb::RepairDB("/tmp/testdb", leveldb::Options());

DestroyDB deletes the database's contents — use with care. RepairDB attempts to recover as much data as possible from a database that will not open; some data may be lost, so only use it on a database you cannot otherwise open.


9. Performance Tuning

LevelDB's defaults are reasonable, but several Options fields meaningfully affect throughput and latency.

9.1 Synchronous vs. asynchronous writes

By default writes are asynchronous: Put/Write return after handing the data to the operating system, before it reaches persistent storage.

leveldb::WriteOptions write_options;
write_options.sync = true;       // force the write all the way to disk
db->Put(write_options, key, value);
Mode Speed Crash behavior
sync = false (default) Often >1000× faster A machine reboot/power loss may lose the last few updates. A process crash loses nothing.
sync = true Much slower (one disk flush per write) Data is durable once the call returns.

Tip

Asynchronous writes are safe for many workloads. For bulk loads, use sync = false and restart the load from a checkpoint after a crash. Or batch many updates into one WriteBatch and issue a single synchronous Write to amortize the flush cost.

9.2 Block size

options.block_size = 4 * 1024;   // default ~4 KB (uncompressed)
  • Increase for workloads dominated by bulk scans.
  • Consider decreasing for workloads dominated by point reads of small values (measure first).
  • There's little benefit below ~1 KB or above a few MB. Larger blocks compress better.

9.3 Compression

Block compression is on by default (kSnappyCompression), which is fast and self-disables on incompressible data. Disable it only if benchmarks justify it:

options.compression = leveldb::kNoCompression;

Zstandard is also available where the zstd library is present:

options.compression = leveldb::kZstdCompression;
options.zstd_compression_level = 1;   // supported range [-5, 22], default 1

9.4 Block cache

Provide an explicit LRU cache to keep frequently used uncompressed blocks in memory.

#include "leveldb/cache.h"

leveldb::Options options;
options.block_cache = leveldb::NewLRUCache(100 * 1048576);  // 100 MB
leveldb::DB* db;
leveldb::DB::Open(options, name, &db);
// ... use the db ...
delete db;
delete options.block_cache;   // delete AFTER the db

If block_cache is null, LevelDB creates an internal 8 MB cache automatically. Size the cache to your application-level data sizes (it holds uncompressed data).

Tip

When performing a one-off bulk read, set ReadOptions::fill_cache = false so the scan doesn't evict your hot working set from the cache:

leveldb::ReadOptions ro;
ro.fill_cache = false;
leveldb::Iterator* it = db->NewIterator(ro);

9.5 Key layout

The unit of disk transfer and caching is a block, and adjacent keys (by sort order) usually land in the same block. Place keys accessed together near each other. For example, when building a file system on LevelDB, prefix metadata keys (/name) and block-data keys (0blockid) with different leading bytes so a metadata scan doesn't drag bulky file contents through the cache.

9.6 Other capacity knobs

Option Default Tune when
write_buffer_size 4 MB Larger speeds bulk loads but uses more memory and lengthens recovery
max_open_files 1000 Increase for large working sets (budget ~1 open file per 2 MB)
max_file_size 2 MB Increase if your filesystem prefers larger files

9.7 Checksums and integrity

LevelDB stores checksums for all data. Two controls govern verification:

leveldb::ReadOptions ro;
ro.verify_checksums = true;       // verify on every read (default: false)
leveldb::Options options;
options.paranoid_checks = true;   // fail fast on detected corruption (default: false)

Caution

With paranoid_checks = true, a single corrupted entry can make a large portion of the database — or the whole database — unreadable, because LevelDB stops at the first detected error. Leave it off if you prefer best-effort availability over fail-fast strictness.


10. Using the C API

LevelDB ships a complete C binding (leveldb/c.h) that exposes opaque struct pointers and functions. It's intended as a stable ABI for shared-library users and for foreign-function interfaces (e.g. JNI).

Conventions of the C API:

  • Errors are reported through a trailing char** errptr. On success *errptr is left unchanged; on failure it is set to a malloc-ed, null-terminated message you must free.
  • There is no Slice type — pass pointer and length as separate arguments.
  • Booleans are uint8_t (0 = false, non-zero = true).
  • All pointer arguments must be non-NULL.

A minimal open / put / get / close sequence:

#include "leveldb/c.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
  char* err = NULL;

  leveldb_options_t* options = leveldb_options_create();
  leveldb_options_set_create_if_missing(options, 1);

  leveldb_t* db = leveldb_open(options, "/tmp/c_testdb", &err);
  if (err != NULL) { fprintf(stderr, "open: %s\n", err); leveldb_free(err); return 1; }

  leveldb_writeoptions_t* woptions = leveldb_writeoptions_create();
  leveldb_put(db, woptions, "foo", 3, "hello", 5, &err);
  if (err != NULL) { fprintf(stderr, "put: %s\n", err); leveldb_free(err); return 1; }

  leveldb_readoptions_t* roptions = leveldb_readoptions_create();
  size_t vlen;
  char* val = leveldb_get(db, roptions, "foo", 3, &vlen, &err);
  if (err == NULL && val != NULL) {
    printf("foo = %.*s\n", (int)vlen, val);
    leveldb_free(val);
  }

  leveldb_writeoptions_destroy(woptions);
  leveldb_readoptions_destroy(roptions);
  leveldb_options_destroy(options);
  leveldb_close(db);
  return 0;
}
foo = hello

The C API mirrors the C++ one: write batches (leveldb_writebatch_*), iterators (leveldb_iter_*), snapshots (leveldb_create_snapshot), custom comparators (leveldb_comparator_create), filter policies (leveldb_filterpolicy_create_bloom), LRU caches (leveldb_cache_create_lru), and management calls (leveldb_destroy_db, leveldb_repair_db, leveldb_compact_range).

Important

Free every buffer the C API returns with leveldb_free (not plain free). On some platforms — notably Windows — the library and your program may use different allocators, and leveldb_free routes the deallocation correctly.

The C compression enum differs from C++: leveldb_no_compression = 0, leveldb_snappy_compression = 1.

Note

The C API does not support: option getters, custom comparators that implement key shortening, or custom iterator/DB/Env/Cache implementations written purely through the C bindings.


11. The leveldbutil Command-Line Tool

Building LevelDB also produces leveldbutil, a small command-line utility. It currently supports exactly one command.

11.1 Usage

leveldbutil command...
Usage: leveldbutil command...
   dump files...         -- dump contents of specified files

11.2 dump — print the contents of LevelDB files

leveldbutil dump /path/to/db/000005.ldb /path/to/db/MANIFEST-000002

dump prints, in text form, the contents of each named LevelDB storage file (SSTables, log files, and MANIFEST/descriptor files) to standard output. It accepts one or more file paths. If a file cannot be read or is not a recognized LevelDB file, an error message is printed to standard error and the tool exits with a non-zero status.

Note

dump operates on the individual files inside a database directory, not on the directory as a whole. Point it at specific .ldb, .log, or MANIFEST-* files. This is a diagnostic and inspection aid, not a backup or export format.

Important

dump is the only subcommand leveldbutil provides. There are no flags, no get/put/repair subcommands, and no interactive mode. To manipulate data, use the library API.


12. Troubleshooting

Errors surface as leveldb::Status values (or *errptr strings in the C API). The patterns below cover the situations you are most likely to hit.

Error: database open fails with "exists" when error_if_exists is set

Cause: Options::error_if_exists = true and the target directory already contains a database. Solution: Either remove the existing database (DestroyDB) or set error_if_exists = false to open the existing one. Prevention: Only set error_if_exists = true when you specifically intend to fail on a pre-existing database.

Error: open returns NotFound / "does not exist"

Cause: The database directory does not exist and create_if_missing is false (the default). Solution: Set options.create_if_missing = true before calling DB::Open. Prevention: Decide explicitly whether your code should create databases on demand.

Error: Corruption: ... on open or read

Cause: On-disk data failed a checksum or structural check (bad sectors, a partially written file after a power loss, or a bug). Solution: Try leveldb::RepairDB(name, options) to salvage what is recoverable, then reopen. Inspect suspect files with leveldbutil dump. Prevention: Use WriteOptions::sync = true for writes that must survive power loss; consider verify_checksums = true for early detection.

Warning

RepairDB can lose data. Take a copy of the database directory before repairing if the data is important.

Error: comparator name mismatch — open fails

Cause: The database was created with one comparator name and is being opened with a different one (Options::comparator->Name() differs from the recorded name). Solution: Open with the same comparator (and name) used to create the database. Prevention: Never rename a comparator unless you intend to abandon existing databases. Embed a version byte in keys to evolve formats without renaming. See Custom comparators.

Error: "too many open files" / IOError under heavy load

Cause: The working set requires more file descriptors than max_open_files (or the OS limit) allows. Solution: Increase options.max_open_files (budget ~1 open file per 2 MB of working set) and/or raise the OS file-descriptor limit (ulimit -n). Prevention: Size max_open_files to your dataset; modern LevelDB also caps level-0 files to mitigate this.

Error: second process cannot open the database (lock held)

Cause: A LevelDB database may be opened by only one process at a time; LevelDB holds an OS lock on the directory. Solution: Ensure no other process has the database open. Within one process, share the single DB object across threads instead of opening twice. Prevention: Architect for a single owning process; wrap your own server around LevelDB if multiple clients need access. See Limitations.

Error: recent writes lost after a power failure

Cause: Writes used the default sync = false, so the last updates were still in the OS buffer cache when power was lost. Solution: Re-run from your last checkpoint. Prevention: Use WriteOptions::sync = true for durability-critical writes, or checkpoint periodically with a synchronous marker write.

Symptom: high read latency right after many random writes

Cause: Many level-0 files accumulate during bursty writes; reads must merge across them until background compaction catches up. Solution: Allow compaction to complete, or trigger CompactRange(nullptr, nullptr). Add a block cache and a Bloom filter policy. Prevention: Provide a FilterPolicy and a generously sized block_cache; avoid sustained write bursts far above your disk's compaction throughput.

Symptom: dangling data / crash when using Slice

Cause: A Slice outlived the byte array it pointed to. Solution: Copy with Slice::ToString() when you need to retain key/value bytes beyond the current scope or the next iterator mutation. Prevention: Treat every Slice as a borrow, never as an owner. See the Slice warning.


13. Frequently Asked Questions

Q: Is LevelDB a database server I can connect to over the network? No. LevelDB is an in-process library with no built-in client-server support. If you need remote access, wrap your own server around the library.

Q: Can multiple processes share one database? No. Only a single process (which may be multi-threaded) can have a given database open at a time; LevelDB enforces this with a file lock. Within that process, the DB object is safe to share across threads.

Q: Does LevelDB support SQL, tables, or secondary indexes? No. It is a sorted key-value store only — no relational model, no SQL, no indexes. You build higher-level structures on top of it.

Q: Are my keys and values restricted to text? No. Keys and values are arbitrary byte arrays and may contain '\0'. That is why the API uses Slice (pointer + length) rather than C strings.

Q: How are keys ordered? By default, lexicographically by byte value (BytewiseComparator). Supply a custom Comparator to change the order.

Q: Is compression required? What if Snappy isn't installed? Compression is optional and on by default. If the Snappy library isn't present at build time, blocks are stored uncompressed and the default compression setting is simply a no-op.

Q: How do I make a write durable against power loss? Set WriteOptions::sync = true. By default writes are asynchronous and a reboot may lose the most recent updates (a mere process crash will not).

Q: How do I take a consistent backup? Snapshots give a consistent read-only view for live reads/iteration. For a file backup, ensure writes are quiesced or use a snapshot-aware copy of the directory; LevelDB's design (immutable SSTables) makes copying the directory feasible. See the Admin Guide for operational backup guidance.

Q: How big can a LevelDB database get? LevelDB has been tested with very large datasets (on the order of a billion ~100-byte entries, ~100 GB). Practical limits depend on your hardware, max_open_files, and workload.

Q: How do I delete an entire database? Call leveldb::DestroyDB(name, options) on a closed database. To recover an un-openable one, try leveldb::RepairDB.

Q: Why is there no Close() method? You close the database by delete-ing the DB* handle. Delete all iterators and release all snapshots first.

Q: What is the current version? This documentation reflects LevelDB 1.23 (kMajorVersion = 1, kMinorVersion = 23).

Q: Which headers am I allowed to include? Only those under include/leveldb/. Everything else is internal and may change without notice.

Q: Does leveldbutil let me read or edit values from the shell? Only indirectly. leveldbutil dump prints the textual contents of LevelDB files; there is no command-line get/put. Use the library API to manipulate data.


14. Reference

14.1 Options fields

Defaults as defined in include/leveldb/options.h.

Field Type Default Description
comparator const Comparator* BytewiseComparator() Key ordering. Must match the DB's recorded comparator.
create_if_missing bool false Create the DB if it doesn't exist.
error_if_exists bool false Fail to open if the DB already exists.
paranoid_checks bool false Stop early on any detected corruption.
env Env* Env::Default() OS abstraction for file/thread operations.
info_log Logger* nullptr Where progress/error logs go (null = a file in the DB dir).
write_buffer_size size_t 4 * 1024 * 1024 In-memory buffer before flushing to an SSTable.
max_open_files int 1000 Max files the DB may keep open.
block_cache Cache* nullptr Uncompressed-block cache (null = internal 8 MB cache).
block_size size_t 4 * 1024 Approx uncompressed bytes per block.
block_restart_interval int 16 Keys between restart points for key delta-encoding.
max_file_size size_t 2 * 1024 * 1024 Max bytes per table file.
compression CompressionType kSnappyCompression Block compression algorithm.
zstd_compression_level int 1 Zstd level, range [-5, 22].
reuse_logs bool false (Experimental) append to existing MANIFEST/log on open for faster startup.
filter_policy const FilterPolicy* nullptr Optional filter (e.g. Bloom) to cut disk reads.

14.2 ReadOptions fields

Field Type Default Description
verify_checksums bool false Verify checksums of all data read.
fill_cache bool true Cache the data read by this operation.
snapshot const Snapshot* nullptr Read as of this snapshot (null = implicit current).

14.3 WriteOptions fields

Field Type Default Description
sync bool false Flush to persistent storage before returning.

14.4 Status codes

Predicate Meaning
ok() Success
IsNotFound() Key not present
IsCorruption() Data corruption detected
IsIOError() Underlying I/O failure
IsNotSupportedError() Operation not supported
IsInvalidArgument() Bad argument

Use Status::ToString() for a printable description.

14.5 DB methods

Method Purpose
static Status Open(options, name, &db) Open or create a database.
Status Put(wopts, key, value) Insert/overwrite an entry.
Status Delete(wopts, key) Remove an entry (OK if absent).
Status Write(wopts, batch) Apply a WriteBatch atomically.
Status Get(ropts, key, &value) Look up a key.
Iterator* NewIterator(ropts) Create a sorted iterator (caller deletes).
const Snapshot* GetSnapshot() Take a consistent read snapshot.
void ReleaseSnapshot(snap) Release a snapshot.
bool GetProperty(name, &value) Read an internal property.
void GetApproximateSizes(ranges, n, sizes) Estimate on-disk size of key ranges.
void CompactRange(begin, end) Force compaction of a key range.

Free functions: DestroyDB(name, options), RepairDB(name, options).

14.6 Iterator methods

Method Purpose
bool Valid() Is the iterator positioned at an entry?
void SeekToFirst() Position at the first key.
void SeekToLast() Position at the last key.
void Seek(target) Position at the first key ≥ target.
void Next() Advance forward (requires Valid()).
void Prev() Move backward (requires Valid()).
Slice key() Current key (valid until next mutation).
Slice value() Current value (valid until next mutation).
Status status() Error status encountered during iteration.

14.7 Public headers

c.h, cache.h, comparator.h, db.h, dumpfile.h, env.h, export.h, filter_policy.h, iterator.h, options.h, slice.h, status.h, table.h, table_builder.h, write_batch.h. Start with db.h.

Limitations

  • Not a SQL database — no relational model, no SQL queries, no indexes.
  • Single process — only one (possibly multi-threaded) process may access a database at a time.
  • No client-server — no built-in network access; wrap your own server if needed.