Chapter 16 · Partitioning, Large Tables, Online DDL, and Data Lifecycle
Partitioning Strategies, Partition Pruning, Keys, Constraints, and Design Limits
Design partitioning as a routing and lifecycle mechanism, then prove optimizer pruning and confront the key/constraint limits that determine whether partitioning is appropriate.
Learning outcomes
ServiceHub has grown from a small ticketing database into an event platform that retains years of operational telemetry. A query that asks for one recent day should not need to touch every historical chunk, and retention should not mean deleting hundreds of millions of rows one by one. Partitioning can help—but only when its routing rules, key restrictions, optimizer pruning, and operational limits are understood precisely.
Distinguish partition routing from partition pruning and explain why partitioning is not automatically a query-speed feature.
Choose among RANGE, LIST, HASH, and KEY strategies based on the access/lifecycle problem rather than fashion.
Apply MariaDB restrictions on unique keys, storage engines, and foreign keys before designing a partitioned schema.
Build a time-series InnoDB table and prove pruning with EXPLAIN PARTITIONS and INFORMATION_SCHEMA.PARTITIONS.
Diagnose two realistic partitioning mistakes and repair them without weakening relational correctness.
This lesson uses MariaDB Community Server 12.3.2 as the current lab/reference baseline while retaining the course curriculum anchor of 11.8 LTS. Partitioning behavior is storage-engine and version sensitive. Verify the target version before using limits or maintenance syntax in production.
1. Mental model: one logical table, many physical partitions
A partitioned table is still one SQL table. MariaDB evaluates a partitioning expression to decide which partition owns each row. That write-time decision is routing. On reads, the optimizer may infer that only a subset of partitions can satisfy a predicate; eliminating the rest is partition pruning. Pruning is an optimizer result, not a promise that follows merely from creating partitions.
| Term | Meaning | Operational consequence |
|---|---|---|
| partitioning key | Column(s)/expression used to route a row | Changing it can move a row between partitions. |
| RANGE / RANGE COLUMNS | Partitions cover ordered intervals | Natural fit for time-based retention and rolling windows. |
| LIST / LIST COLUMNS | Partitions own enumerated values | Useful for small, stable categorical domains. |
| HASH / LINEAR HASH | Expression maps rows across N partitions | Distributes data but usually does not provide time-retention semantics. |
| KEY / LINEAR KEY | MariaDB hashes key columns | Useful for distribution when an explicit hash expression is undesirable. |
| pruning | Optimizer excludes impossible partitions | Must be demonstrated from the real query shape. |
MariaDB also supports SYSTEM_TIME partitioning for system-versioned tables, but that is a separate lifecycle mechanism covered later in the course. For this chapter, focus on conventional partitioning of current tables.
2. Verify support and capture a baseline before creating anything
SELECT VERSION() AS server_version;SHOW PLUGINS;SHOW ENGINES;SELECT @@sql_mode, @@innodb_file_per_table;
Look for active partition support and an available InnoDB engine. “CREATE TABLE succeeded on another MariaDB machine” does not prove your packaged server, storage engine, or version has the same capabilities.
3. Build a time-series table whose keys obey partition rules
MariaDB requires every unique key—including the primary key—to
include every column used by the partitioning expression. That
requirement affects identifier design. A globally unique
event_id by itself cannot be the primary key if the
table is partitioned by event_day.
DROP DATABASE IF EXISTS servicehub16_l1;CREATE DATABASE servicehub16_l1;USE servicehub16_l1;CREATE TABLE ticket_events ( event_id BIGINT NOT NULL AUTO_INCREMENT, event_day DATE NOT NULL, ticket_id BIGINT NOT NULL, event_type VARCHAR(32) NOT NULL, payload VARCHAR(255) NULL, created_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), PRIMARY KEY (event_id, event_day), KEY ix_ticket_day (ticket_id, event_day)) ENGINE=InnoDBPARTITION BY RANGE COLUMNS(event_day) ( PARTITION p2026q1 VALUES LESS THAN ('2026-04-01'), PARTITION p2026q2 VALUES LESS THAN ('2026-07-01'), PARTITION p2026q3 VALUES LESS THAN ('2026-10-01'), PARTITION p2026q4 VALUES LESS THAN ('2027-01-01'), PARTITION p_future VALUES LESS THAN (MAXVALUE));INSERT INTO ticket_events(event_day,ticket_id,event_type,payload) VALUES('2026-02-10',101,'created','web'),('2026-05-12',101,'assigned','queue-a'),('2026-08-03',202,'created','api'),('2026-11-18',303,'closed','resolved');
SELECT PARTITION_NAME, PARTITION_ORDINAL_POSITION, PARTITION_DESCRIPTION, TABLE_ROWSFROM INFORMATION_SCHEMA.PARTITIONSWHERE TABLE_SCHEMA='servicehub16_l1' AND TABLE_NAME='ticket_events'ORDER BY PARTITION_ORDINAL_POSITION;
TABLE_ROWS is metadata and may be estimated for
InnoDB; use it to understand partition shape, not as an exact
accounting ledger.
4. Prove pruning instead of assuming it
EXPLAIN PARTITIONSSELECT event_id,ticket_id,event_typeFROM ticket_eventsWHERE event_day >= '2026-08-01' AND event_day < '2026-09-01';
The partitions column should identify
p2026q3 for this predicate. That proves the
optimizer can derive the relevant range. It does not prove the
query is fast: index selectivity, row count inside the
partition, cache state, and result volume still matter.
EXPLAIN PARTITIONSSELECT event_id,event_day,event_typeFROM ticket_eventsWHERE event_type='created';
Because event_type does not constrain
event_day, multiple/all partitions can remain
candidates. Partitioning did not replace the need for suitable
indexes.
5. Wrong approach #1: keep a primary key that omits the partitioning column
CREATE TABLE bad_events ( event_id BIGINT PRIMARY KEY, event_day DATE NOT NULL, payload VARCHAR(100)) ENGINE=InnoDBPARTITION BY RANGE COLUMNS(event_day) ( PARTITION p_old VALUES LESS THAN ('2026-07-01'), PARTITION p_new VALUES LESS THAN (MAXVALUE));
MariaDB rejects this design because the primary key does not
include event_day. A representative error is
ERROR 1503 (HY000): A PRIMARY KEY must include all columns in
the table's partitioning function. The repair is not to remove uniqueness blindly; redesign the
key so every unique key contains the partitioning column, and
verify whether that changes application lookup semantics.
6. Wrong approach #2: add a foreign key to a partitioned InnoDB table
CREATE TABLE tickets ( ticket_id BIGINT PRIMARY KEY) ENGINE=InnoDB;ALTER TABLE ticket_events ADD CONSTRAINT fk_event_ticket FOREIGN KEY (ticket_id) REFERENCES tickets(ticket_id);
Current MariaDB partitioning does not allow a partitioned table to contain or be referenced by foreign keys. The operation fails rather than quietly weakening integrity. If the domain requires database-enforced referential integrity, partitioning this table may be the wrong design. Application-side checks are not automatically equivalent to an InnoDB foreign key.
7. Design limits that should influence the decision
| Constraint | Why it matters |
|---|---|
| Every unique key includes partition columns | Can widen primary/secondary indexes and change application lookup patterns. |
| No foreign keys on/referencing partitioned tables | May conflict directly with relational integrity requirements. |
| All partitions use one supported engine | You cannot treat partitions as arbitrary per-engine mini-tables. |
| Many partitions add metadata/operational cost | A very high partition count can hurt management and planning. |
| Cross-partition queries are not automatically parallel | Partitioning is not a substitute for distributed execution. |
| Pruning depends on predicates | Poor query shapes may still scan many partitions. |
8. Production judgment and cleanup
Use partitioning when the partition key aligns with a real management or access boundary—especially time-based retention, archival, or predictable pruning. Do not introduce it solely because a table is “large.” First prove the bottleneck, check key/FK consequences, measure representative plans, and document how partitions will be created, monitored, backed up, and retired.
Prerequisites: MariaDB Community Server 12.3.2 lab (curriculum anchor 11.8 LTS), active partitioning support, InnoDB, and a disposable account with CREATE/ALTER/DROP plus normal DML privileges. Production designs must re-check target-version partition limits and storage-engine support.
Check your understanding
- What is the difference between partition routing and partition pruning?
- Why must event_day appear in every UNIQUE/PRIMARY key in the lab table?
- What does EXPLAIN PARTITIONS prove, and what does it not prove?
- Why can partitioning conflict with a schema that depends on foreign keys?
- When is a RANGE time partition a stronger design choice than HASH partitioning?
Review the answers
Routing decides where a row belongs; pruning decides which partitions a read can skip. MariaDB requires all partition-expression columns in every unique key. EXPLAIN PARTITIONS proves optimizer partition selection, not end-to-end latency. Current MariaDB partitioned tables cannot participate in foreign keys. RANGE is especially useful when ordered time boundaries match pruning and lifecycle actions such as dropping old partitions.
DROP DATABASE IF EXISTS servicehub16_l1;