Details
-
New Feature
-
Status: Open (View Workflow)
-
Major
-
Resolution: Unresolved
-
None
Description
Problem
MariaDB nested-loop joins can repeatedly probe the same inner ref key when
outer rows contain duplicate join key values. The executor currently repeats
the same handler lookup and scans the same matching inner rows for each
duplicate key.
MariaDB already has subquery_cache for dependent subquery expression results,
but it does not cache and replay ordinary nested-loop join inner row batches.
JOIN_CACHE/BNL/BKA batch join execution, but they do not memoize the complete
row batch for one JT_REF key.
Proposed solution
Add a statement-local Partial Result Cache, also called PTRC, for eligible
nested-loop join inner ref accesses.
The cache is local to a single statement. It is not shared across statements,
sessions, or transactions, and it does not change table data visibility.
For an eligible inner JT_REF join tab:
- on a cache miss, execute the original ref lookup and copy returned
TABLE::record[0] rows into a key-local cache entry - on a cache hit, replay cached rows into TABLE::record[0]
- continue through the normal nested-loop evaluation path after replay
The implementation adds:
- optimizer_switch=partial_result_cache, disabled by default
- PRC_JOIN / NO_PRC_JOIN optimizer hints for targeted review and testing
- bounded memory controls for statement-local cache storage
- runtime low-hit-ratio bypass mode
- pass-through fallback when caching is unsupported, unsafe, or not selected
- EXPLAIN, EXPLAIN FORMAT=JSON, ANALYZE FORMAT=JSON, optimizer trace, and
status counter diagnostics
Unsupported or fallback cases
Unsupported shapes keep the original executor path. Examples include:
- non-JT_REF access
- first non-const table
- selected join-buffer plans, including BNL/BKA/BKAH
- outer-join or semi-join inner tables
- BLOB/TEXT row storage
- locking reads
- triggered ref access
- zero memory cap
- low estimated or runtime hit-ratio cases
If runtime memory allocation exceeds the configured cap, the cache clears its
entries, enters bypass mode, and continues through the normal ref access path.
Correctness
PTRC does not change join semantics. Cached rows are copied back into
TABLE::record[0], and MariaDB continues through the existing
evaluate_join_record() path, including normal condition evaluation,
projection, grouping, and result production.
Negative cache entries are supported for repeated ref keys that find no inner
rows. The cache is statement-local, so it does not reuse rows across statement
boundaries or visibility changes.
Validation
The current MariaDB PTRC branch has focused MTR coverage for:
- optimizer_switch and system variables
- nested-loop ref row replay
- multi-row cache entries
- negative cache entries
- VARCHAR and NULL row replay
- PRC_JOIN and NO_PRC_JOIN hints
- traditional EXPLAIN and EXPLAIN FORMAT=JSON visibility
- ANALYZE FORMAT=JSON runtime counters
- optimizer trace diagnostics
- status counters
- memory-cap bypass
- runtime low-hit-ratio bypass
- unsupported BLOB and outer-join shapes
Release build and focused release MTR validation were run locally.
Local performance validation used a TPCH-derived repeated nested-loop join
case, not standard TPC-H Q17 as the performance claim. Standard Q17 was used
only as a correlated-subquery sanity check for the switch matrix.
On the TPCH-derived repeated-NLJ case, local release-build measurements showed
about 3.23x speedup with subquery_cache=off and about 3.25x speedup with
subquery_cache=on, with identical result hashes. These numbers are local
engineering evidence only, not official TPC-H results.
Implementation branch / pull request
Initial development branch:
https://github.com/ZhuQingping/mariadb_server/tree/support_ptrc
Licensing
I submit this contribution under the New BSD License, also known as the
3-clause BSD License.
Attachments
Issue Links
- blocks
-
PERF-537 Loading...