CONCEPT Cited by 1 source
Adaptive Hash Index¶
The Adaptive Hash Index (AHI) is an in-memory hash table that MySQL's InnoDB storage engine builds at runtime as an optional overlay on top of its B+tree indexes. For index values InnoDB observes being looked up repeatedly, it populates an AHI entry whose key is the (full or prefix of the) index value and whose value is a direct pointer into the target page inside the buffer pool. Future lookups of the same value short-circuit the B+tree descent: a single O(1) hash probe replaces the O(log N) tree walk.
InnoDB does not support true on-disk HASH indexes. A user-
issued CREATE INDEX ... USING HASH against an InnoDB table
"will instead create a B-tree index" — InnoDB silently
substitutes B+tree and emits a
warning. The AHI is the in-memory-only workaround that
recovers hash-lookup performance for the hot subset of
keys. (Source:
sources/2026-04-21-planetscale-the-mysql-adaptive-hash-index.)
Architectural shape¶
┌────────────────────────────────┐
│ Query execution (InnoDB) │
└──────────────┬─────────────────┘
│ lookup(index, key)
▼
┌────────────────────────────────┐
│ Adaptive Hash Index │ ← in-memory, optional overlay
│ key → buffer-pool page ptr │
└──────────────┬─────────────────┘
│ miss / not eligible
▼
┌────────────────────────────────┐
│ Buffer pool │ ← cached 16KB pages (hot working set)
└──────────────┬─────────────────┘
│ miss
▼
┌────────────────────────────────┐
│ B+tree on disk (via page I/O)│
└────────────────────────────────┘
Three facts follow from this picture:
- AHI is a cache on top of the buffer pool, not on top of the B+tree. "The pointers in the adaptive hash index only point to data within the buffer pool." An AHI entry is valid only while the referenced page is buffer-pool-resident. Page eviction invalidates the corresponding AHI entries.
- AHI presupposes a sufficiently large buffer pool. "If it is small and there are a lot of evictions taking place, it is not worth using it." Small buffer pool + churn → AHI entries constantly invalidated by evictions → maintenance overhead exceeds lookup savings.
- AHI is adaptive both up and down. MySQL observes buffer-pool behaviour and "automatically adjust[s] its use of the AHI based on the behavior it observes … If conditions are not right for its use (few repeated lookups, small buffer pool, etc), MySQL will reduce or eliminate its use." See patterns/runtime-adaptive-in-memory-index for the general pattern.
Keys: full values vs prefixes¶
AHI entries can be either the full index value or a prefix of it. "If MySQL observes that a particular value is getting repeatedly looked up in a B-tree index, an entry in the AHI can be created either with the full value, or a prefix of the value." Prefix-keyed AHI entries amortise a single hash entry across multiple lookups sharing a common prefix. This mirrors prefix-index mechanics on B+tree secondary indexes without incurring the prefix's selectivity penalty — the B+tree still resolves the final page, the AHI just avoids the descent.
Config surface¶
| Knob | Default | Purpose |
|---|---|---|
innodb_adaptive_hash_index |
ON |
Enable/disable AHI globally |
innodb_adaptive_hash_index_parts |
8 (MySQL 5.7+) |
Partition the AHI across N independent hash tables to reduce latch contention |
Operationally observable via:
The INSERT BUFFER AND ADAPTIVE HASH INDEX section reports
Hash table size, node heap has N buffer(s), and the
load-bearing metric — hash searches/s vs non-hash
searches/s — which directly exposes AHI hit rate.
Why it pays off (and when it doesn't)¶
B+tree descents in InnoDB are already close to memory-speed when the relevant pages are buffer-pool-resident. What AHI still eliminates:
- Per-level page lookups. "There's still a (small) cost to looking up a page in the buffer pool." — hash- table lookup, latch acquisition, metadata bookkeeping at each level. For a 4-level tree, that's four per-level lookups replaced by one.
- Per-level key comparisons. Each internal node requires a binary-search through the node's keys; a deep enough tree accumulates hundreds of comparisons per lookup.
The benefit scales with (a) tree depth — "workloads using deeper B-tree indexes may see even more performance improvement" — and (b) workload concentration on a small repeated key set.
Benchmark: Dicken 2024-04-24¶
Two benchmark runs against a 390M-row user table with a
username B+tree secondary index (4 levels deep):
| Workload | AHI off | AHI on | Δ |
|---|---|---|---|
Single-value repeated (WHERE username='willpeace1', 500k iter) |
35.6 s / 14,043 QPS / 0 hash/s / 418k non-hash/s | 29.94 s / 16,701 QPS / 350,953 hash/s / 50,985 non-hash/s | +16% |
| 1000-value hot-set random (500k iter) | 54.16 s / 9,232 QPS | 43.24 s / 11,562 QPS | +20% |
Canonical framing: "even with such a large data set, the B-tree index … was only 4 levels deep … Running these types of workloads on tables with smaller indexes, and therefore shorter B-tree indexes, may result in less noticeable speedup. On the other hand, workloads using deeper B-tree indexes may see even more performance improvement."
Non-hash searches remain nonzero even with AHI on because "the query still needs to access the actual data in the row, not just the index value" — secondary-index lookups in InnoDB require a second B+tree walk into the clustered index to fetch the row data. AHI only accelerates the first (secondary-index) walk.
Relationship to other InnoDB in-memory structures¶
- Buffer pool — the substrate. AHI entries point into it; AHI is nothing without it. Common 50–70% of RAM sizing.
- Change buffer — deferred secondary-index write batching. Orthogonal to AHI (which accelerates reads). Both sit between SQL execution and the B+tree.
- Doublewrite buffer — durability mechanism for partial-page-write protection. Not a cache; no performance role.
- Adaptive hash index — this page. Read-side O(1) overlay on top of buffer-pool-resident hot pages.
When operators disable AHI¶
Two common reasons to set innodb_adaptive_hash_index=0:
- Latch contention at very high concurrency — before
MySQL 5.7's AHI partitioning, the single
btr_search_latchmutex became a bottleneck at dozens of concurrent B+tree lookups per second. Historically this produced the canonical "disable AHI on high- concurrency OLTP" advice still repeated in older references. MySQL 5.7'sinnodb_adaptive_hash_index_parts(default 8) largely closed this gap. - Workload shape mismatch — analytic / scan-heavy workloads with low temporal locality pay AHI's maintenance overhead without getting lookup savings. Dicken frames this as the primary decision criterion in 2024-04-24.
Seen in¶
- sources/2026-04-21-planetscale-the-mysql-adaptive-hash-index
— Ben Dicken (PlanetScale, 2024-04-24) canonicalises AHI
as InnoDB's in-memory substitute for true on-disk hash
indexes, a runtime-adaptive overlay on top of the
buffer pool that turns O(log N) B+tree descents into
O(1) hash probes for frequently looked-up values. Two
benchmarks at 390M rows quantify the benefit: 16%
speed-up on single-value repeated lookups (14,044 →
16,701 QPS), 20% on 1000-value hot-set random lookups
(9,232 → 11,562 QPS). Canonical disclosures: (1) AHI
pointers go into the buffer pool, so buffer pool size +
eviction rate determine whether AHI is worthwhile; (2)
AHI keys can be prefixes, not just full values; (3) AHI
usage auto-scales up and down as MySQL observes workload
behaviour; (4) observable via
SHOW ENGINE INNODB STATUS \G;under theINSERT BUFFER AND ADAPTIVE HASH INDEXsection (hash searches/svsnon-hash searches/s); (5) tuned viainnodb_adaptive_hash_index(on/off) andinnodb_adaptive_hash_index_parts(partition count, for latch-contention mitigation).