
Caching in Golang is a powerful technique to improve the performance and scalability of your applications. It allows you to store frequently accessed data in memory, reducing the need to retrieve it from slower storage systems.
Golang has a built-in `sync` package that provides a simple and efficient way to implement caching. This package includes a `Mutex` type that can be used to synchronize access to shared resources.
The key to effective caching is to identify the data that is most frequently accessed and store it in a cache layer. This can be done using a caching library such as Go Cache, which provides a simple and intuitive API for caching data.
A cache hit occurs when the requested data is found in the cache, while a cache miss occurs when the data is not found in the cache. The cache hit ratio is a measure of the effectiveness of the caching strategy.
Worth a look: Create a Package in Golang
Built-In Caching
Golang has a built-in caching mechanism that can be used to store frequently accessed data. This is achieved through the use of goroutines and channels.
The cache is implemented using a simple in-memory store, which can be useful for small-scale applications. It's not suitable for large-scale applications due to its limited capacity.
To use the built-in caching mechanism, you can create a cache instance and use the Get and Set methods to store and retrieve data. This approach can be used in conjunction with goroutines to improve performance.
Built In Stores
Built-In Stores offer a range of caching options to choose from.
Memory caching is a popular choice, with options like bigcache, ristretto, and go-cache available. These libraries provide efficient in-memory caching solutions.
The bigcache library, for example, is designed to store data in memory, making it a fast and reliable choice. It's also highly customizable.
Other options like memcache and Redis are also built-in, providing a range of caching solutions to suit different needs.
Here's a quick rundown of some popular built-in caching libraries:
- Memory (bigcache) (allegro/bigcache)
- Memory (ristretto) (dgraph-io/ristretto)
- Memory (go-cache) (patrickmn/go-cache)
- Memcache (bradfitz/memcache)
- Redis (go-redis/redis)
- Redis (rueidis) (redis/rueidis)
- Freecache (coocood/freecache)
- Hazelcast (hazelcast-go-client/hazelcast)
These libraries are all well-established and widely used, making them a great starting point for any caching project.
Go Map with Mutex
Using a Go map with a sync.Mutex can be a simple approach to caching, but it has its downsides. This method can lead to contention among goroutines, causing them to block upon a single lock.
Go maps with sync.Mutex are not memory bounded, meaning they can consume all available memory, causing an Out of Memory (OOM) crash.
This approach also doesn't account for the amount of memory usage, making it difficult to implement a memory-bounded cache.
In practice, I've found that this method can be slow and inefficient, especially when dealing with a large number of goroutines.
Intriguing read: Golang vs Go
Installation and Setup
To get started with golang caching, you'll need to import the library in your project. This is done by adding the necessary import statements.
You can use the following import statement to begin working with the latest version of gocache: import "github.com/my/project/gocache".
Then, import the store(s) you want to use from the available ones. This will vary depending on your specific needs and the stores available in the gocache library.
A different take: Does Incognito Mode Use Cache
Installation

To install the latest version of gocache, you can import the library in your project by running a simple import statement.
You'll also need to import the store(s) you want to use from the available ones, which will allow you to access its features.
If you're new to gocache, start with the basic import statement and then add the specific store imports as needed.
To keep your project organized, be sure to run go mod tidy to clean your go.mod file if you encounter any errors.
See what others are reading: Golang Go
Requirements
To ensure a smooth installation and setup process, it's essential to meet the requirements for the cache.
The cache must be concurrent, allowing multiple operations to occur simultaneously without interfering with each other.
Here are the key requirements for the cache:
- Concurrent.
- Memory-bounded (limit to configurable max memory usage).
- Scale well as the number of cores and goroutines increase.
- Scale well under non-random key access distribution (e.g. Zipf).
- High cache hit ratio
These requirements will help you achieve a high-performance cache that can handle your application's needs.
A Loadable
A Loadable cache is a game-changer for data retrieval. It has a load function that acts as a callable function, setting your data back in your cache if it's not available.
Check this out: Golang Reflect to Call Function in Package

This cache is particularly useful when you need to access data from multiple sources. It allows you to pass a Chain cache into the Loadable one, so if your data is not available in all caches, it will bring it back in all caches.
With a Loadable cache, you can ensure that your data is always accessible, even when it's not stored in the cache. This can save you a lot of time and effort in the long run.
Explore further: Are Caches on Computer Important
Free
FreeCache is a high-performance Golang cache library that provides efficient memory allocation and supports item expiration.
It uses a TinyLFU-based admission policy for better cache hit rates, which means it's optimized to reduce garbage collection pressure.
FreeCache divides the cache into 256 segments, each containing 256 slots and a ring buffer to store the data.
This division helps reduce the search space when looking for a key in the cache, making it more efficient.

