SD System Design In Depth
0%
lesson system-designstorage-engines

Custom binary file format

Module 2.3: Storage Engine Internals & Blob Systems Updated Sep 17, 2026
📄 Cheat Sheet
How databases store data on disk?
Architecture Weekly How databases store data on disk? Watch on YouTube ↗

Oskar Dudycz explains how databases lay records out on disk (pages, headers, offsets) in a compact, concrete way that matches this unit's focus on binary layout.

0:00 / --:--

Under the Hood: Binary Serialization & Disk Layout

Text formats such as JSON and CSV favor inspectability and interoperability, but parsing and locating individual records can cost more CPU and I/O than a purpose-built representation. When measurements justify that tradeoff, a storage engine can use a versioned binary format with explicit record boundaries and validation.

Physical Record Binary Memory Layout

flowchart LR CRC["CRC32 Checksum (4 Bytes)"] --> TS["Timestamp (8 Bytes)"] TS --> KLen["Key Length (2 Bytes)"] KLen --> VLen["Value Length (4 Bytes)"] VLen --> Flags["Flags / Tombstone (1 Byte)"] Flags --> KeyPayload["Key Bytes (Variable: KLen)"] KeyPayload --> ValPayload["Value Bytes (Variable: VLen)"]

Header Specification & Hardware Alignment

1. File Header (Magic Bytes & Metadata)

One possible format starts with a fixed-size header block:

  • Magic Bytes (4 Bytes): A unique signature (e.g. 0x53444231 for "SDB1") verifying that the file is indeed a valid database file.
  • Format Version (2 Bytes): Allows future database upgrades to read older file formats or migrate them on the fly.
  • Block Size (2 Bytes): Defines page size alignment (e.g. 4096 bytes or 16384 bytes).
  • Endianness Marker (2 Bytes): Resolves Little-Endian vs Big-Endian integer encoding differences across CPU architectures.

2. The 4KB Memory Alignment Rule

Filesystems and devices expose different logical blocks, physical sectors, pages, and direct-I/O alignment constraints. Packing records into blocks can bound the number of reads and simplify checksums, but padding every record may waste space. Choose and record the block size from the actual I/O contract rather than assuming a universal 4KB rule.

Worked example: one record, byte by byte

A simple length-prefixed record format (this is essentially Bitcask's layout):

FieldSizeExample value
CRC32C over the rest of the record4 Bcomputed last
Timestamp (ms, little-endian)8 B1,790,000,000,000
Key length4 B7
Value length4 B100
Key bytes7 Buser:42
Value bytes100 Bpayload

Total 127 bytes, with 20 bytes of fixed header. To read the record at offset 4096, read 20 bytes, check that 4096 + 20 + 7 + 100 does not exceed the file size, read the remaining 107 bytes, recompute the CRC and compare. If a crash truncated the file mid-record, the length check or CRC fails and recovery truncates the file back to 4096.

Choosing a representation

FormatSeek to record NParse costSchema evolutionDebuggability
JSON linesScan, or keep a side indexHigh (text parsing)Easy, looseExcellent
Length-prefixed binary with CRCOffset index gives one readVery lowNeeds a version fieldNeeds a dump tool
Protobuf or FlatBuffers recordsNeeds framing plus indexLow (FlatBuffers near zero-copy)Strong field-number rulesTooling available
Fixed-width slotted pagesPage id times page sizeVery lowHarderNeeds tooling

How real formats do it

  • SQLite: a 100-byte file header beginning with the string SQLite format 3, including page size and format version numbers.
  • LevelDB/RocksDB WAL: 32 KB blocks; each record fragment has a 7-byte header (checksum, length, type) with types FULL, FIRST, MIDDLE and LAST so records can span blocks and a torn block is detectable.
  • Parquet: the magic PAR1 at both ends, with the metadata footer at the end so a writer can stream data first and a reader can find the footer with one small read from the tail.
Check yourself Question 1 of 3
Why a version field recall
From: Header Specification & Hardware Alignment

Which file header field lets a newer build of the engine read, or migrate, files written by an older build without guessing their layout?

Key Takeaways

Custom binary formats avoid text-parsing CPU overhead, and combined with an offset index or fixed-size slots they enable single-read random access by offset.
A binary format should include only the boundaries, versioning, integrity checks, and mutation metadata required by its recovery and compatibility contract.
Block sizing and alignment should follow measured filesystem and device constraints; padding trades simpler I/O for extra space.
Was this lesson helpful? Spot an issue or have a suggestion? Let us know on GitHub.
Report an issue