LabHub

Blog

Database Complete Guide — Internals, Indexes, Query Planner, Partitioning, Vector DB (Season 2 Ep 13, 2025)

한국어English日本語中文

Intro — Why You Have to Know Databases Deeply

Counterexamples to "surely knowing SQL is enough?":

For a senior engineer in 2025, deep database knowledge is required literacy.


Part 1 — Two Philosophies of Storage Engines

1.1 B-Tree (Balanced Tree)

Read-optimized. The default in traditional RDBMSs (PostgreSQL, MySQL InnoDB).

     [50]
    /    \
  [20]  [80]
 /  \   /  \
[10][30][70][90]

Characteristics:

Downside: many random writes cause splits plus I/O.

1.2 LSM-Tree (Log-Structured Merge)

Write-optimized. RocksDB, Cassandra, ScyllaDB, LevelDB, ClickHouse.

Memtable (RAM)
   ↓ flush
L0 SSTables (disk)
   ↓ compact
L1 SSTables
   ↓ compact
L2 SSTables ...

Characteristics:

Downside: reads have to search several layers → mitigated with Bloom filters and friends.

1.3 How to Choose

WorkloadPreference
OLTP (reads ~ writes)B-Tree (Postgres, MySQL)
Write-heavy (logs, messages)LSM (Cassandra, RocksDB)
Time-seriesLSM or columnar (InfluxDB, TimescaleDB)
AnalyticsColumnar (ClickHouse, DuckDB)

1.4 2024-2025 Trend: B-Tree and LSM Converge


Part 2 — Indexes in Depth

2.1 Eight Kinds of Index

KindPurpose
B-TreeRange queries (most common)
HashEquality only
GINFull-text search, JSONB, Array
GiSTGeo and geometry, range types
SP-GiSTSpatial data
BRINApproximate index for huge tables
BloomMulti-column OR search
Covering (INCLUDE)Extra columns in the index (index-only scan)

2.2 Five Principles of Index Design

  1. Queries come first: design from the queries you actually run
  2. Selectivity: highly selective columns first
  3. Composite index order: start with the filter you use most often
  4. Cover with INCLUDE: avoid touching the table
  5. Drop indexes you do not use: they degrade write performance

2.3 Top 10 Reasons an Index Is Not Used

  1. A function is applied: WHERE LOWER(email) = ... → needs an expression index
  2. Implicit type conversion: WHERE id = '123' (id is an int)
  3. Leading wildcard: LIKE '%foo' → full scan
  4. OR conditions: the optimizer may give up → use UNION
  5. Not equals (!=): mostly does not use an index
  6. NULL comparison: Postgres can index IS NULL, but design with care
  7. The data is small: the planner prefers a sequential scan
  8. Wrong composite index order: WHERE b = ? against an (a, b) index
  9. Stale statistics: ANALYZE needed
  10. Index bloat: REINDEX needed

Part 3 — The Query Planner and EXPLAIN

3.1 What the Planner Does

It converts SQL (declarative) into an execution plan (procedural). Cost-based optimization (CBO).

3.2 Reading PostgreSQL EXPLAIN

EXPLAIN (ANALYZE, BUFFERS, VERBOSE)
SELECT u.name, COUNT(o.id)
FROM users u
JOIN orders o ON o.user_id = u.id
WHERE u.country = 'KR'
GROUP BY u.name;

Example output:

HashAggregate  (cost=1234..5678 rows=100)
  Group Key: u.name
  ->  Hash Join  (cost=500..1200 rows=10000)
        Hash Cond: o.user_id = u.id
        ->  Seq Scan on orders o  (cost=0..800 rows=100000)
        ->  Hash  (cost=300..300 rows=500)
              ->  Index Scan on users_country_idx  (cost=0..300 rows=500)

How to read it:

3.3 Important Node Types

NodeMeaning
Seq ScanFull scan (fine for small tables or low selectivity)
Index ScanUses an index
Index Only ScanAnswered from the index alone (fast)
Bitmap Heap ScanFind rows via the index, then hit the table in bulk
Nested Loop JoinSmall outer plus an indexed inner
Hash JoinBuilds a hash table and matches against it
Merge JoinWhen both sides are already sorted
Hash AggregateGROUP BY
SortORDER BY