Here are the key features of FreeCache:
- Supports item expiration.
- Optimized to reduce garbage collection pressure.
- Uses TinyLFU-based admission policy for better cache hit rates.
- Efficient and scales well with large datasets.
- More complex configuration than simpler caches like go-cache.
To find an entry in the cache, FreeCache performs a binary search in the sorted array in the corresponding slot.
This approach is more efficient than a linear search, especially for large datasets.
Discover more: Golang Search
Caching Libraries
In Go, there are several caching libraries to choose from, each with its own strengths and weaknesses.
For high-throughput applications, libraries like BigCache, FreeCache, or Ristretto are better choices due to their performance.
go-cache is the simplest caching library and is best suited for small-scale applications, making it a great choice for ease of use.
Here's a comparison of some popular caching libraries in Go:
By considering these factors and choosing the right caching library, you can optimize your application's performance and reduce the load on the garbage collector.
Selecting the Best Library
Choosing the right caching library can be a daunting task, especially with so many options available. BigCache, FreeCache, or Ristretto are better choices for high-throughput applications.
Performance is crucial for these types of applications. BigCache and Ristretto are optimized for concurrent access, making them ideal for large-scale caching.
If you need item expiration, go-cache and FreeCache provide built-in support. Expiration management is a key feature to consider when selecting a caching library.
Here's a breakdown of the key features to consider:
In conclusion, the choice of caching library depends on your specific needs. Consider the performance, expiration management, and concurrency requirements of your application when making your decision.
Marshaler Wrapper
When working with certain caches like Redis, you may encounter the issue of having to marshal or unmarshal your structs to store and retrieve objects. Some caches store and return values as strings, requiring this extra step.
This is where a marshaler wrapper comes in, a service that wraps your cache and handles the marshaling and unmarshaling for you. It's a game-changer for simplifying the caching process.
To use a marshaler wrapper, you simply need to specify the struct you want your value to be un-marshalled into as a second argument when calling the .Get() method. This makes it easy to retrieve cached objects without having to worry about the underlying details.
Caching Features
The LRU cache eviction model in GoLang is based on a periodic schedule, where cache entries are evicted after their expiration time has passed.
Individual cache entries tend to survive around (expirationTime + (evictionInterval / 2)), making it a reliable model for caching.
The cache eviction interval should be set to at least 1 second to ensure consistent performance.
Explicit expiration times can be set for individual cache entries using the SetWithExpiration function, allowing for more fine-grained control over cache lifetimes.
For your interest: Golang Set Env Variable
Available Features
The cache has a time-based eviction model, where individual cache entries are evicted after their expiration time has passed.
Cache eviction is done on a periodic basis, with a frequency specified by the evictionInterval, which should be at least 1 second.
The cache also has an LRU (Least Recently Used) eviction policy, where adding a new item will displace the item that has been referenced least recently when the cache is full.
The defaultExpiration specifies the minimum amount of time a cached entry remains in the cache before eviction, and this value is used with the Set function.
Explicit per-entry expiration times can be set with the SetWithExpiration function instead of relying on the default expiration time.
Cache entries tend to survive around (expirationTime + (evictionInterval / 2)) due to the periodic nature of eviction.
Func NewTTLWithCallback
The NewTTLWithCallback function creates a new cache with a time-based eviction model that will invoke the supplied callback on all evictions.
This function is useful for situations where you need to implement a cache that automatically removes items after a certain period of time, and you want to perform some action when an item is evicted.
The callback function will be invoked on all evictions, allowing you to take specific actions when items are removed from the cache. See also: NewTTL.
Pros and Cons
Caching Features have several pros and cons.
One of the main advantages is improved page load times, which can be reduced by up to 90% with proper caching.
Caching also helps reduce server load, as frequently accessed pages are stored locally on the user's device.
This can be particularly beneficial for websites with high traffic, as it prevents server overload and downtime.
However, caching can also lead to stale content, where users access outdated information.
To mitigate this, caching features often include automatic cache invalidation, which removes expired content.
Additionally, caching can make it difficult to track website analytics, as cached pages may not reflect the most up-to-date visitor data.
Intriguing read: Windows Azure Caching
Caching Strategies
Cache eviction is done on a periodic basis, and individual cache entries are evicted after their expiration time has passed.
The periodic nature of eviction means that cache entries tend to survive around (expirationTime + (evictionInterval / 2)). This can help you understand how long cache entries will last in your application.
In addition to periodic eviction, cache entries are also evicted when the cache is full, and a new item is added. This means that the item that has been referenced least recently will be displaced.
Lock Stripping with Go Maps

