From 7b78efb9de4d7014dd4bac7b72856036fb996a98 Mon Sep 17 00:00:00 2001 From: honestmanxin Date: Thu, 27 Aug 2026 20:38:10 +0800 Subject: [PATCH] [opt](point-query) reduce short circuit lookup cache usage --- .../doris/qe/ShortCircuitQueryContext.java | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/ShortCircuitQueryContext.java b/fe/fe-core/src/main/java/org/apache/doris/qe/ShortCircuitQueryContext.java index 2a4235e332db2e..75bf6c41d62ea5 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/qe/ShortCircuitQueryContext.java +++ b/fe/fe-core/src/main/java/org/apache/doris/qe/ShortCircuitQueryContext.java @@ -32,18 +32,30 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.Maps; +import com.google.common.hash.Hasher; +import com.google.common.hash.Hashing; import com.google.protobuf.ByteString; import org.apache.thrift.TException; import org.apache.thrift.TSerializer; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.UUID; +import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; public class ShortCircuitQueryContext { + // Number of buckets used to spread a hot query over multiple Backend + // LookupConnectionCache shards, avoiding single-shard lock contention. + private static final int CACHE_ID_BUCKET_NUM = 128; + + // Round-robin bucket allocator, giving an even distribution across buckets + // regardless of connection id skew. + private static final AtomicLong CACHE_ID_BUCKET_COUNTER = new AtomicLong(0); + // Cached for better CPU performance, since serialize DescriptorTable and // outputExprs are heavy work public final Planner planner; @@ -110,7 +122,7 @@ public ShortCircuitQueryContext(Planner planner, Queriable analzyedQuery) throws TExprList exprList = new TExprList(exprs); serializedOutputExpr = ByteString.copyFrom( new TSerializer().serialize(exprList)); - this.cacheID = UUID.randomUUID(); + this.cacheID = genCacheID(serializedDescTable, serializedOutputExpr, serializedQueryOptions); this.scanNode = olapScanNode; this.tbl = this.scanNode.getOlapTable(); this.tableName = this.scanNode.getTableNameInPlan(); @@ -118,6 +130,22 @@ public ShortCircuitQueryContext(Planner planner, Queriable analzyedQuery) throws this.analzyedQuery = analzyedQuery; } + // Build a 128-bit cache identifier from serialized query structures and a + // round-robin bucket. Identical hot queries are intentionally spread across + // multiple Backend LookupConnectionCache shards to reduce lock contention and + // high sys CPU, while still bounding the number of cache entries. + private static UUID genCacheID(ByteString serializedDescTable, ByteString serializedOutputExpr, + ByteString serializedQueryOptions) { + int bucket = (int) Math.floorMod(CACHE_ID_BUCKET_COUNTER.getAndIncrement(), CACHE_ID_BUCKET_NUM); + Hasher hasher = Hashing.murmur3_128().newHasher(); + hasher.putBytes(serializedDescTable.toByteArray()); + hasher.putBytes(serializedOutputExpr.toByteArray()); + hasher.putBytes(serializedQueryOptions.toByteArray()); + hasher.putInt(bucket); + ByteBuffer buffer = ByteBuffer.wrap(hasher.hash().asBytes()); + return new UUID(buffer.getLong(), buffer.getLong()); + } + @VisibleForTesting ShortCircuitQueryContext(OlapTable tbl, String tableName, int schemaVersion, long fileCacheQueryLimitBytes) {