diff --git a/benchmarks/src/main/java/org/opensearch/common/LongHashBenchmark.java b/benchmarks/src/main/java/org/opensearch/common/LongHashBenchmark.java
new file mode 100644
index 0000000000000..ab17bde3e7507
--- /dev/null
+++ b/benchmarks/src/main/java/org/opensearch/common/LongHashBenchmark.java
@@ -0,0 +1,153 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * The OpenSearch Contributors require contributions made to
+ * this file be licensed under the Apache-2.0 license or a
+ * compatible open source license.
+ */
+
+package org.opensearch.common;
+
+import org.openjdk.jmh.annotations.*;
+import org.openjdk.jmh.infra.Blackhole;
+import org.opensearch.common.settings.Settings;
+import org.opensearch.common.util.BigArrays;
+import org.opensearch.common.util.LongHash;
+import org.opensearch.common.util.LongRHHash;
+import org.opensearch.common.util.PageCacheRecycler;
+
+import java.util.Arrays;
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+
+@Fork(3)
+@Warmup(iterations = 0)
+@Measurement(iterations = 3)
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.MILLISECONDS)
+public class LongHashBenchmark {
+
+    private static final int ITERATIONS = (1 << 24);
+
+    private static final BigArrays BIG_ARRAYS = new BigArrays(
+        new PageCacheRecycler(Settings.EMPTY),
+        null,
+        "REQUEST"
+    );
+
+    @State(Scope.Benchmark)
+    public static class BenchmarkState {
+        @Param({
+            "1", "2", "3", "6", "8", "10", "13", "14", "15", "18", "20", "24", "27", "31", "35", "38", "40", "42", "46",
+            "48", "53", "60", "68", "73", "77", "81", "86", "93", "100", "104", "109", "118", "130", "136", "149", "162",
+            "168", "174", "186", "201", "210", "229", "248", "261", "274", "284", "296", "313", "336", "352", "377",
+            "388", "408", "431", "449", "469", "488", "502", "520", "535", "545", "607", "647", "733", "764", "826",
+            "866", "881", "953", "968", "1017", "1032", "1078", "1132", "1196", "1209", "1263", "1358", "1390", "1444",
+            "1552", "2304", "2466", "3175", "3675", "4102", "4635", "4751", "5469", "6187", "6935", "7072", "7404",
+            "7808", "8336", "9037", "9154", "9844", "9996", "10287"
+        })
+        public int size;
+
+        @Param({"1", "128", "1024"})
+        public long capacity;
+
+        @Param({"0.6"})
+        public float loadFactor;
+
+        public long[] data;
+        public long[] queries;
+
+        // Pre-populated instances to benchmark the 'find' method.
+        public LongHash lh;
+        public LongRHHash rh;
+
+        @Setup(Level.Trial)
+        public void setUp() {
+            data = nyc_taxis();
+            // data = http_logs();
+            // data = random();
+
+            // Fisher-Yates shuffle.
+            // This will avoid hitting the cache lines which would otherwise unfairly favour
+            // the naive linear-probing algorithm.
+            Random random = new Random(0);
+            queries = Arrays.copyOf(data, size);
+            for (int i = size - 1; i > 0; i--) {
+                int j = Math.abs(random.nextInt()) % (i + 1);
+                long temp = queries[i];
+                queries[i] = queries[j];
+                queries[j] = temp;
+            }
+
+            lh = new LongHash(capacity, loadFactor, BIG_ARRAYS);
+            rh = new LongRHHash(capacity, loadFactor, BIG_ARRAYS);
+            for (int i = 0; i < data.length * 2; i++) {
+                lh.add(data[i % data.length]);
+                rh.add(data[i % data.length]);
+            }
+        }
+
+        private long[] nyc_taxis() {
+            long[] data = new long[size];
+            for (int i = 0; i < size; i++) {
+                data[i] = 1420070400000L + 86400000L * i;
+            }
+            return data;
+        }
+
+        private long[] http_logs() {
+            long[] data = new long[size];
+            for (int i = 0; i < size; i++) {
+                data[i] = 893962800000L + 3600000L * i;
+            }
+            return data;
+        }
+
+        private long[] random() {
+            Random random = new Random(0);
+            long[] data = new long[size];
+            for (int i = 0; i < size; i++) {
+                data[i] = random.nextLong();
+            }
+            return data;
+        }
+    }
+
+    /* Benchmarks for the 'add' method. */
+    @Benchmark
+    public void baselineAdd(Blackhole bh, BenchmarkState s) {
+        try (LongHash h = new LongHash(s.capacity, s.loadFactor, BIG_ARRAYS)) {
+            for (int i = 0; i < ITERATIONS; i++) {
+                long key = s.queries[i % s.queries.length];
+                bh.consume(h.add(key));
+            }
+        }
+    }
+
+    @Benchmark
+    public void contenderAdd(Blackhole bh, BenchmarkState s) {
+        try (LongRHHash h = new LongRHHash(s.capacity, s.loadFactor, BIG_ARRAYS)) {
+            for (int i = 0; i < ITERATIONS; i++) {
+                long key = s.queries[i % s.queries.length];
+                bh.consume(h.add(key));
+            }
+        }
+    }
+
+    /* Benchmarks for the 'find' method. */
+    @Benchmark
+    public void baselineFind(Blackhole bh, BenchmarkState s) {
+        for (int i = 0; i < ITERATIONS; i++) {
+            long key = s.queries[i % s.queries.length];
+            bh.consume(s.lh.find(key));
+        }
+    }
+
+    @Benchmark
+    public void contenderFind(Blackhole bh, BenchmarkState s) {
+        for (int i = 0; i < ITERATIONS; i++) {
+            long key = s.queries[i % s.queries.length];
+            bh.consume(s.rh.find(key));
+        }
+    }
+}
diff --git a/server/src/main/java/org/opensearch/common/util/LongRHHash.java b/server/src/main/java/org/opensearch/common/util/LongRHHash.java
new file mode 100644
index 0000000000000..9b827ad6664ef
--- /dev/null
+++ b/server/src/main/java/org/opensearch/common/util/LongRHHash.java
@@ -0,0 +1,153 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * The OpenSearch Contributors require contributions made to
+ * this file be licensed under the Apache-2.0 license or a
+ * compatible open source license.
+ */
+
+package org.opensearch.common.util;
+
+import com.carrotsearch.hppc.BitMixer;
+import org.opensearch.common.lease.Releasable;
+
+
+public class LongRHHash implements Releasable {
+    private final float loadFactor;
+    private final BigArrays bigArrays;
+
+    private long capacity;
+    private long mask;
+    private long size;
+    private long grow;
+
+    private LongArray ords;
+    private LongArray keys;
+
+    public LongRHHash(long capacity, float loadFactor, BigArrays bigArrays) {
+        capacity = (long) (capacity / loadFactor);
+        capacity = Math.max(1, Long.highestOneBit(capacity - 1) << 1); // next power of two
+
+        this.loadFactor = loadFactor;
+        this.bigArrays = bigArrays;
+        this.capacity = capacity;
+        this.mask = capacity - 1;
+        this.size = 0;
+        this.grow = (long) (capacity * loadFactor);
+
+        this.ords = bigArrays.newLongArray(capacity, false);
+        this.ords.fill(0, capacity, -1);
+        this.keys = bigArrays.newLongArray(capacity, false);
+    }
+
+    public long add(final long key) {
+        final long found = find(key);
+        if (found != -1) {
+            return -(1 + found);
+        }
+
+        if (size >= grow) {
+            grow();
+        }
+
+        return set(key, size);
+    }
+
+    private long set(final long key, final long ordinal) {
+        long idx = slot(key), ord = ordinal, psl = 0;
+        long curOrd, curPsl;
+
+        do {
+            if ((curOrd = ords.get(idx)) == -1) {
+                ords.set(idx, ord);
+                keys = bigArrays.grow(keys, size + 1);
+                keys.set(ordinal, key);
+                return size++;
+            } else if ((curPsl = psl(keys.get(curOrd), idx)) < psl) {
+                ord = ords.set(idx, ord);
+                psl = curPsl;
+            }
+            idx = (idx + 1) & mask;
+            psl++;
+        } while (true);
+    }
+
+    public long get(final long ordinal) {
+        return keys.get(ordinal);
+    }
+
+    public long find(final long key) {
+        for (long idx = slot(key);; idx = (idx + 1) & mask) {
+            final long ord = ords.get(idx);
+            if (ord == -1 || keys.get(ord) == key) {
+                return ord;
+            }
+        }
+    }
+
+    private long slot(final long key) {
+        return BitMixer.mix64(key) & mask;
+    }
+
+    private long psl(final long key, final long idx) {
+        return (capacity + idx - slot(key)) & mask;
+    }
+
+    public long size() {
+        return size;
+    }
+
+    public long maxPsl() {
+        long maxPsl = 0;
+
+        for (long idx = 0; idx < capacity; idx++) {
+            long ordinal = ords.get(idx);
+            if (ordinal == -1) {
+                continue;
+            }
+
+            long key = keys.get(ordinal);
+            maxPsl = Math.max(maxPsl, psl(key, idx));
+        }
+
+        return maxPsl;
+    }
+
+    public double avgPsl() {
+        long pslSum = 0;
+
+        for (long idx = 0; idx < capacity; idx++) {
+            long ordinal = ords.get(idx);
+            if (ordinal == -1) {
+                continue;
+            }
+
+            long key = keys.get(ordinal);
+            pslSum += psl(key, idx);
+        }
+
+        return (double) pslSum / size;
+    }
+
+    private void grow() {
+        final long oldSize = size;
+
+        capacity <<= 1;
+        mask = capacity - 1;
+        size = 0;
+        grow = (long) (capacity * loadFactor);
+
+        ords = bigArrays.resize(ords, capacity);
+        ords.fill(0, capacity, -1);
+
+        for (long ordinal = 0; ordinal < oldSize; ordinal++) {
+            set(keys.get(ordinal), ordinal);
+        }
+    }
+
+    @Override
+    public void close() {
+        ords.close();
+        keys.close();
+    }
+}
diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/terms/LongKeyedBucketOrds.java b/server/src/main/java/org/opensearch/search/aggregations/bucket/terms/LongKeyedBucketOrds.java
index bcf77ee194ea4..a357ddcd0e3e6 100644
--- a/server/src/main/java/org/opensearch/search/aggregations/bucket/terms/LongKeyedBucketOrds.java
+++ b/server/src/main/java/org/opensearch/search/aggregations/bucket/terms/LongKeyedBucketOrds.java
@@ -34,8 +34,8 @@
 
 import org.opensearch.common.lease.Releasable;
 import org.opensearch.common.util.BigArrays;
