Skip to content

executor: Improve HashAgg string collation key caching#10978

Open
ChangRui-Ryan wants to merge 4 commits into
pingcap:masterfrom
ChangRui-Ryan:changrui_ci_cache
Open

executor: Improve HashAgg string collation key caching#10978
ChangRui-Ryan wants to merge 4 commits into
pingcap:masterfrom
ChangRui-Ryan:changrui_ci_cache

Conversation

@ChangRui-Ryan

@ChangRui-Ryan ChangRui-Ryan commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: close #10979

Problem Summary

For string GROUP BY keys using a case-insensitive collation such as utf8mb4_general_ci, HashAgg needs to generate a collation sort key before probing or inserting into the hash table.

Previously, the sort key was generated for every input row. When the grouped column has a low number of distinct raw values, the same raw string is converted to the same sort key repeatedly, causing unnecessary collation processing and memory writes.

For example:

SELECT l_shipmode, COUNT(*)
FROM lineitem
GROUP BY l_shipmode;

l_shipmode usually has a very low NDV. If it uses utf8mb4_general_ci, repeatedly generating sort keys for millions of rows can consume a significant portion of the HashAgg execution time.

At the same time, unconditionally caching sort keys is not suitable for high-NDV inputs because maintaining the cache may introduce additional hash lookups and memory usage without enough cache hits. Therefore, the optimization needs to provide substantial benefits for low-NDV workloads while keeping the high-NDV overhead small and bounded.

What is changed and how it works

This PR adds an always-on collation sort-key cache to the batch string-key processing path of HashAgg.

For each input Block, the cache maps a raw string value to its generated collation sort key:

raw string -> collation sort key

When processing a row:

  1. Look up the raw string in the cache.
  2. If it is found, reuse the cached sort key.
  3. If it is not found, generate the sort key once and insert it into the cache.
  4. Use the resulting sort key for the existing hash-table lookup or insertion.

The cache is enabled only when all of the following conditions are satisfied:

  • HashAgg uses the batch string-key processing path.
  • The string key uses a supported case-insensitive collation.
  • The current processing range contains at least 1,024 rows.

To bound the cost for high-NDV inputs, the maximum number of cached distinct values is calculated from the number of rows in the current Block:

cache distinct limit = rows in the Block / 32

For a typical maximum runtime Block containing 65,536 rows, the cache can hold at most 2,048 distinct raw values.

If a cache miss occurs after the distinct-value limit has been reached, the current Block falls back to direct sort-key generation. The fallback state is also recorded in the worker's AggregatedDataVariants, so subsequent Blocks processed by the same aggregation worker do not attempt to initialize the cache again. This worker-level once-fallback behavior prevents high-NDV workloads from repeatedly paying the cache construction and lookup overhead for every Block.

The cache itself remains local to one Block. It only stores references to raw strings owned by that Block, so no input-column memory needs to be retained across Blocks. Only the fallback decision is preserved across Blocks.

The optimization does not change collation or grouping semantics. Hash-table keys are still the sort keys generated by the existing collator; the change only avoids regenerating an identical sort key for repeated raw string values.

The benchmark uses 128 Blocks with 65,536 rows per Block:

Workload Baseline With cache Change
NDV 64 27.16 M rows/s 73.41 M rows/s +170.3%
NDV 2,048 26.05 M rows/s 55.71 M rows/s +113.9%
NDV 2,049, fallback 26.95 M rows/s 26.85 M rows/s -0.4%
NDV 65,536, fallback 24.17 M rows/s 23.99 M rows/s -0.7%
First 64 Blocks low NDV, remaining Blocks high NDV 25.40 M rows/s 35.75 M rows/s +40.8%

The results show substantial improvements for low-NDV and mixed workloads, while the worker-level fallback keeps the high-NDV overhead below 1% in the benchmark.


Check List

Tests

  • Unit test
  • Integration test
  • Manual test (add detailed scripts or steps below)
  • No code

Side effects

  • Performance regression: Consumes more CPU
  • Performance regression: Consumes more Memory
  • Breaking backward compatibility