Lock stripping with Go maps is a technique that splits keys using a fingerprint into many smaller Go map shards protected by mutex locks. This approach can lead to contention, particularly when setting the number of shards to exceed the number of threads in the program (> GOMAXPROCS).
Using lock striping alone does not solve the problem of contention, as the most frequently accessed keys are still under a few locks, causing contention for all goroutines. This is especially true when access patterns are bounded by Zipf's law, where a few keys are accessed much more frequently than others.
The performance of lock striping with Go maps does not scale well with the number of cores. In fact, Go is slow to release memory back to the OS but fast to request it, leading to a spike in memory usage and a rapid OOM crash when trying to release memory from a shard.
If this caught your attention, see: Nextjs Memory Cache
Customize Your Own
You can write your own custom cache by implementing the CacheInterface, which allows you to mix your own caches with the ones available in the library.
To create a custom cache, you need to respect the CacheInterface, which includes the Get and Set methods.
This means you can implement your own proprietary cache logic if needed.
You can also implement a custom cache key generator by implementing the interface that generates a custom cache key.
Here are some key takeaways to keep in mind when customizing your own cache:
- Caches available in the library implement the CacheInterface.
- You can mix your own caches with the ones available in the library.
By chaining caches, you can create a fallback system where data is stored in multiple caches, and if one cache doesn't have the data, it will be retrieved from the next cache in the chain.
For example, you can chain a cache in memory with Ristretto store, and then in Redis as a fallback.
In a write-only workload, all libraries seem to perform similarly, although FreeCache performs slightly better than the other two.
Read-Write (75% Reads, 25% Writes)
In a mixed workload with 75% reads and 25% writes, BigCache is the only library that clearly scales well. BigCache handles this type of workload effectively.
BigCache's performance is notable in this scenario. For a workload with 25% writes and 75% reads, BigCache's hit ratios are bad for a Zipf workload, which is explained in the next section.
Striped LRU
Striped LRU is a cache implementation that involves dividing the cache into multiple shards, each with its own LRU eviction policy. This approach can be seen as an incremental improvement over a single LRU cache, but it may not scale well.
From our experiments with striped map shards, we know that it would only be an incremental improvement and would not scale well. This suggests that while Striped LRU may be beneficial in some cases, it's not a silver bullet for all caching needs.
The basic LRU cache implementation in Go's groupcache is a good example of a cache that didn't quite work as expected. Despite its limitations, it did solve some immediate issues around memory spikes, but it introduced contention that ultimately caused more problems than it solved.
Every read is a write in this implementation, which updates the relative positioning of the element in the recency linked list. This means that all accesses wait on a single mutex lock, leading to severe contention.
Performance and Statistics
To get a better understanding of how a cache is performing, you can use a metric cache to retrieve cache statistics. This cache will record metrics depending on the metric provider you pass to it.
Using a Prometheus provider, for instance, will give you a wealth of information about the cache's performance. The Stats type returns usage statistics about an individual cache, which can help you assess its efficiency.
The values returned in this struct are approximations of the current state of the cache, so keep that in mind when making decisions based on the data. For the sake of efficiency, certain edge cases in the implementation can lead to inaccuracies.
Metric for Statistics Retrieval
To retrieve cache statistics, you can use a metric cache that records metrics based on a metric provider.
A Prometheus provider is a good example of this, allowing you to retrieve cache statistics in a structured format.

