<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Database Internals on Software Engineering Learning Paths</title><link>https://kyngo.github.io/learning-paths/paths/database-internals/</link><description>Recent content in Database Internals on Software Engineering Learning Paths</description><generator>Hugo</generator><language>en</language><atom:link href="https://kyngo.github.io/learning-paths/paths/database-internals/index.xml" rel="self" type="application/rss+xml"/><item><title>Storage Fundamentals</title><link>https://kyngo.github.io/learning-paths/paths/database-internals/01-storage-fundamentals/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://kyngo.github.io/learning-paths/paths/database-internals/01-storage-fundamentals/</guid><description>&lt;h2 id="why-storage-matters"&gt;Why Storage Matters&lt;a class="anchor" href="#why-storage-matters"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Every database operation — reads, writes, joins, index lookups — ultimately translates into I/O against a storage device. Understanding how data is physically organised determines whether a query takes microseconds or minutes.&lt;/p&gt;
&lt;p&gt;This section covers the lowest layer of a database: how bytes are arranged on disk, how the database divides storage into fixed-size pages, and how individual rows are packed into those pages.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="disk-vs-ssd"&gt;Disk vs SSD&lt;a class="anchor" href="#disk-vs-ssd"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="mechanical-hard-drives-hdd"&gt;Mechanical Hard Drives (HDD)&lt;a class="anchor" href="#mechanical-hard-drives-hdd"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;HDDs store data on spinning magnetic platters read by a moving head. The cost of an I/O operation is dominated by &lt;strong&gt;seek time&lt;/strong&gt; (moving the head to the right track) and &lt;strong&gt;rotational latency&lt;/strong&gt; (waiting for the platter to spin to the right sector).&lt;/p&gt;</description></item><item><title>B-Trees &amp; B+Trees</title><link>https://kyngo.github.io/learning-paths/paths/database-internals/02-btrees/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://kyngo.github.io/learning-paths/paths/database-internals/02-btrees/</guid><description>&lt;h2 id="the-index-problem"&gt;The Index Problem&lt;a class="anchor" href="#the-index-problem"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Given a heap file with millions of rows, finding a specific row without an index requires scanning every page — a &lt;strong&gt;full table scan&lt;/strong&gt;. B-trees and B+trees solve this by maintaining a sorted, balanced tree structure that narrows the search to a handful of page reads.&lt;/p&gt;
&lt;p&gt;The Databases &amp;amp; SQL path covered &lt;em&gt;when&lt;/em&gt; to use indexes. This section covers &lt;em&gt;how&lt;/em&gt; they work internally.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="b-tree-fundamentals"&gt;B-Tree Fundamentals&lt;a class="anchor" href="#b-tree-fundamentals"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A &lt;strong&gt;B-tree&lt;/strong&gt; (Bayer &amp;amp; McCreight, 1972) is a self-balancing tree where:&lt;/p&gt;</description></item><item><title>LSM Trees &amp; Log-Structured Storage</title><link>https://kyngo.github.io/learning-paths/paths/database-internals/03-lsm-trees/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://kyngo.github.io/learning-paths/paths/database-internals/03-lsm-trees/</guid><description>&lt;h2 id="the-write-optimisation-problem"&gt;The Write Optimisation Problem&lt;a class="anchor" href="#the-write-optimisation-problem"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;B+trees provide excellent read performance, but writes are expensive: inserting a single key may require reading a leaf page, modifying it, and writing it back — all random I/O. For write-heavy workloads (time-series, event logs, messaging), this becomes a bottleneck.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Log-Structured Merge-trees (LSM-trees)&lt;/strong&gt; flip the trade-off: absorb all writes in memory, then flush sorted runs to disk sequentially. Reads become more expensive, but writes are dramatically cheaper.&lt;/p&gt;</description></item><item><title>Write-Ahead Logging</title><link>https://kyngo.github.io/learning-paths/paths/database-internals/04-write-ahead-logging/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://kyngo.github.io/learning-paths/paths/database-internals/04-write-ahead-logging/</guid><description>&lt;h2 id="the-durability-problem"&gt;The Durability Problem&lt;a class="anchor" href="#the-durability-problem"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A database crash can happen at any moment — power failure, kernel panic, hardware fault. If modified pages exist only in the buffer pool (memory), they are lost. Writing every modified page to disk immediately would be safe but catastrophically slow (random I/O for every change).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Write-Ahead Logging (WAL)&lt;/strong&gt; solves this: before modifying any data page, write a log record describing the change to a sequential log file. If the system crashes, replay the log to reconstruct any modifications that were not yet flushed to the data files.&lt;/p&gt;</description></item><item><title>Buffer Pool Management</title><link>https://kyngo.github.io/learning-paths/paths/database-internals/05-buffer-pool/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://kyngo.github.io/learning-paths/paths/database-internals/05-buffer-pool/</guid><description>&lt;h2 id="the-memorydisk-gap"&gt;The Memory–Disk Gap&lt;a class="anchor" href="#the-memorydisk-gap"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Disk I/O (even on SSDs) is orders of magnitude slower than memory access. The &lt;strong&gt;buffer pool&lt;/strong&gt; bridges this gap by caching frequently accessed disk pages in memory. A well-tuned buffer pool means most reads never touch disk.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Access latency comparison:
 L1 cache: ~1 ns
 DRAM: ~100 ns
 SSD random: ~50,000 ns (500× slower than RAM)
 HDD random: ~10,000,000 ns (100,000× slower than RAM)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The buffer pool is the single most important performance structure in a database — it determines the &lt;strong&gt;cache hit ratio&lt;/strong&gt;, which directly controls query latency.&lt;/p&gt;</description></item><item><title>MVCC Implementation</title><link>https://kyngo.github.io/learning-paths/paths/database-internals/06-mvcc/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://kyngo.github.io/learning-paths/paths/database-internals/06-mvcc/</guid><description>&lt;h2 id="why-mvcc"&gt;Why MVCC?&lt;a class="anchor" href="#why-mvcc"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Without concurrency control, simultaneous readers and writers would see inconsistent data — partial updates, phantom rows, or torn reads. Lock-based approaches (shared/exclusive locks on every row) are correct but cause readers to block writers and vice versa.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Multi-Version Concurrency Control (MVCC)&lt;/strong&gt; solves this by keeping multiple versions of each row. Readers see a consistent snapshot without acquiring locks; writers create new versions instead of overwriting in place. The result: &lt;strong&gt;readers never block writers, and writers never block readers&lt;/strong&gt;.&lt;/p&gt;</description></item><item><title>Query Processing</title><link>https://kyngo.github.io/learning-paths/paths/database-internals/07-query-processing/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://kyngo.github.io/learning-paths/paths/database-internals/07-query-processing/</guid><description>&lt;h2 id="from-sql-text-to-result-set"&gt;From SQL Text to Result Set&lt;a class="anchor" href="#from-sql-text-to-result-set"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A SQL query goes through several transformation stages before producing results. Understanding this pipeline explains why some queries are fast and others are slow — regardless of how the SQL is written.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Parser │ → │ Analyser │ → │ Rewriter │ → │ Planner/ │ → │ Executor │
