📄Das Dateiformat

Eine SQLite-Datenbank ist genau eine Datei – und ihr Format ist offen dokumentiert, stabil seit 2004 und wird laut sqlite.org mindestens bis 2050 unterstützt. Alle Angaben auf dieser Seite folgen sqlite.org/fileformat.html; die Beispielwerte stammen aus der echten, beim Build erzeugten Beispiel-Datenbank.

🧱Die Datei besteht aus Seiten

Die Datei ist in gleich große Seiten eingeteilt, nummeriert ab 1. Jede Seite hat genau eine Aufgabe: B-Baum-Seite einer Tabelle oder eines Index, Overflow-Seite, Freelist-Seite, Pointer-Map-Seite (nur bei Auto-Vacuum) oder die nie benutzte Lock-Byte-Seite bei 1 GiB.
Beispiel-Datenbank wird geladen …

🪪Der 100-Byte-Datenbankkopf

Die ersten 100 Bytes von Seite 1. Alle Zahlen sind Big-Endian. Hier steht u. a., wie groß eine Seite ist, wie viele Seiten die Datei hat und in welcher Textkodierung Strings gespeichert sind.
Beispiel-Datenbank wird geladen …

🌳B-Baum-Seiten

Jede Tabelle und jeder Index ist ein B-Baum. Tabellen verwenden B+-Bäume (Daten nur in den Blättern, Schlüssel = Rowid), Indizes klassische B-Bäume (Schlüssel auch in inneren Seiten, kein Datenteil).

Aufbau einer Seite

Offset 0                                              Seitenende
┌───────────┬─────────────┬───────────────┬──────────────────┬──────────┐
│ Dateikopf │ Seitenkopf  │ Zellzeiger-   │   unbenutzt      │  Zellen  │
│ (100 B,   │ 8 B Blatt / │ Array: je 2 B │      ◄── wächst ──│ ◄── wach-│
│ nur S. 1) │ 12 B innen  │ ─► wächst ─►  │                  │  sen     │
└───────────┴─────────────┴───────────────┴──────────────────┴──────────┘
                                          ▲                  ▲
                                   Ende Zeiger-Array   Kopf-Offset 5:
                                                       Beginn Zellinhalt

• Zellzeiger sind nach Schlüssel sortiert, die Zellen selbst nicht.
• Gelöschte Zellen werden zu Freeblocks (Kette ab Kopf-Offset 1);
  Lücken unter 4 Bytes zählen als „fragmentierte Bytes“ (Offset 7).

Seitenkopf

OffsetGrößeInhalt
01Seitentyp (0x02, 0x05, 0x0a, 0x0d)
12Beginn des ersten Freeblocks (0 = keiner)
32Anzahl Zellen auf der Seite
52Beginn des Zellinhaltsbereichs (0 bedeutet 65 536)
71Anzahl fragmentierter freier Bytes
84rechtester Kindzeiger – nur bei inneren Seiten
Typ-Bytedez.SeitentypKopfAufbau einer Zelle
0x022Index – innere Seite12 Blinker Kindzeiger (4 B) · Payload-Größe (Varint) · Schlüssel-Payload · ggf. Overflow-Zeiger
0x055Tabelle – innere Seite12 Blinker Kindzeiger (4 B) · Rowid-Schlüssel (Varint) – keine Daten!
0x0a10Index – Blattseite8 BPayload-Größe (Varint) · Schlüssel-Payload · ggf. Overflow-Zeiger
0x0d13Tabelle – Blattseite8 BPayload-Größe (Varint) · Rowid (Varint) · Record · ggf. Overflow-Zeiger
💡 Warum 0x02, 0x05, 0x0a, 0x0d?
Die Bits haben Bedeutung (btreeInt.h): Wert 1 = „intKey“ (Rowid-Schlüssel), Wert 2 = „zeroData“ (kein Datenteil, Index), Wert 4 = „leafData“ (Daten nur in Blättern), Wert 8 = Blatt. 0x0d = 8+4+1 ist also ein Tabellen-Blatt, 0x05 = 4+1 eine innere Tabellenseite, 0x0a = 8+2 ein Index-Blatt, 0x02 eine innere Indexseite.
✅ Wie findet SQLite Rowid 42?
Von der Wurzelseite aus: Zellzeiger per Binärsuche durchgehen, der erste Schlüssel ≥ 42 liefert den linken Kindzeiger – sonst der rechteste Zeiger aus dem Kopf. So bis zur Blattseite. Bei 1024-Byte-Seiten reichen zwei Ebenen für die 120 Kunden der Beispiel-DB.

🔢Varints

Längen, Rowids und Serial Types stehen als Varint in der Datei: kleine Zahlen brauchen 1 Byte, große bis zu 9. Achtung, das ist nicht das Protobuf-/LEB128-Format – SQLite ist Big-Endian und nutzt beim 9. Byte alle 8 Bit.

Zahl → Varint

82
10000010
geht weiter
2c
00101100
letztes

