# How KDB stores data > A short tour of the files in a KDB database folder - data files, blocks and checksums, the log, the manifest and backups. - Category: Data formats - Keywords: file format, on-disk format, files, folder, kdb.lock, \_manifest.a, \_manifest.b, manifest, wal, wal\_archive, \_trash, WAL, write-ahead log, log segment, LSN, checkpoint, block, CRC32C, checksum, digest, copy-on-write, version, partition file, column file, backup.kdb, .partial, timeline, FORMAT.md, little-endian, manifest keys, removed manifest keys, column stats - See also: [Backup, check, repair and restore](backup-and-safety.md), [kdb info, check, repair, dump-file](cli-maintenance.md), [Partitions, SORT BY and DEDUP KEY](partitions-and-dedup.md), [Limits (disk-memory)](limits.md#disk-memory) This page explains, in simple words, what KDB writes to the disk and why. You do not need it to use KDB. It helps to understand `kdb info`, `kdb check` and the messages about damage. The exact bytes, for developers, are in docs/FORMAT.md. When that file and the code disagree, the code is right. **Do not change or delete files in a database folder by hand.** Use `kdb` commands. To copy a database, stop every program that uses it, or better use [kdb backup](backup-and-safety.md#kdb-backup). The format is the same on Windows, Linux and macOS, so a database folder can move between them. ## The database folder Syntax: ```sql / kdb.lock locked while a program has the database open _manifest.a _manifest.b the list of current files, saved twice files/ data files: files/.v.kdb wal/ the write-ahead log (the newest changes) wal_archive/ older log files, kept for backups and repairs _trash/ files taken out of use, deleted after 24 hours ``` A database is one folder. In a data folder, each database is a sub-folder, plus `_trash` (dropped databases) and `_system` (the server's users). | Name | What it is | |---|---| | `kdb.lock` | An empty file. While a program has the database open, the operating system **locks** it, so a second program gets `the database is open in another program`. Do not delete it: when no program runs, the lock is free anyway. | | `_manifest.a`, `_manifest.b` | The **manifest**: the list of the current data files, their versions, sizes and checksums, and the log positions. Two copies (see [manifest](#manifest)). | | `files/` | The **data files**: `files/catalog.v6.kdb` (the list of tables and columns) and `files/t//.v.kdb` (the rows of one partition). A table [split by symbol](partitions-and-dedup.md#partition-by-symbol) adds the symbol to the partition name, as hex: `2026-09/s-43495459` is month `2026-09`, symbol `CITY`. | | `wal/` | The **write-ahead log** (WAL): every change is written here first (see [the log](#wal)). | | `wal_archive/` | Old log files that the data files no longer need. Only backups and repairs use them. The oldest are deleted when this folder is over its limit: 1 GB by default, changed with [ALTER DATABASE ... SET WAL_ARCHIVE](databases.md#wal-archive) or on the [Settings page](console-settings.md#wal-archive). | | `_trash/` | Files taken out of use: old versions, damaged files moved away by a repair, unknown files. Folders older than 24 hours are deleted when the database opens. | **Command line: kdb info lists what the manifest says** (sample data: sample) ```bash kdb info market ``` Output: ```text database market manifest copy a, sequence 9 last shutdown clean log position 30 (saved in files up to 30) newest change 2026-10-22 11:28:27 features batched_log_sync, block_stats, compressed_log, partition_format_2 manifest keys 26 FILE VERSION SIZE LSN STATUS catalog 6 2.1 KB 30 ok t/1/all 7 777 B 30 ok t/2/all 8 556 B 30 ok t/3/2025 9 1.7 KB 30 ok t/3/2026 10 3.6 KB 30 ok t/4/2026-09 11 57.0 KB 30 ok t/5/2026-09-09 12 1.4 KB 30 ok t/5/2026-09-10 13 2.4 KB 30 ok t/6/2026-09-10 14 2.7 KB 30 ok 9 files, 72.1 KB, 0 marked damaged (STATUS is what the manifest records. To read every block and find new damage, run: kdb check) ``` `t/4/2026-09` is table number 4 (tables are numbered in the order they were made; here `ochl_minutes`), partition `2026-09`. `all` is a table without partitions. `VERSION 11` is the file `files/t/4/2026-09.v11.kdb`. ## Data files are never changed Syntax: ```sql files/t/
/.v.kdb ``` A data file is written **once** and then never changed (this is called copy-on-write). 1. Changes first go into the log and into memory. 2. From time to time (a **checkpoint**), KDB writes a **new version** of each changed partition: a new file with the next version number. 3. KDB reads the new file back and checks it. 4. Only then it saves the manifest, which now names the new version. - A crash at any moment leaves the old version complete. There is never a half-changed file. - KDB keeps the **2 previous versions** of every file, for the repair ladder. Older ones go to `_trash`. - A checkpoint happens after about 1 million changes, every 5 minutes, when the changes use a quarter of `--memory`, about 2 seconds after the last write when at least 10,000 changes wait, when a database closes, before a backup or a schema change, and when you ask for one with [CHECKPOINT](backup-and-safety.md#checkpoint). On a busy database a write may leave its checkpoint to a background thread, but only until the changes use half of `--memory`, or for at most 5 seconds; after that the next write waits for the checkpoint, so unsaved changes cannot grow without limit. Rows a load (`POST /write`, `POST /imp`, `COPY`) keeps in column form count as 1/8 of a change, so a bulk load checkpoints about every 8 million rows (or at a quarter of `--memory`, whichever comes first) instead of every million: each checkpoint writes every partition it touched whole, and on a big load into a few partitions most of those bytes were rows already on disk (since 2026-10-02). - One partition file holds all columns of that partition, column after column, with the number of rows, the smallest and biggest value and the sum of each column (the **column stats**). - KDB uses the column stats without reading the rows: a `WHERE` skips a partition in memory (or a block of 65,536 rows in it) whose values cannot match, and `count`, `min`, `max` and `sum` of a partition that is not in memory are answered from its file's stats, without loading the rows. [EXPLAIN](explain.md) shows both. This needed no change of the files: partition files have had the stats since 2026-09-12. **Command line: One data file: the table securities** (sample data: sample) ```bash kdb dump-file market/files/t/1/all.v7.kdb ``` Output: ```text file market/files/t/1/all.v7.kdb type data file version 1 data 693 B block size 65536 LSN 30 id 1 created 2026-10-22 11:28:27 blocks 1 digest 1d198644 OK: all blocks match their checksums ``` ## Blocks and CRC32C checksums Syntax: ```sql [ header 64 bytes ][ block 1 ][ block 2 ] ... [ checksum table ][ trailer 16 bytes ] ``` Every KDB file (data files, manifests, `backup.kdb`) is cut into **blocks** of 64 KB. Each block has a **CRC32C checksum**: a 4-byte number made from the bytes of the block. - When KDB reads a block, it computes the number again. If one bit on the disk has changed, the numbers differ, and KDB says `block N (bytes ...) failed its checksum`. It **never** returns data from a bad block. - The **header** (the first 64 bytes) says what the file is: its type, the format version, the block size, the data length, the log position, and when it was made. It has its own checksum. - The **trailer** at the end holds the number of blocks and a checksum of the checksum table, so a file that was cut short is noticed. - The **digest** is one checksum over the header and all block checksums. The manifest keeps it for each file, so KDB also notices a whole file that was replaced by another one. - The header is written **last**. A file that a crash interrupted has no valid header and is never used. - `crc32c hardware: yes` in `kdb version` means the CPU computes these checksums with fast special instructions. **Command line: Check every block of one file** (sample data: sample) ```bash kdb dump-file market/_manifest.a ``` Output: ```text file market/_manifest.a type manifest version 1 data 2.3 KB block size 65536 LSN 30 id 9 created 2026-10-22 11:28:27 blocks 1 digest 35a9d31d OK: all blocks match their checksums ``` ## The write-ahead log (WAL) Syntax: ```sql wal/00000000000000000001.log a log file (segment), named after its first LSN record: length | checksum | LSN | transaction id | type (data or commit) | data ``` Every change is first written to the **log**, and the log is on the disk before KDB says a statement is done. - Each record in the log gets the next number: the **LSN** (log sequence number). `kdb info` shows `log position` = the newest LSN. - A transaction is its data records plus one **commit** record. The whole transaction is written in one piece and saved with **one** disk sync. Without its commit record, a transaction does not count. - Every record has its own checksum. - A log file (a **segment**) grows to 64 MB; then a new one starts. Log files that the data files no longer need move to `wal_archive/`. - **After a crash**, KDB reads the log from the position the data files have, and replays every committed transaction. `kdb info` then shows `last shutdown: not clean`, which is normal. - **A torn tail:** a power cut can leave the last, unconfirmed transaction half written at the end of the log. KDB notices it and cuts it off (`dropped an unfinished write at the end of the log`). Nothing confirmed is lost. - **Damage in the middle** of the log, in records that are still needed, is refused with a clear message. See [after damage](backup-and-safety.md#after-damage). - With `--sync-commits batch`, several transactions share one disk sync every 10 ms. ## The manifest: two copies Syntax: ```sql _manifest.a _manifest.b the newer valid copy (highest sequence number) wins ``` The manifest is the list of what is current: every data file with its version, size, digest and log position, the previous versions, which files are marked damaged, and whether the last shutdown was clean. - It is saved in two files. A save always writes over the **older** copy. So if a crash happens during a save, the other copy is still complete. - When opening, KDB reads both and uses the valid copy with the highest **sequence** number. `kdb info` shows `manifest copy b, sequence 12`. - If one copy is damaged, KDB notes it and writes over it at the next save. - It is saved at every checkpoint, after a recovery, and when the database closes (then with `clean shutdown`). ### Manifest keys Besides the file list, the manifest keeps small **keys** for the tables: | Key | What it holds | |---|---| | `rows.` | the row count of a partition file | | `sym.` | which symbols a partition file holds, so a `WHERE symbol = ...` skips files without opening them | | `rowid.
`, `auto.
` | the next row id and the next `AUTO` value of a table | - Every checkpoint rewrites the manifest with all its keys. Since 2026-09-23 the keys of a dropped table (and of partition files that are gone) are removed at the next checkpoint, and once when a database is opened for writing. Older builds left them behind for ever. - The open then says `removed N manifest keys left behind by dropped tables`. `kdb info` shows `manifest keys N`, and how many of them belong to files that are gone. - This is no change of the format: an older KDB opens such a manifest as before. **Command line: Which copy is newest** (sample data: sample) ```bash kdb info market kdb dump-file market/_manifest.b ``` Output: `> kdb info market` ```text database market manifest copy a, sequence 9 last shutdown clean log position 30 (saved in files up to 30) newest change 2026-10-22 11:28:27 features batched_log_sync, block_stats, compressed_log, partition_format_2 manifest keys 26 FILE VERSION SIZE LSN STATUS catalog 6 2.1 KB 30 ok t/1/all 7 777 B 30 ok t/2/all 8 556 B 30 ok t/3/2025 9 1.7 KB 30 ok t/3/2026 10 3.6 KB 30 ok t/4/2026-09 11 57.0 KB 30 ok t/5/2026-09-09 12 1.4 KB 30 ok t/5/2026-09-10 13 2.4 KB 30 ok t/6/2026-09-10 14 2.7 KB 30 ok 9 files, 72.1 KB, 0 marked damaged (STATUS is what the manifest records. To read every block and find new damage, run: kdb check) ``` `> kdb dump-file market/_manifest.b` ```text file market/_manifest.b type manifest version 1 data 2.3 KB block size 65536 LSN 30 id 8 created 2026-10-22 11:28:27 blocks 1 digest fe6eb549 OK: all blocks match their checksums ``` In the header of a manifest, `id` is its sequence number. The copy that `kdb info` names has the higher one; the other copy is one save older. ## Backup folders Syntax: ```sql / 20260911-161530-123/ a backup; the name is the UTC time backup.kdb the list of the backup, written last files/.v.kdb the current version of every data file wal/.log the log files needed to replay from those files 20260912-...-.partial/ an unfinished backup: never used, removed later ``` - A backup holds only the **current** version of each file, plus the log files needed. With `--hard-links`, the data files are hard links; log files are always copied. - `backup.kdb` is a KDB block file too (with checksums). It is written **last**, after every file was copied and synced. Then the whole backup is read again. Only then the folder gets its final name (without `.partial`). - A backup knows which database it came from (a random id made when the database was created) and its log history. Repairs and `--logs-from` never use a backup or log of another database. - **Restore** copies the files into a new folder, checks every copy, adds newer log files from `--logs-from`, cuts the log at `--until`, and writes a new manifest. The log is replayed when the new database is first opened. The restored database starts a new log **history** (timeline), so old log files of the abandoned history are never mixed in. **Command line: A backup, and its list** (sample data: sample) ```bash kdb backup market backups kdb backups backups ``` Output: `> kdb backup market backups` ```text backup created and verified: backups/20261022-054330-179 9 files, 190.0 KB, log up to 30 ``` `> kdb backups backups` ```text BACKUP CREATED FILES SIZE LOG LSN 20261022-054330-179 2026-10-22 11:28:30 9 190.0 KB 30 1 backups ``` ## Indexes are not stored Syntax: ```sql CREATE INDEX ... only the definition is saved (in the catalog) ``` For `CREATE INDEX`, KDB saves only the **definition** in the catalog. The index itself lives in memory. It is built from the rows after the database opens: `kdb serve` builds every index on a background thread right after the open, and a query that needs one before that builds it itself. - So `kdb check`, `kdb repair`, backups and restores have nothing to check for indexes, and an index can never be old or damaged after a crash. - The cost: after a start, building a big index takes a while. A query that needs it early waits for it (see [indexes](indexes.md#warm)). **Example: An index on a sample table: only its definition is saved** (sample data: sample) ```sql CREATE INDEX trade_log_buyer ON trade_log (buyer); SHOW INDEXES; ``` Result: `CREATE INDEX trade_log_buyer ON trade_log (buyer)` ```text index 'trade_log_buyer' created on table 'trade_log' (32 rows) ``` `SHOW INDEXES` | name (STRING) | table (STRING) | kind (STRING) | columns (STRING) | built (BOOL) | rows (LONG) | memory_bytes (LONG) | create_sql (STRING) | building (BOOL) | |---|---|---|---|---|---|---|---|---| | trade_log_buyer | trade_log | INDEX | buyer | true | 32 | 1777 | CREATE INDEX trade_log_buyer ON trade_log (buyer) | false | (1 row)