Documentation

  • Affects user behaviors
  • Contains syntax changes
  • Contains variable changes
  • Contains experimental features
  • Changes MySQL compatibility

Release note

None

Summary by CodeRabbit

Summary by CodeRabbit

  • Performance

    • Improved case-insensitive string aggregation by caching collation sort keys during batch processing when eligible.
    • The cache stops after reaching a distinct-key threshold, then switches to uncached sort-key generation to avoid extra overhead.
  • Bug Fixes

    • Added consistent detection and propagation of “collation key cache fallback” so later processing behaves correctly across batches/workers.
  • Tests

    • Added unit tests covering low/high NDV behavior and verified the fallback transition.
    • Added/updated benchmarks for collation key cache scenarios.

@ti-chi-bot ti-chi-bot Bot added do-not-merge/needs-linked-issue release-note-none Denotes a PR that doesn't merit a release note. labels Jul 14, 2026
@ti-chi-bot

ti-chi-bot Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign flowbehappy for approval. For more information see the Code Review Process.
Please ensure that each of them provides their approval before proceeding.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@ti-chi-bot ti-chi-bot Bot added the size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. label Jul 14, 2026
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7510c825-1d12-4384-a926-a5b36bd4edc7

📥 Commits

Reviewing files that changed from the base of the PR and between 7e54367 and 6321c17.

📒 Files selected for processing (1)
  • dbms/src/Flash/tests/bench_aggregation_hash_map.cpp

📝 Walkthrough

Walkthrough

The PR adds CI collation sort-key caching to string hash aggregation, tracks persistent fallback after cache thresholds are exceeded, wires initialization and status propagation through Aggregator, and adds executor tests plus NDV-focused benchmarks.

Changes

Collation sort-key caching

Layer / File(s) Summary
Cache-enabled string hashing
dbms/src/Common/ColumnsHashing.h, dbms/src/Common/ColumnsHashingImpl.h
String batching caches CI collation sort keys until the distinct-key limit, then switches to direct computation through new base and string-hash APIs.
Aggregation cache lifecycle
dbms/src/Interpreters/Aggregator.h, dbms/src/Interpreters/Aggregator.cpp
Aggregation state records fallback, initializes caching for eligible batches, and propagates fallback status across later blocks.
Behavior validation and performance coverage
dbms/src/Flash/tests/gtest_aggregation_executor.cpp, dbms/src/Flash/tests/bench_aggregation_hash_map.cpp
Tests cover CI equivalence, collation key sizing, NDV behavior, and worker fallback; benchmarks cover low, threshold, high, and transitioning NDV workloads.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Aggregator
  participant HashMethodString
  participant Collator
  participant AggregatedDataVariants
  Aggregator->>AggregatedDataVariants: check fallback state
  Aggregator->>HashMethodString: initialize cache for batch
  HashMethodString->>Collator: generate or reuse sort keys
  HashMethodString-->>Aggregator: return fallback status
  Aggregator->>AggregatedDataVariants: persist fallback status
Loading

Poem

I’m a rabbit with keys in a row,
Reusing the sort signs I know.
When distinct paths grow wide,
I switch cache aside—
And hop through the blocks as they flow.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: improving HashAgg string collation key caching.
Description check ✅ Passed The PR description follows the template and covers the problem, implementation, tests, side effects, and release note.
Linked Issues check ✅ Passed The changes implement the requested sort-key caching and fallback behavior for repeated string keys under case-insensitive collations [10979].
Out of Scope Changes check ✅ Passed All code changes are directly related to HashAgg collation sort-key caching, tests, or benchmarking; no unrelated scope is evident.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
dbms/src/Common/ColumnsHashing.h (1)

102-141: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Reserve the lookup hash map here too.

cached_sort_keys is already reserved for the expected distinct count, but raw_to_sort_key_index still grows from its default capacity and will rehash repeatedly as the cache fills. Reserving it alongside the vector avoids avoidable rehashes.