-import org.opensearch.common.util.LongHash;
 import org.opensearch.common.util.LongLongHash;
+import org.opensearch.common.util.LongRHHash;
 import org.opensearch.search.aggregations.CardinalityUpperBound;
 
 /**
@@ -148,10 +148,10 @@ public long value() {
      * @opensearch.internal
      */
     public static class FromSingle extends LongKeyedBucketOrds {
-        private final LongHash ords;
+        private final LongRHHash ords;
 
         public FromSingle(BigArrays bigArrays) {
-            ords = new LongHash(1, bigArrays);
+            ords = new LongRHHash(1, 0.6f, bigArrays);
         }
 
         @Override
diff --git a/server/src/test/java/org/opensearch/common/util/LongRHHashTests.java b/server/src/test/java/org/opensearch/common/util/LongRHHashTests.java
new file mode 100644
index 0000000000000..46675ad5da645
--- /dev/null
+++ b/server/src/test/java/org/opensearch/common/util/LongRHHashTests.java
@@ -0,0 +1,63 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * The OpenSearch Contributors require contributions made to
+ * this file be licensed under the Apache-2.0 license or a
+ * compatible open source license.
+ */
+
+package org.opensearch.common.util;
+
+import org.opensearch.test.OpenSearchTestCase;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+public class LongRHHashTests extends OpenSearchTestCase {
+
+    public void testFuzzy() {
+        Map<Long, Long> reference = new TreeMap<>();
+        LongRHHash h = new LongRHHash(1, 0.6f, BigArrays.NON_RECYCLING_INSTANCE);
+
+        for (int i = 0; i < (1 << 20); i++) {
+            long key = randomLong() % (1 << 12);
+            if (reference.containsKey(key)) {
+                long expectedOrdinal = reference.get(key);
+                assertEquals(-1-expectedOrdinal, h.add(key));
+                assertEquals(expectedOrdinal, h.find(key));
+            } else {
+                assertEquals(-1, h.find(key));
+                reference.put(key, (long) reference.size());
+                assertEquals((long) reference.get(key), h.add(key));
+            }
+        }
+
+        h.close();
+    }
+
+    public void testReport() {
+        int[] sizes = new int[]{
+            1, 2, 3, 6, 8, 10, 13, 14, 15, 18, 20, 24, 27, 31, 35, 38, 40, 42, 46,
+            48, 53, 60, 68, 73, 77, 81, 86, 93, 100, 104, 109, 118, 130, 136, 149, 162,
+            168, 174, 186, 201, 210, 229, 248, 261, 274, 284, 296, 313, 336, 352, 377,
+            388, 408, 431, 449, 469, 488, 502, 520, 535, 545, 607, 647, 733, 764, 826,
+            866, 881, 953, 968, 1017, 1032, 1078, 1132, 1196, 1209, 1263, 1358, 1390, 1444,
+            1552, 2304, 2466, 3175, 3675, 4102, 4635, 4751, 5469, 6187, 6935, 7072, 7404,
+            7808, 8336, 9037, 9154, 9844, 9996, 10287
+        };
+
+        for (int size : sizes) {
+            try (LongRHHash h = new LongRHHash(1, 0.6f, BigArrays.NON_RECYCLING_INSTANCE)) {
+                for (int i = 0; i < size; i++) {
+                    long key = 1420070400000L + 86400000L * i;
+                    h.add(key);
+                }
+                for (int i = 0; i < size; i++) {
+                    long key = 1420070400000L + 86400000L * i;
+                    h.add(key);
+                }
+                System.out.println("size: " + size + ", max_psl: " + h.maxPsl() + ", avg_psl: " + h.avgPsl());
+            }
+        }
+    }
+}