The Stats type returns usage statistics about an individual cache, which is useful for assessing the efficiency of a cache.
These statistics are approximations of the current state of the cache, and certain edge cases in the implementation can lead to inaccuracies.
The values returned by the Stats type are approximations, not exact measurements, which is a trade-off for efficiency.
Performance Comparison
To evaluate the performance of different cache libraries, we can look at the results of a study that compared three cache libraries using a Zipf-distributed workload.
The study used a machine with 32 high-performance CPUs to run benchmarks, ensuring a robust testing environment.
A Zipf-distributed workload was generated to simulate real-world usage patterns, providing a reliable basis for comparison.
The results showed a significant difference in performance between the three cache libraries for a read-only workload.
For a read-only workload, the cache libraries demonstrated varying levels of efficiency, with some performing better than others.
Worth a look: Golang Read Json File
LRU and Expiration
LRU (Least Recently Used) is a cache eviction policy that removes the item that has been referenced least recently when the cache is full.
This policy is used in combination with a time-based eviction model, which evicts cache entries after their expiration time has passed.
The expiration time is the default minimum amount of time a cached entry remains in the cache before eviction, which can be set with the Set function.
Lru
The LRU cache is a type of cache that uses a Least Recently Used (LRU) eviction policy to remove the least recently accessed items.
LRU caches use a periodic eviction model, where cache entries are evicted after their expiration time has passed. This means that cache entries tend to survive around (expirationTime + (evictionInterval / 2)).
The LRU cache in Go has a basic implementation as part of groupcache, but it introduces contention due to the use of a single mutex lock for all accesses.
In an LRU cache, every read is a write that updates the relative positioning of the element in the recency linked list, resulting in all accesses waiting on a single mutex lock.
The default expiration time for a cached entry in an LRU cache is specified by the defaultExpiration parameter, which is used with the Set function.
LRU caches can also use explicit per-entry expiration times, which are set with the SetWithExpiration function instead of using the default expiration time.
The eviction interval in an LRU cache specifies the frequency at which eviction activities take place, and it should be greater than or equal to 1 second.
A striped LRU cache, while not implemented in practice, would likely only provide an incremental improvement over a standard LRU cache and would not scale well.
Type Expiring
ExpiringCache is a cache with entries that are evicted over time.
This cache type is perfect for situations where you want to automatically remove old or unused data.

Entries in an ExpiringCache are evicted based on their expiration time, which can be set explicitly with the SetWithExpiration function.
The cache also has a periodic nature of eviction, which means that entries tend to survive around (expirationTime + (evictionInterval / 2)).
This can be a useful feature if you want to ensure that certain entries are retained for a minimum amount of time.
The eviction interval should be set to at least 1 second, and can be adjusted to suit your specific needs.
Don't forget to set a default expiration time with the Set function, or use the SetWithExpiration function to set an explicit expiration time for each entry.
Caching Best Practices
For high-throughput applications, consider BigCache, FreeCache, or Ristretto for optimal performance. These libraries are designed to handle large volumes of data and provide fast caching.
If you need item expiration, go-cache and FreeCache are good choices, as they provide built-in support for expiration management. This feature is particularly useful for applications that require cache items to be removed after a certain period.
To ensure your caching library can handle concurrent access, look for libraries that are optimized for concurrency, such as BigCache, Ristretto, or FreeCache. These libraries are designed to handle multiple requests simultaneously, reducing the risk of cache inconsistencies.
Here are some caching library features to consider:
Key Takeaways
When choosing a caching library in Golang, it's essential to consider the specific needs of your application.
Different libraries serve different purposes - some focus on simplicity, while others prioritize high performance.
Go-cache is a great option for smaller projects that don't require complex caching logic.
BigCache and Ristretto are better suited for high-throughput applications that demand maximum performance.
Expiration support varies across libraries - go-cache and FreeCache have built-in expiration, but BigCache requires manual handling.
Here's a summary of the key takeaways:
By considering these factors, you can choose the best caching library for your Golang application and improve its performance and efficiency.
Using Tags
Using tags is a great way to invalidate specific items in your cache later on. You can attach some tags to the items you create, making it easy to target them for invalidation.
Tags are stored using the same storage you choose for your cache, which means you don't have to worry about managing separate storage for your tags.
In fact, you can mix tags with expiration times on your caches to have a fine-tuned control on how your data are cached. This allows you to get the most out of your caching strategy.
A different take: Golang Azure Blob Storage
Frequently Asked Questions
Can I use Redis as a cache?
Yes, Redis can be used as a cache, and implementing caching patterns like proactive and reactive approaches can help you achieve your caching and application objectives.
Is go cache thread-safe?
Yes, the go cache is thread-safe, allowing multiple goroutines to safely use it. This is due to its design as a thread-safe map.
Featured Images: pexels.com