│ │ │ │ │ │ │Optimiser │ │ │
│ SQL text │ │ Semantic │ │ View │ │ Cost- │ │ Runs the │
│ → parse │ │ checks, │ │ expansion│ │ based │ │ chosen │
│ tree │ │ resolve │ │ rule │ │ plan │ │ plan │
│ │ │ names │ │ rewrites │ │ selection│ │ │
└──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2 id="parsing"&gt;Parsing&lt;a class="anchor" href="#parsing"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The parser converts SQL text into an &lt;strong&gt;abstract syntax tree (AST)&lt;/strong&gt;, also called a parse tree.&lt;/p&gt;</description></item><item><title>Lock Management</title><link>https://kyngo.github.io/learning-paths/paths/database-internals/08-lock-management/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://kyngo.github.io/learning-paths/paths/database-internals/08-lock-management/</guid><description>&lt;h2 id="why-locks-still-matter-with-mvcc"&gt;Why Locks Still Matter with MVCC&lt;a class="anchor" href="#why-locks-still-matter-with-mvcc"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;MVCC handles most read/write concurrency without locks, but locks are still essential for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Write–write conflicts&lt;/strong&gt; — two transactions updating the same row need coordination&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DDL operations&lt;/strong&gt; — schema changes must not conflict with concurrent queries&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Explicit locking&lt;/strong&gt; — &lt;code&gt;SELECT ... FOR UPDATE&lt;/code&gt;, advisory locks&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Internal structures&lt;/strong&gt; — B+tree page latches, buffer pool frame pins&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This section covers the lock manager: the subsystem that grants, queues, detects deadlocks, and escalates locks.&lt;/p&gt;</description></item><item><title>Index Internals</title><link>https://kyngo.github.io/learning-paths/paths/database-internals/09-index-internals/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://kyngo.github.io/learning-paths/paths/database-internals/09-index-internals/</guid><description>&lt;h2 id="beyond-btrees"&gt;Beyond B+Trees&lt;a class="anchor" href="#beyond-btrees"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Section 2 covered B+trees — the most common index type. But databases support many specialised index structures for different access patterns. This section explores the internal mechanics of hash indexes, GiST, GIN, BRIN, bitmap indexes, and key optimisations like index-only scans and HOT updates.&lt;/p&gt;
&lt;p&gt;The Databases &amp;amp; SQL path covered &lt;em&gt;when&lt;/em&gt; to create indexes. This section covers &lt;em&gt;how&lt;/em&gt; they work under the hood.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="hash-indexes"&gt;Hash Indexes&lt;a class="anchor" href="#hash-indexes"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Hash indexes map keys to bucket numbers using a hash function. Each bucket is a page (or chain of pages) containing entries with the same hash.&lt;/p&gt;</description></item><item><title>Storage Engine Architecture</title><link>https://kyngo.github.io/learning-paths/paths/database-internals/10-storage-engine-architecture/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://kyngo.github.io/learning-paths/paths/database-internals/10-storage-engine-architecture/</guid><description>&lt;h2 id="what-is-a-storage-engine"&gt;What Is a Storage Engine?&lt;a class="anchor" href="#what-is-a-storage-engine"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A storage engine is the component responsible for &lt;strong&gt;how data is physically stored, retrieved, and modified&lt;/strong&gt;. It sits below the SQL layer and above the operating system, composing several subsystems covered in earlier sections.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;┌─────────────────────────────────────────────┐
│ SQL Layer │
│ (Parser → Planner → Optimiser → Executor) │
├─────────────────────────────────────────────┤
│ Storage Engine │
│ ┌──────────┐ ┌──────────┐ ┌─────────────┐ │
│ │ Access │ │ Buffer │ │ Transaction │ │
│ │ Methods │ │ Pool │ │ Manager │ │
│ │ (B+tree, │ │ │ │ (WAL, MVCC, │ │
│ │ heap, │ │ │ │ locks) │ │
│ │ LSM) │ │ │ │ │ │
│ └──────────┘ └──────────┘ └─────────────┘ │
├─────────────────────────────────────────────┤
│ File System / Direct I/O │
├─────────────────────────────────────────────┤
│ Storage Device │
└─────────────────────────────────────────────┘&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2 id="engine-composition"&gt;Engine Composition&lt;a class="anchor" href="#engine-composition"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Every storage engine combines the same fundamental subsystems — the differences lie in the choices each subsystem makes.&lt;/p&gt;</description></item></channel></rss>