Top 5 High‑Performance Go In‑Memory Cache Libraries for Large Keys and High Concurrency

This article evaluates five Go in‑memory cache libraries—Ristretto, BigCache, FreeCache, FastCache, and groupcache—detailing their support for large keys, high‑throughput LRU‑like eviction, TPS/QPS benchmarks, core APIs, code examples, and practical selection guidance for backend developers.

Nightwalker Tech
Nightwalker Tech
Nightwalker Tech
Top 5 High‑Performance Go In‑Memory Cache Libraries for Large Keys and High Concurrency

Recommended List

Ristretto

Ristretto is an open‑source high‑performance cache from Dgraph that uses a Sampled LFU algorithm combined with TinyLFU admission, offering higher hit rates than classic LRU under real‑world loads. It leverages sync.Pool and lock‑free techniques to stay GC‑friendly in high‑concurrency scenarios.

TPS/QPS Data

Set (write) : can reach millions of TPS on multi‑core CPUs.

Get (read) : achieves tens of millions up to near a hundred million QPS, with minimal lock contention.

Supported Operations

Set(key, value, cost)

Get(key) → (interface{}, bool)

Del(key)

Wait() – waits for asynchronous Set processing

Clear()

Metrics – provides hit‑rate, eviction counts, etc.

Brief Usage Example

package main

import (
    "fmt"
    "github.com/dgraph-io/ristretto"
    "time"
)

