Cache — Redis, EhCache or Caffeine?
A cache is a reserved storage location that collects temporary data to help websites, browsers, and apps load faster. The data stored in a cache might be the result of an earlier computation or a copy of data stored elsewhere.
A cache hit occurs when the requested data can be found in a cache, while a cache miss occurs when it cannot.
Types of Cache
Redis is an in-memory data structure store, used as a database, cache, and message broker. Redis provides data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs, geospatial indexes, and streams. Redis has built-in replication, Lua scripting, LRU eviction, transactions, and different levels of on-disk persistence, and provides high availability via Redis Sentinel and automatic partitioning with Redis Cluster.
EhCache is a pure Java in-process cache framework. It is fast and compact. It is the default cache provided by Hibernate and MyBatis.(Note: Although EhCache3 supports distributed caching, it is also based on Java process caching)
Caffeine is a rewritten version of the Guava cache using Java8 that replaced Guava in Spring 5 and supports a variety of cache expiration policies. It is a near-optimal cache library known as Best Performance.
Redis vs EhCache:
1. Multilingual projects
You can think of Redis as a shared data structure, while Ehcache is a memory block storing serialized data objects. This is the main difference.
Redis as a shared data structure means you can put some predefined data structure (such as String, List, Set, etc) in one language and retrieve it in another language. This is useful if your project is multilingual, for example, Java on the backend side, and PHP on the front side. You can use Redis for a shared cache. But it can only store a predefined data structure, you cannot insert any Java objects you want.
If your project is only Java, i.e. not multilingual, Ehcache is a convenient solution.
2. Scalability
You will meet issues with EhCache scaling and need resources to manage it during failover and etc. Redis benefits over EhCache:
- It uses a time-proven gossip protocol for Node discovery and synchronization.
- Availability of fully managed services like AWS ElastiCache, Azure Redis Cache. Such services offer full automation, support, and management of Redis, so developers can focus on their applications and not maintaining their databases.
- Correct large memory amount handling (we all know that Redis can manage with hundreds of gigabytes of RAM on a single machine). It doesn’t have problems with Garbage collection like Java.
- The existence of Java Developer friendly Redis client — Redisson, provides many Java friendly objects on top of Redis.
Caffeine vs Ehcache
- From the perspective of supported features, Ehcache supports more features, such as multi-level cache, distributed cache, cache listener, and so on.
- If you just want to simply use a memory cache component and have high-performance requirements, you can give priority to Caffeine, which is better than Ehcache, and it uses a better cache elimination strategy. Caffeine uses the Window TinyLfu eviction policy, which provides a near-optimal hit rate.
Caffeine is higher than ehcache mostly in terms of writing performance.
3. Caffeine has three strategies for value eviction — size-based, time-based, and reference-based.
Ehcache also provides three eviction algorithms to choose from for the MemoryStore
— Least Recently Used (LRU), Least Frequently Used (LFU), and First In First Out (FIFO).
4. Ehcache provides support for memory and disk storage. When the MemoryStore
gets full, elements are evicted. What happens on eviction depends on the cache configuration.
To implement a similar ‘post-eviction strategy for caffeine’, you can create a victim cache to capture recently expired entries from the cache and resurrect from it on a load (say if the DB is down). The victim cache would need its own bounding, e.g. a longer expiration threshold.
Cache<K, V> victimCache = Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTE)
.build();
LoadingCache<K, V> mainCache = Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTE)
.writer(new CacheWriter<K, V>() {
public void write(K key, V value) { /* ignored */ }
public void delete(K key, V value, RemovalCause cause) {
if (cause == RemovalCause.EXPIRED) {
victimCache.put(key, value);
}
})
.build(key -> {
try {
// load from db
} catch (DatabaseAccessException e) {
return victimCache.asMap().remove(key);
}
});
5. Ehcache also supports multiple cache manager instances, as well as multiple cache regions for one instance.
6. Caffeine provides three strategies for cache population — manual, synchronous, and asynchronous loading. On the other hand, Ehcache does not seem to provide asynchronous loading.
7. The API provided by Caffeine is easier to use, while the configuration of Ehcache is slightly more cumbersome.
Redis vs Caffeine:
- Redis is distributed cache, while (like Ehcache) caffeine is in-process or local cache.
- Compared with Redis, caffeine has no network IO consumption.
To optimize the application, caffeine can be used as a first-level cache and Redis as the second-level cache.
Conclusion
There are various caching methods, such as Redis, Caffeine, JCache, EhCache, and so on. But if only one kind of cache is used, it will either consume more network (such as Redis) or occupy too much memory (such as Caffeine, an application memory cache). In many scenarios, more than one cache can be combined to achieve the first and second-level caching, which can greatly improve the processing efficiency of the application.
There are many other types of cache available for your application, and I will be writing more about them as well in my next posts. If my post was helpful, please provide a ‘clap’, or else, please leave a comment to support or help me improve.