preface
With the rapid development of the Internet, more and more websites and apps appear on the market. We judge whether a software is easy to use, and user experience is an important measure. For example, we often use wechat, it takes more than ten seconds to open a page, and it takes a few minutes for the other party to receive a voice. I believe that such software is certainly not willing to use. In order to achieve good user experience and fast response, caching is an essential artifact. There are two types of cache: in-process cache and distributed cache: distributed cache, such as redis and memcached, and local (in-process) cache, such as ehcache, GuavaCache and Caffeine. Speaking of Guava Cache, many people are familiar with it. It is a very convenient and easy-to-use localized cache implementation in Google Guava toolkit. Based on LRU algorithm, it supports a variety of cache expiration strategies. Due to the extensive use of Guava, Guava Cache has also been widely used. However, is the performance of Guava Cache necessarily the best? Maybe its performance was very good. The so-called Yangtze River back wave pushes the front wave, and the front wave is photographed on the beach. Let's introduce a caching framework with higher performance than Guava Cache: Caffeine.
Tips: Spring5 (SpringBoot2) began to replace guava with Caffeine. See official information SPR-13797 for details
https://jira.spring.io/browse/SPR-13797
Official performance comparison
The following tests are based on jmh tests, Official website address
Why is the test based on jmh, You can refer to Zhihu's answer
When running microbenchmark on HotSpot VM, remember not to run cycle timing in main(). This is a typical mistake. Repeat the important things three times: JMH, JMH and JMH. Unless you know the implementation details of HotSpot very well, the result of running cycle timing in main doesn't mean anything to ordinary programmers, because it can't be explained.
- 8 threads read, 100% read operation
- Six threads read, two threads write, that is 75% of read operations, 25% of write operations.
- 8 threads write, 100% write operation
Comparative conclusion
It can be seen from the data that the performance of Caffeine is better than that of Guava. Then the operation function of Caffeine's API is basically consistent with that of Guava. In order to be compatible with the users of Guava before, Caffeine has made a Guava Adapter for you to use, which is also very considerate.
How to use
- At pom.xml Add caffeine dependency to
<!-- https://mvnrepository.com/artifact/com.github.ben-manes.caffeine/caffeine --> <dependency> <groupId>com.github.ben-manes.caffeine</groupId> <artifactId>caffeine</artifactId> <version>2.8.2</version> </dependency>
create object
- At pom.xml Add caffeine dependency to
Cache<String, Object> cache = Caffeine.newBuilder() .initialCapacity(100)//initial size .maximumSize(200)//Maximum quantity .expireAfterWrite(3, TimeUnit.SECONDS)//Expiration time .build();
Introduction to creating parameters
- initialCapacity: initial cache space size
- maximumSize: maximum number of caches
- maximumWeight: the maximum weight of the cache
- expireAfterAccess: expires after the specified time after the last read or write operation
- expireAfterWrite: expires after the specified time after the last write operation
- refreshAfterWrite: refresh the cache at a specified time interval after the cache is created or last updated
- weakKeys: open weak reference of key
- weakValues: open a weak reference to value
- softValues: open the soft reference of value
- recordStats: developing statistical functions
be careful:
When both expireAfterWrite and expireAfterAccess exist, the expireAfterWrite shall prevail. maximumSize and maximumWeight cannot be used at the same time.
Add data
Caffeine provides us with manual, synchronous and asynchronous population strategies.
Now let's demonstrate the manual filling strategy. If you are interested, you can go to Official website
Cache<String, String> cache = Caffeine.newBuilder() .build(); cache.put("java finance", "java finance"); System.out.println(cache.getIfPresent("java finance"));
Auto add (custom add function)
public static void main(String[] args) { Cache<String, String> cache = Caffeine.newBuilder() .build(); // 1. If it can be found in the cache, it will return directly // 2. If it cannot be found, get the data from our custom getValue method and add it to the cache String val = cache.get("java finance", k -> getValue(k)); System.out.println(val); } /** * If it is not found in the cache, it will enter this method. Generally, the content is obtained from the database * @param k * @return */ private static String getValue(String k) { return k + ":value"; }
Expiration Policies
Caffeine provided us with Three expiration strategies
, which are size based, time-based and reference based
Size based
LoadingCache<String, String> cache = Caffeine.newBuilder() // Maximum capacity is 1 .maximumSize(1) .build(k->getValue(k)); cache.put("java Finance 1","java Finance 1"); cache.put("java Finance 2","java Finance 2"); cache.put("java Finance 3","java Finance 3"); cache.cleanUp(); System.out.println(cache.getIfPresent("java Finance 1")); System.out.println(cache.getIfPresent("java Finance 2")); System.out.println(cache.getIfPresent("java Finance 3"));
The operation result is as follows: only one is left after two are eliminated.
null null java Finance 3
Time based
Caffeine provides three timing expulsion strategies:
expireAfterWrite(long, TimeUnit)
- Timing starts after the last write to the cache and expires after the specified time.
LoadingCache<String, String> cache = Caffeine.newBuilder() // Maximum capacity is 1 .maximumSize(1) .expireAfterWrite(3, TimeUnit.SECONDS) .build(k->getValue(k)); cache.put("java finance","java finance"); Thread.sleep(1*1000); System.out.println(cache.getIfPresent("java finance")); Thread.sleep(1*1000); System.out.println(cache.getIfPresent("java finance")); Thread.sleep(1*1000); System.out.println(cache.getIfPresent("java finance"));
The value is empty in the third second of the running result.
java finance java finance null
expireAfterAccess
- Starts timing after the last read or write and expires after the specified time. If there is always a request to access the key, the cache will never expire.
LoadingCache<String, String> cache = Caffeine.newBuilder() // Maximum capacity is 1 .maximumSize(1) .expireAfterAccess(3, TimeUnit.SECONDS) .build(k->getValue(k)); cache.put("java finance","java finance"); Thread.sleep(1*1000); System.out.println(cache.getIfPresent("java finance")); Thread.sleep(1*1000); System.out.println(cache.getIfPresent("java finance")); Thread.sleep(1*1000); System.out.println(cache.getIfPresent("java finance")); Thread.sleep(3001); System.out.println(cache.getIfPresent("java finance"));
Run result: when there is no read or write, it will expire in 3 seconds, and then null will be output.
java finance java finance java finance null
expireAfter(Expiry)
- You need to implement the Expiry interface in the expireAfter. This interface supports expireAfterCreate,expireAfterUpdate, and how long the expireAfterRead expires. Note that this is mutually exclusive with expireAfterAccess and expireAfterAccess. Different from expireafteraccess and expireafteraccess, you need to tell the cache framework that it should expire at a specific time to get the specific expiration time.
LoadingCache<String, String> cache = Caffeine.newBuilder() // Maximum capacity is 1 .maximumSize(1) .removalListener((key, value, cause) -> System.out.println("key:" + key + ",value:" + value + ",Delete reason:" + cause)) .expireAfter(new Expiry<String, String>() { @Override public long expireAfterCreate(@NonNull String key, @NonNull String value, long currentTime) { return currentTime; } @Override public long expireAfterUpdate(@NonNull String key, @NonNull String value, long currentTime, @NonNegative long currentDuration) { return currentTime; } @Override public long expireAfterRead(@NonNull String key, @NonNull String value, long currentTime, @NonNegative long currentDuration) { return currentTime; } }) .build(k -> getValue(k));
delete
- Single delete: Cache.invalidate(key)
- Bulk delete: Cache.invalidateAll(keys)
- Delete all cached items: Cache.invalidateAll
summary
This article is just an introduction to the simple use of Caffeine. There are many good things about Caffeine, such as cache monitoring, event monitoring, and W-TinyLFU algorithm (high hit rate and low memory occupation). Students interested in Caffeine can go to the official website to check it.
end
- Due to their lack of talent and learning, there will inevitably be mistakes. If you find the wrong place, please leave a message for me to point out, and I will correct it.
- If you think the article is not bad, your forwarding, sharing, appreciation, likes and comments are my biggest encouragement.
- Thank you for reading, welcome and thank you for your attention.
reference resources
https://www.itcodemonkey.com/article/9498.html
https://juejin.im/post/5dede1f2518825121f699339
https://www.cnblogs.com/CrankZ/p/10889859.html
https://blog.csdn.net/hy245120020/article/details/78080686
https://github.com/ben-manes/caffeine