Chapter 10 · Optimizer, EXPLAIN, Statistics, Histograms, and Query Tuning

Optimizer Switches, Hints, Invisible/Ignored Index Concepts, and Regression Control

Control MariaDB query-plan regressions with evidence packets, scoped optimizer switches/hints, ignored-index experiments, explicit acceptance criteria and reversible rollback paths.

Advanced100–120 minutesRegression-control + ignored-index labMariaDB Community 12.3.2 baselineIgnored indexes 10.6+ · hints version-gatedLast reviewed: August 2026

Learning outcomes

After a MariaDB upgrade, one ServiceHub query regresses. The team immediately adds FORCE INDEX, commits it to the ORM, and declares victory. Six months later the data distribution changes, the forced index becomes worse than the optimizer’s natural plan, and nobody remembers why the hint exists. Hints can be valuable, but a durable regression workflow needs a query identity, before/after plans, runtime measurements, change isolation, acceptance criteria and an explicit removal/rollback path.

01

Use optimizer_switch, classic index hints, new-style hints and ignored indexes as scoped diagnostic/control mechanisms.

02

Distinguish MariaDB ignored indexes from MySQL invisible-index terminology while understanding the comparable purpose.

03

Build a query-regression evidence packet with fingerprint, parameter classes, plans, runtime counters and workload context.

04

Test an index-removal hypothesis safely by marking the index IGNORED before dropping it.

05

Define rollback and hint-retirement criteria so tactical controls do not become undocumented permanent architecture.

Version boundary

Ignored indexes are available from MariaDB 10.6. New-style optimizer hints expanded substantially in MariaDB 12.x; join-order hints are available from 12.0 and additional hints were added in later 12.x releases. The stable lab baseline is MariaDB 12.3.2, so the lesson labels version-sensitive hint syntax and retains classic USE/FORCE/IGNORE INDEX patterns as portable MariaDB diagnostics.

1. A regression-control packet starts before the fix

sql · capture a reproducible baseline
USE servicehub_optimizer_lab;SELECT VERSION(),@@optimizer_switch,@@use_stat_tables;SHOW CREATE TABLE work_orders\GSHOW INDEX FROM work_orders;EXPLAIN FORMAT=JSONSELECT work_order_id,scheduled_at,total_centsFROM work_ordersWHERE status='open' AND region_code='BAK'ORDER BY scheduled_at DESCLIMIT 50;ANALYZE FORMAT=JSONSELECT work_order_id,scheduled_at,total_centsFROM work_ordersWHERE status='open' AND region_code='BAK'ORDER BY scheduled_at DESCLIMIT 50;

The regression record should include a normalized query fingerprint plus representative parameter classes, not only one literal query. A predicate can be selective for one value and broad for another. Capture server version, optimizer policy, relevant DDL, statistics state and data-distribution summary. If the issue appeared after an upgrade, keep the pre-upgrade plan/runtime packet as the comparison target; MariaDB’s optimizer changed materially starting in 11.0, so plan changes across versions are expected and must be validated rather than assumed good or bad.

2. Classic index hints are local but still brittle

Control Effect Use carefully because
USE INDEX Limits candidate indexes. Can hide a better index added later.
FORCE INDEX Strongly favors named index and treats scans as expensive. Can lock the query into a poor choice as data changes.
IGNORE INDEX Excludes named indexes for this query. Query-specific and easy to forget in application SQL.
optimizer_switch Enables/disables optimizer strategy families. A session/global setting can affect many statements.
new-style /*+ ... */ hints Fine-grained query/table strategy control on supported versions. Version-sensitive syntax; can become upgrade debt.
sql · compare natural and forced candidates
EXPLAINSELECT work_order_id,scheduled_at,total_centsFROM work_ordersWHERE status='open' AND region_code='BAK'ORDER BY scheduled_at DESC LIMIT 50;EXPLAINSELECT work_order_id,scheduled_at,total_centsFROM work_orders FORCE INDEX(idx_status_region_sched)WHERE status='open' AND region_code='BAK'ORDER BY scheduled_at DESC LIMIT 50;

If the forced plan is better in a controlled test, that is a clue—not proof that FORCE INDEX is the final design. Ask why the optimizer rejected it. A stale or skew-blind estimate can often be fixed with statistics; a query shape may need a better composite index; an upgrade may have changed cost assumptions. Keep the hint as a tactical containment only when the business needs immediate stability and the underlying cause cannot be corrected safely yet.

3. Ignored indexes create a reversible removal experiment

sql · mark, verify, restore
ALTER TABLE work_orders  ALTER INDEX idx_customer_sched IGNORED;SHOW INDEX FROM work_orders;SELECT INDEX_NAME,IGNOREDFROM information_schema.STATISTICSWHERE TABLE_SCHEMA='servicehub_optimizer_lab'  AND TABLE_NAME='work_orders';EXPLAINSELECT * FROM work_ordersWHERE customer_id=120ORDER BY scheduled_at DESC;-- Roll back the experiment instantly:ALTER TABLE work_orders  ALTER INDEX idx_customer_sched NOT IGNORED;

MariaDB keeps an ignored index maintained and visible in metadata but the optimizer treats it as if it does not exist; it is also not used as a source of optimizer statistics. This makes ignored state useful before dropping a suspected redundant index: observe the real workload, restore instantly if regressions appear, and only schedule a physical drop after the observation window. The primary key cannot be ignored.

4. Deliberately wrong: force the plan before checking statistics

A hint can make symptoms disappear while preserving the bad estimate that caused the regression. That is dangerous because related queries without the hint can still regress, and the hinted query may become worse later. The repair workflow is causal: validate statistics and index definitions, compare estimated/actual rows, inspect transformations, then use the narrowest control necessary.