2 Bytes. Rotes Bit = „es folgt ein Byte“, die übrigen 7 Bit sind Nutzdaten (höherwertige zuerst).

Hex → Zahl

144115188075855872 (9 Bytes gelesen)

BytesWertebereich
10 … 2^7 − 1 = 127
20 … 2^14 − 1 = 16.383
30 … 2^21 − 1 = 2.097.151
40 … 2^28 − 1 = 268.435.455
50 … 2^35 − 1 = 34.359.738.367
60 … 2^42 − 1 = 4.398.046.511.103
70 … 2^49 − 1 = 562.949.953.421.311
80 … 2^56 − 1 = 72.057.594.037.927.935
9alle 64-Bit-Werte, auch negative

🧾Das Record-Format

Eine Tabellenzeile oder ein Index-Eintrag ist ein Record: zuerst ein Header mit seiner eigenen Länge und je einem Serial Type pro Spalte, danach die Werte ohne Trennzeichen. Probier eigene Werte:
0800211b09020710416e6e61204c696e6465536565646f72667530400a000000000000cafe

Unterstrichen = Record-Header (8 Bytes), danach der Body. Gleiche Farbe = Serial Type und Inhalt derselben Spalte.

SpalteWertSerial TypeBedeutungInhalt
0NULL0NULL0 B
1'Anna Linde'33TEXT, 10 Bytes10 B
2'Seedorf'27TEXT, 7 Bytes7 B
319Ganzzahl 1 (ohne Inhalt)0 B
4300002Ganzzahl 16 Bit2 B
53.257Gleitkomma 64 Bit (IEEE 754)8 B
6x'cafe'16BLOB, 2 Bytes2 B

Tipp: In einer Tabelle mit INTEGER PRIMARY KEY steht an dieser Stelle NULL (Serial Type 0) – der Wert ist die Rowid und steht schon im Zellkopf. Ganzzahlen 0 und 1 kosten dank Serial Type 8/9 gar kein Byte im Body.

Serial TypeInhaltslänge (Bytes)Bedeutung
00NULL
11Ganzzahl, 8 Bit, Zweierkomplement
22Ganzzahl, 16 Bit, Big-Endian
33Ganzzahl, 24 Bit
44Ganzzahl, 32 Bit
56Ganzzahl, 48 Bit
68Ganzzahl, 64 Bit
78Gleitkommazahl IEEE 754, 64 Bit, Big-Endian
80die Ganzzahl 0 (ab Schemaformat 4)
90die Ganzzahl 1 (ab Schemaformat 4)
10, 11–reserviert für interne Zwecke
N ≥ 12, gerade(N − 12) / 2BLOB
N ≥ 13, ungerade(N − 13) / 2TEXT in der Kodierung der Datenbank (ohne Nullbyte)

Beispiel: TEXT 'Seedorf' hat 7 Bytes → Serial Type 7·2+13 = 27 = 0x1b. REAL-Spalten speichern ganzzahlige Werte intern platzsparend als Ganzzahl; beim Lesen macht der Opcode RealAffinity wieder eine Gleitkommazahl daraus.

🧵Overflow-Seiten

Ist die Payload einer Zelle zu groß, bleibt nur ein Anfangsstück auf der B-Baum-Seite, der Rest liegt in einer verketteten Liste von Overflow-Seiten. Jede Overflow-Seite beginnt mit 4 Bytes „nächste Seite“ (0 = letzte).
X = U − 35 = 989
M = (U−12)·32/255 − 23 = 103
K = M + (P−M) mod (U−4) = 960
lokal: 960 Bytes
in der Zelle
OF 1
OF 2

P > X: 960 Bytes bleiben in der Zelle (K ≤ X → K), 2.040 Bytes verteilen sich auf 2 Overflow-Seite(n) mit je 1020 Nutzbytes (4 Bytes je Seite für den Zeiger auf die nächste). Die Wahl von K sorgt dafür, dass die letzte Overflow-Seite möglichst voll ist.

In der Beispiel-DB ist der Record der Notiz „Lang“ 2 861 Bytes groß. Bei U = 1024 gilt X = 989, M = 103 und K = 103 + (2 758 mod 1020) = 821 ≤ X: 821 Bytes bleiben in der Zelle, die restlichen 2 040 Bytes füllen genau zwei Overflow-Seiten à 1020 Nutzbytes.

🗑️Freelist

Seiten, die nicht mehr gebraucht werden (z. B. nach DROP TABLE oder DELETE), kommen auf die Freelist. Sie ist eine Kette von Trunk-Seiten; jede listet bis zu (U/4 − 2) Blattseiten.
Beispiel-Datenbank wird geladen …

📚sqlite_schema – das Inhaltsverzeichnis

Die Tabelle auf Seite 1 kennt jede Tabelle, jeden Index, jede View und jeden Trigger – mit Wurzelseite und dem originalen CREATE-Befehl. Früher hieß sie sqlite_master (der Name funktioniert weiterhin).
Beispiel-Datenbank wird geladen …