3.4 Postgres Has No Plan Hints

MySQL and Oracle have hints. In Postgres you steer the planner by tuning ANALYZE, statistics targets, and random_page_cost.


Part 4 — Transaction Isolation

4.1 ACID Restated

4.2 Four Isolation Levels (SQL-92)

LevelPrevents
Read Uncommitted(prevents almost nothing)
Read CommittedDirty Read
Repeatable Read+ Non-repeatable Read
Serializable+ Phantom

4.3 Default Isolation Level by DB

DBDefault
PostgreSQLRead Committed
MySQL InnoDBRepeatable Read
OracleRead Committed (Serializable available)
SQL ServerRead Committed

4.4 Snapshot Isolation vs Serializable

4.5 A Write Skew Example

At least one of two doctors must be on call:

-- T1: reads on_call, sees 2 → OK
-- T2: concurrently reads on_call, sees 2 → OK
-- Both flip their own status to off-call
-- Commit → 0 doctors on call. Constraint violated

Only Serializable detects this.

4.6 Deadlock

Two or more transactions wait on each other's locks. The DB detects it → sacrifices one (abort).

Prevention:


Part 5 — MVCC (Multi-Version Concurrency Control)

5.1 MVCC Basics

"Writers do not block readers, and readers do not block writers."

Multiple versions of each row are kept, and each transaction reads the version belonging to its own snapshot.

5.2 MVCC in Postgres

5.3 The Vacuum Bloat Problem

5.4 MySQL InnoDB MVCC


Part 6 — Partitioning and Sharding

6.1 Vertical vs Horizontal

6.2 Partitioning (Inside a Single DB)

Native in Postgres (10+):

CREATE TABLE events (
  id bigserial,
  event_time timestamptz,
  data jsonb
) PARTITION BY RANGE (event_time);

CREATE TABLE events_2025_01 PARTITION OF events
  FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');

Upside: partition pruning, easy maintenance (DROP old partition). Limit: still one DB. Bounded by a single server's capacity.

6.3 Sharding (Multiple DBs)

Spread the data across several servers.

Choosing a shard key:

Sharding methods:

6.4 Cross-Shard Queries

Expensive by default. When a JOIN or ORDER BY spans several shards, the merge happens at the application level.

Fixes:

6.5 Distributed SQL DBs (Sharding Handled for You)


Part 7 — Replication and HA

7.1 Replication Styles

7.2 Postgres Replication in 2025

7.3 The Failover Pattern

Primary failure detected
   (consensus: etcd/Patroni)
Pick the most up-to-date standby
Promote the new primary + the application switches DSN
Rebuild the former primary

Key point: prevent split-brain (fencing).

7.4 Putting Read Replicas to Work


Part 8 — PostgreSQL Runs Away With 2025

8.1 Why Postgres Won

Why Postgres became the de facto default when picking a DB in 2024-2025:

  1. Extensibility: a huge number of extensions
  2. JSONB: NoSQL features built in
  3. pgvector: vector DB capability
  4. PostGIS: unmatched GIS
  5. Timescale: time-series
  6. Citus: distribution
  7. Logical Replication: CDC and migrations
  8. License: the PostgreSQL License (permissive)
  9. Community: stable, not captive to one big vendor
  10. Managed in the cloud: Aurora, RDS, Cloud SQL, Neon, Supabase

8.2 Top 15 Postgres Extensions (2025)

ExtensionPurpose
pgvectorVector Search
PostGISGeospatial
TimescaleDBTime-series
CitusDistribution
pg_partmanPartition management
pg_cronScheduling
pg_stat_statementsQuery performance
hypopgHypothetical indexes
pg_repackOnline reorganization
pg_hint_planPlan hints
pg_trgmSimilarity search
unaccentAccent removal
uuid-osspUUID generation
pgcryptoCryptography
hstoreKey-value