text · evidence-first sequence
1. Fingerprint query and classify parameters.2. Capture EXPLAIN FORMAT=JSON and safe ANALYZE FORMAT=JSON.3. Record relevant index/statistics metadata and data skew.4. Reproduce under representative cache/concurrency.5. Apply one controlled change: stats refresh, index, SQL rewrite, switch, or hint.6. Re-run the same measurements.7. Accept only if correctness is unchanged and target latency/throughput improves without a material regression elsewhere.8. Define rollback and, for hints, a retirement date/condition.

Do not combine “refresh stats + add index + change optimizer_switch + deploy ORM hint” into one experiment. You will not know which change mattered, and rollback becomes ambiguous. One-variable-at-a-time discipline is especially important in query tuning because plan changes can interact nonlinearly.

5. New-style hints are diagnostic tools, not a new configuration language

MariaDB 12.0 introduced join-order hints such as JOIN_FIXED_ORDER and table-level BKA/BNL controls; 12.1 expanded the hint set further. MariaDB 12.3 also documents newer query-control additions. These features can make A/B plan experiments precise, but their availability and semantics are version-sensitive. A course or runbook should always state the minimum server version and verify ignored/accepted hints rather than copying MySQL syntax by appearance.

sql · version-gated example
-- MariaDB 12.0+ join-order hint example for a disposable test:EXPLAIN FORMAT=JSONSELECT /*+ JOIN_FIXED_ORDER() */       c.customer_id,w.work_order_idFROM customers cJOIN work_orders w ON w.customer_id=c.customer_idWHERE c.region_code='TBZ' AND w.status='open';

If the hinted plan wins, use optimizer trace and cardinality evidence to understand why the optimizer’s natural order differed. If the hint is deployed, document the exact incident, server version, baseline metrics, owner, expiry/retirement condition and test coverage that will tell you when it can be removed.

6. Regression acceptance and rollback matrix

Dimension Acceptance evidence Rollback trigger
Correctness Same result set / business invariants across parameter classes. Any semantic difference.
Plan quality Estimate/runtime row flow improves or remains stable. New explosion in loops/rows/temp work.
Latency Representative p50/p95/p99 under controlled concurrency. Tail latency breaches SLO.
Resources CPU, reads, temporary work and memory remain acceptable. Material resource regression.
Writes No unacceptable index-maintenance or lock cost. Write throughput/lock SLO regression.
Operability Documented owner/version/rollback/retirement path. Control cannot be safely reversed or explained.

A plan is not “better” merely because it uses more indexes or shows a lower local execution time once. Production judgment includes tail latency, concurrency, write cost, memory, lock behavior, replication impact and operational reversibility. The optimizer is one component of a system; regression control must protect the whole workload.

7. Parameter-class baselines prevent false regressions

A single SQL fingerprint can represent workloads with very different selectivity. For example, the same predicate can target a tiny enterprise customer or a tenant that owns a large fraction of the table. A regression packet should therefore identify parameter classes such as rare, typical, and dominant—not only one literal query. If a change improves the rare class but damages the dominant class, the aggregate dashboard may hide the tradeoff until traffic mix changes.

For each class, retain plan and runtime evidence plus workload-level latency percentiles. This makes upgrade testing substantially stronger: you can compare whether the new optimizer merely chose a different-looking plan or actually changed resource consumption for a business-relevant segment.

8. Hints and ignored indexes need lifecycle ownership

Control Required documentation Retirement test
FORCE INDEX Incident, parameter class, forced alternative, measured benefit Re-test natural optimizer after stats/schema/version change.
optimizer_switch override Exact flag, scope, server version, affected query set Remove when underlying estimate/transformation issue is fixed.
New-style optimizer hint Minimum MariaDB version and accepted syntax Upgrade test without hint plus plan/runtime comparison.
Ignored index Reason for proposed removal and observation window Drop only after workload evidence shows no regression.

Ownership matters because tactical controls age. A hint written for MariaDB 12.3 may become unnecessary after statistics improve or optimizer behavior changes. An ignored index retained forever still consumes write and storage cost. Every control should therefore have an owner, review date, rollback command and measurable condition for removal.

9. Chapter verification, cleanup, and next bridge

  1. Confirm all query experiments were run on the disposable ServiceHub optimizer lab.
  2. Restore any changed session optimizer settings.
  3. Restore ignored indexes to NOT IGNORED unless the experiment explicitly requires otherwise.
  4. Save one complete regression packet: SQL fingerprint, parameter class, EXPLAIN JSON, ANALYZE JSON, stats/index evidence, before/after metric, rollback criterion.
  5. Clean up with DROP DATABASE servicehub_optimizer_lab; when finished.

Check your understanding

  1. Why is FORCE INDEX best treated as a containment/diagnostic tool rather than the first permanent fix?
  2. What does IGNORED do to an index in MariaDB?
  3. Why is ignored-index testing safer than immediately dropping a questionable index?
  4. What must a version-sensitive optimizer hint record?
  5. What does “one controlled change at a time” protect during tuning?
Review the answers

FORCE INDEX can hide stale statistics, bad schema or a data-distribution problem and may age poorly. An ignored index remains maintained and visible but is excluded from optimizer use/statistics. Ignoring is instantly reversible, while recreating a dropped large index can be expensive. Version-sensitive hints need the exact supported server version, rationale and rollback/retirement criteria. Isolating one change preserves causal attribution and makes rollback unambiguous.

Chapter 10 completes the optimizer evidence loop: candidate plans, runtime counters, statistics, transformations and scoped controls. Chapter 11 shifts from query planning to server-side objects—views, routines, triggers and the event scheduler—where definers, privileges and hidden coupling become the next operational correctness boundary.

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only assets compatible with the Ethereum/ERC-20 network. Do not send TRC-20/TRON assets.