♻️ Proposed fix
         collation_sort_key_cache_active = true;
         collation_sort_key_cache_distinct_limit = rows / max_collation_sort_key_cache_distinct_ratio;
         cached_sort_keys.reserve(collation_sort_key_cache_distinct_limit + 1);
+        raw_to_sort_key_index.reserve(collation_sort_key_cache_distinct_limit + 1);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dbms/src/Common/ColumnsHashing.h` around lines 102 - 141, Reserve
raw_to_sort_key_index for collation_sort_key_cache_distinct_limit when
initializing the sort-key cache, alongside the existing cached_sort_keys
reservation, so filling the cache does not trigger repeated hash-map rehashes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@dbms/src/Common/ColumnsHashing.h`:
- Around line 102-141: Reserve raw_to_sort_key_index for
collation_sort_key_cache_distinct_limit when initializing the sort-key cache,
alongside the existing cached_sort_keys reservation, so filling the cache does
not trigger repeated hash-map rehashes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 06cf7ea1-eedb-4167-958d-12fb9dbf3da4

📥 Commits

Reviewing files that changed from the base of the PR and between b5093dd and 8eb6ffc.

📒 Files selected for processing (6)
  • dbms/src/Common/ColumnsHashing.h
  • dbms/src/Common/ColumnsHashingImpl.h
  • dbms/src/Flash/tests/bench_aggregation_hash_map.cpp
  • dbms/src/Flash/tests/gtest_aggregation_executor.cpp
  • dbms/src/Interpreters/Aggregator.cpp
  • dbms/src/Interpreters/Aggregator.h

@ChangRui-Ryan

Copy link
Copy Markdown
Contributor Author

/retest

3 similar comments
@ChangRui-Ryan

Copy link
Copy Markdown
Contributor Author

/retest

@ChangRui-Ryan

Copy link
Copy Markdown
Contributor Author

/retest

@ChangRui-Ryan

Copy link
Copy Markdown
Contributor Author

/retest

@ChangRui-Ryan

Copy link
Copy Markdown
Contributor Author

/retest

1 similar comment
@ChangRui-Ryan

Copy link
Copy Markdown
Contributor Author

/retest

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves HashAgg performance for case-insensitive string GROUP BY keys by avoiding repeated collation sort-key generation when raw string values repeat frequently. It introduces a bounded, block-scoped cache in the batch string-key processing path and records a worker-level “once fallback” flag to avoid repeated cache overhead on high-NDV inputs.

Changes:

  • Add a collation sort-key cache for batch string-key hashing when using CI collations, with a distinct-key limit to bound overhead.
  • Persist a worker-level fallback flag (AggregatedDataVariants) to skip cache attempts after a high-NDV fallback is detected.
  • Add unit tests and a micro-benchmark covering semantics, cache sizing behavior, and fallback behavior.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
dbms/src/Interpreters/Aggregator.h Adds worker-level flag to remember collation sort-key cache fallback.
dbms/src/Interpreters/Aggregator.cpp Initializes cache per execution range and propagates fallback to the worker variant.
dbms/src/Common/ColumnsHashingImpl.h Adds no-op cache hooks to the generic hash method base.
dbms/src/Common/ColumnsHashing.h Implements the CI-collation sort-key cache in the batch string-key handler.
dbms/src/Flash/tests/gtest_aggregation_executor.cpp Adds unit tests validating semantics and worker-level once-fallback behavior.
dbms/src/Flash/tests/bench_aggregation_hash_map.cpp Adds benchmarks for low/high NDV and NDV transition workloads for the new cache.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

agg_process_info.resetBlock(*block);
do
{
aggregator->executeOnBlock(agg_process_info, *data_variants, 1);
@ChangRui-Ryan

Copy link
Copy Markdown
Contributor Author

/retest

2 similar comments
@ChangRui-Ryan

Copy link
Copy Markdown
Contributor Author

/retest

@ChangRui-Ryan

Copy link
Copy Markdown
Contributor Author

/retest

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-note-none Denotes a PR that doesn't merit a release note. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Optimize HashAgg by caching collation sort keys for repeated string keys

2 participants