func main() {
    cache, err := ristretto.NewCache(&ristretto.Config{
        NumCounters: 1e7, // 10M counters
        MaxCost:     1 << 30, // 1 GB
        BufferItems: 64,
    })
    if err != nil { panic(err) }

    ok := cache.Set("my-large-key", "my-large-value", 1)
    if !ok { fmt.Println("Set failed, maybe due to contention") }
    cache.Wait()
    time.Sleep(10 * time.Millisecond)
    if v, found := cache.Get("my-large-key"); found {
        fmt.Printf("Found value: %s
", v.(string))
    } else {
        fmt.Println("Value not found.")
    }
    cache.Del("my-large-key")
    if _, found := cache.Get("my-large-key"); !found {
        fmt.Println("Value deleted successfully.")
    }
}

BigCache

BigCache is designed for storing massive numbers of entries and handling large keys while avoiding GC overhead by allocating a single large byte slice (or off‑heap memory) and sharding it across hash maps. Its eviction is time‑based (least‑recently‑updated).

TPS/QPS Data

Set : stable performance of hundreds of thousands to over a million TPS on multi‑core machines.

Get : typically millions to tens of millions QPS, with steady P99 latency even under heavy write loads.

Supported Operations

Set(key, value)

Get(key) → ([]byte, error)

Delete(key)

Len()

Reset()

Iterator()

Brief Usage Example

package main

import (
    "fmt"
    "github.com/allegro/bigcache/v3"
    "time"
)

func main() {
    config := bigcache.Config{
        Shards: 1024,
        LifeWindow: 10 * time.Minute,
        CleanWindow: 5 * time.Minute,
        MaxEntriesInWindow: 1000 * 10 * 60,
        MaxEntrySize: 500,
        Verbose: true,
        HardMaxCacheSize: 8192, // MB
    }
    cache, err := bigcache.NewBigCache(config)
    if err != nil { panic(err) }

    key := "my-large-key"
    value := []byte("my-large-value-as-bytes")
    if err = cache.Set(key, value); err != nil {
        fmt.Printf("Error setting value: %v
", err)
    }
    if v, err := cache.Get(key); err == nil {
        fmt.Printf("Found value: %s
", string(v))
    } else {
        fmt.Printf("Value not found or error: %v
", err)
    }
    if err = cache.Delete(key); err == nil {
        fmt.Println("Value deleted successfully.")
    }
    if _, err = cache.Get(key); err != nil {
        fmt.Printf("Could not get deleted value: %v
", err)
    }
}

FreeCache

FreeCache targets zero GC overhead by storing all entries in a pre‑allocated ring buffer (a large byte slice), eliminating GC scans. It uses an approximate LRU eviction algorithm and excels when storing many small objects or large keys, provided total memory is bounded.

TPS/QPS Data

Set : comparable to BigCache, typically hundreds of thousands to a million TPS.

Get : excellent performance, reaching millions to tens of millions QPS, with near‑zero GC pause.

Supported Operations

Set(key, value, expireSeconds)

Get(key) → ([]byte, error)

Del(key) → bool

EntryCount()

Clear()

Iterator

Brief Usage Example

package main

import (
    "fmt"
    "github.com/coocood/freecache"
)

func main() {
    cacheSize := 100 * 1024 * 1024 // 100 MB
    cache := freecache.NewCache(cacheSize)

    key := []byte("my-large-key")
    value := []byte("my-large-value-for-freecache")
    expire := 60 // seconds
    if err := cache.Set(key, value, expire); err != nil { panic(err) }

    if v, err := cache.Get(key); err == nil {
        fmt.Printf("Found value: %s
", string(v))
    } else {
        fmt.Printf("Value not found or error: %v
", err)
    }
    if cache.Del(key) {
        fmt.Println("Value deleted successfully.")
    }
    if _, err := cache.Get(key); err != nil {
        fmt.Printf("Could not get deleted value: %v
", err)
    }
}

FastCache

FastCache, created by the VictoriaMetrics team, focuses on raw speed. It stores data in large byte slices divided into buckets, each bucket using a map for indexing, while values are appended to chunks, minimizing allocations and GC pressure.

TPS/QPS Data

Set : benchmarks show several million TPS.

Get : can reach tens of millions QPS, performing strongly in mixed read/write workloads.

Supported Operations

Set(key, value)

Get(dst, key) – appends the value to dst slice and returns the new slice

Has(key)

Del(key)

Reset()

UpdateStats(s *Stats)

Brief Usage Example

package main

import (
    "fmt"
    "github.com/VictoriaMetrics/fastcache"
)

func main() {
    maxBytes := 100 * 1024 * 1024 // 100 MB
    cache := fastcache.New(maxBytes)

    key := []byte("my-large-key")
    value := []byte("my-large-value-for-fastcache")
    cache.Set(key, value)

    var dst []byte
    if v := cache.Get(dst, key); len(v) > 0 {
        fmt.Printf("Found value: %s
", string(v))
    } else {
        fmt.Println("Value not found.")
    }
    cache.Del(key)
    if !cache.Has(key) {
        fmt.Println("Value deleted successfully.")
    }
}

groupcache

groupcache, authored by members of the Go team, provides a local LRU cache plus a peer‑to‑peer distributed caching framework. It prevents cache‑stampedes by ensuring only one node fetches a cold key while others wait for the result.

TPS/QPS Data

Focuses on distributed coordination rather than raw local speed; local LRU QPS is typically tens of thousands to a few hundred thousand.

Suitable when you need consistent cache across multiple services.

Supported Operations

NewGroup(name, cacheBytes, getter)

Get(ctx, key, dest)

Peer configuration for HTTP communication

Brief Usage Example (single‑node)

package main

import (
    "context"
    "fmt"
    "github.com/golang/groupcache"
    "log"
    "sync"
    "time"
)

var (
    db = map[string][]byte{"key1": []byte("value1 from DB"), "key2": []byte("value2 from DB")}
    dbMutex sync.RWMutex
)

func getter(ctx context.Context, key string, dest groupcache.Sink) error {
    log.Printf("Cache miss! Fetching key '%s' from DB...", key)
    dbMutex.RLock()
    v, ok := db[key]
    dbMutex.RUnlock()
    if !ok { return fmt.Errorf("key not found in DB") }
    return dest.SetBytes(v, time.Now().Add(time.Hour))
}

func main() {
    myGroup := groupcache.NewGroup("my-cache-group", 64<<20, groupcache.GetterFunc(getter))
    ctx := context.Background()
    var data []byte
    fmt.Println("
--- First Get for key1 ---")
    if err := myGroup.Get(ctx, "key1", groupcache.AllocatingByteSliceSink(&data)); err != nil { log.Fatal(err) }
    fmt.Printf("Got data: %s
", data)

    fmt.Println("
--- Second Get for key1 ---")
    if err := myGroup.Get(ctx, "key1", groupcache.AllocatingByteSliceSink(&data)); err != nil { log.Fatal(err) }
    fmt.Printf("Got data (from cache): %s
", data)

    fmt.Println("
--- Get for non-existent key3 ---")
    if err := myGroup.Get(ctx, "key3", groupcache.AllocatingByteSliceSink(&data)); err != nil {
        fmt.Printf("Correctly failed to get non-existent key: %v
", err)
    }
}

Summary and Selection Advice

If maximizing cache hit rate with the most modern eviction algorithm is the primary goal, Ristretto is the top choice.

When handling very large values or massive entry counts while requiring ultra‑stable latency, consider BigCache or FreeCache and benchmark both for your workload.

For raw speed and confidence in VictoriaMetrics engineering, FastCache offers a highly competitive option.

If you need a distributed cache solution rather than a local one, groupcache provides an officially backed, proven approach.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

cacheconcurrencyLRUdistributedMemory
Nightwalker Tech
Written by

Nightwalker Tech

[Nightwalker Tech] is the tech sharing channel of "Nightwalker", focusing on AI and large model technologies, internet architecture design, high‑performance networking, and server‑side development (Golang, Python, Rust, PHP, C/C++).

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.