8.3 The 2024-2025 Postgres Ecosystem


Part 9 — NoSQL: When to Reach for It

9.1 Five NoSQL Categories

CategoryExamplesPurpose
DocumentMongoDB, FirestoreFlexible schema
Key-ValueRedis, DynamoDBFast lookups
Wide-columnCassandra, ScyllaDB, HBaseLarge-scale writes
GraphNeo4j, NeptuneRelationship-centric
Time-SeriesInfluxDB, TimescaleTime-series

9.2 The 2025 Reality: "Postgres Is Enough"

Postgres covers most NoSQL use cases:

Still pick NoSQL when you need:

9.3 Where Redis Sits

In-memory cache, queue, ranking, rate limiting. The tool "everybody has at least one of."

The 2024 license change → the Valkey fork (driven by AWS and Google). More options to choose from now.


Part 10 — Vector DB

10.1 Why a Vector DB Is Needed

Approximate nearest neighbor (ANN) search, for things like LLM embedding search and recommendations.

10.2 ANN Algorithms

10.3 2025 Vector DB Comparison

DBTraits
pgvectorA Postgres extension, best integration
QdrantRust, fast, strong filtering
WeaviateHybrid search, GraphQL API
MilvusLarge scale, Zilliz
PineconeSaaS, easy to operate
LanceDBFile based, embedded
TurbopufferServerless

10.4 2025 Recommendations

10.5 A pgvector Example

CREATE EXTENSION vector;

CREATE TABLE documents (
  id bigserial PRIMARY KEY,
  content text,
  embedding vector(1536)
);

CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops);

-- Search
SELECT content
FROM documents
ORDER BY embedding <=> '[0.1, 0.2, ...]'::vector
LIMIT 10;

Part 11 — A Six-Month DB Roadmap

Month 1: SQL in Depth

Month 2: Postgres Internals

Month 3: Storage Engines

Month 4: Distributed DBs

Month 5: Vector DB

Month 6: Optimization and Operations


Part 12 — A 12-Point DB Checklist

  1. You know the criteria for choosing B-Tree vs LSM-Tree
  2. You know five Postgres index types and what each is for
  3. You can name five reasons an index is not used
  4. You can read EXPLAIN ANALYZE output
  5. You know the four transaction isolation levels along with the anomalies they prevent
  6. You can explain write skew
  7. You know the relationship between MVCC and Vacuum
  8. You know the difference between partitioning and sharding
  9. You know the trade-offs of hash vs range sharding
  10. You know the top 5 Postgres extensions
  11. You know how to choose between pgvector and Qdrant
  12. You know why a connection pool (PgBouncer) is necessary

Part 13 — Ten DB Antipatterns

  1. Trusting the ORM and never inspecting the queries: N+1 and loading far too many columns are common
  2. Adding indexes one by one without limit: writes slow down. Drop the ones that go unused
  3. Long transactions: MVCC bloat. Keep transactions short
  4. Blind faith in the default isolation level: not knowing the limits of Read Committed leads to write skew bugs
  5. Managing connections one at a time: thousands of connections with no pool → Postgres falls over
  6. Everything into JSONB: you give up the strengths of a schema. Structured data belongs in columns
  7. The wrong shard key: hot shard → resharding hell
  8. Unplanned migration downtime: a zero-downtime migration strategy is mandatory
  9. Ignoring vacuum tuning: your table bloats 10x without you noticing
  10. Using the DB as a message queue: possible, but not recommended. Use Redis or Kafka

Closing — The DB Is "the Gravity of a Production System"

You can replace the application, but you cannot easily change the DB schema. The DB is the part of a production system with the most inertia.

Which is why you have to design it well the first time:

DB engineering in 2025 looks like this:


Next Post — "Networking Complete Guide: HTTP/3, QUIC, TLS 1.3, gRPC, WebSocket, CDN"

Season 2 Ep 14 is the plumbing of the internet: networking. The next post covers:

The wires you cannot see, in the next post.

Comments

No comments yet.

Sign in to leave a comment