informasi kontak saya
Surat[email protected]
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Di dunia Java, teknologi caching banyak digunakan untuk meningkatkan kinerja aplikasi dan dibagi menjadi dua kategori: caching jarak jauh dan caching lokal. Caching jarak jauh, dengan kinerja dan fleksibilitasnya yang luar biasa, sering kali diterapkan melalui solusi populer seperti Redis dan Memcached. Cache lokal, dengan karakteristik aksesnya yang ringan dan cepat, diwakili oleh teknologi seperti HashMap, Guava Cache, Caffeine, dan Ehcache.
Kita akan menyelidiki misteri cache jarak jauh di postingan blog mendatang, namun hari ini, mari kita fokus pada cache lokal. Artikel ini pertama-tama akan membawa Anda mempelajari teknologi caching lokal untuk memberi Anda gambaran umum yang komprehensif. Selanjutnya, kita akan mempelajari teknologi caching, yang dikenal sebagai raja kinerja, dan mengeksplorasi prinsip serta metode implementasi di baliknya. Terakhir, melalui serangkaian kasus praktis, kami akan mendemonstrasikan cara memanfaatkan teknologi caching lokal berkinerja tinggi ini secara efektif dalam pekerjaan sehari-hari untuk meningkatkan efisiensi pengembangan dan kinerja aplikasi Anda.
Dengan menggunakan implementasi Map yang mendasarinya, kita dapat menyimpan objek yang akan di-cache langsung di memori, yang merupakan metode langsung dan efisien.
Keuntungan: Metode ini sederhana dan langsung, tanpa bergantung pada perpustakaan eksternal, dan sangat cocok untuk aplikasi dengan persyaratan caching yang tidak rumit dan skenario yang sederhana.
Kekurangan: Namun, metode ini tidak memiliki mekanisme penghapusan cache otomatis. Jika strategi caching yang lebih canggih perlu diterapkan, mungkin diperlukan biaya pengembangan khusus yang lebih tinggi.
- public class LRUCache extends LinkedHashMap {
-
- /**
- * 可重入读写锁,保证并发读写安全性
- */
- private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
- private Lock readLock = readWriteLock.readLock();
- private Lock writeLock = readWriteLock.writeLock();
-
- /**
- * 缓存大小限制
- */
- private int maxSize;
-
- public LRUCache(int maxSize) {
- super(maxSize + 1, 1.0f, true);
- this.maxSize = maxSize;
- }
-
- @Override
- public Object get(Object key) {
- readLock.lock();
- try {
- return super.get(key);
- } finally {
- readLock.unlock();
- }
- }
-
- @Override
- public Object put(Object key, Object value) {
- writeLock.lock();
- try {
- return super.put(key, value);
- } finally {
- writeLock.unlock();
- }
- }
-
- @Override
- protected boolean removeEldestEntry(Map.Entry eldest) {
- return this.size() > maxSize;
- }
- }
Guava Cache adalah teknologi caching yang dikembangkan oleh Google yang didasarkan pada algoritma penggantian LRU (paling baru digunakan). Namun, dengan maraknya Kafein, Jambu Cache secara bertahap menghilang dari pandangan orang. Kafein tidak hanya mewarisi keunggulan Guava Cache, tetapi juga mengunggulinya dalam banyak aspek. Meskipun kode contoh spesifik tidak disediakan di sini, pembaca yang tertarik dengan Guava Cache dapat mengunjungi situs resminya untuk informasi lebih lanjut.
Keuntungan: Guava Cache mendukung pengaturan batas kapasitas maksimum dan menyediakan dua strategi kedaluwarsa: berdasarkan waktu penyisipan dan waktu akses, dan juga mendukung beberapa fungsi statistik dasar.
Kekurangan: Dengan dirilisnya Spring Boot 2 dan Spring 5, Guava Cache tidak lagi direkomendasikan untuk keduanya.
Caffeine merupakan teknologi caching open source yang menggunakan algoritma W-TinyLFU, yaitu strategi penghapusan cache yang menggabungkan keunggulan LRU dan LFU (least frekuensi yang digunakan). Performa cache Caffeine mendekati solusi optimal teoretis dan dapat dianggap sebagai versi terbaru dari Guava Cache.
- public class CaffeineCacheTest {
-
- public static void main(String[] args) throws Exception {
- //创建guava cache
- Cache<String, String> loadingCache = Caffeine.newBuilder()
- //cache的初始容量
- .initialCapacity(5)
- //cache最大缓存数
- .maximumSize(10)
- //设置写缓存后n秒钟过期
- .expireAfterWrite(17, TimeUnit.SECONDS)
- //设置读写缓存后n秒钟过期,实际很少用到,类似于expireAfterWrite
- //.expireAfterAccess(17, TimeUnit.SECONDS)
- .build();
- String key = "key";
- // 往缓存写数据
- loadingCache.put(key, "v");
-
- // 获取value的值,如果key不存在,获取value后再返回
- String value = loadingCache.get(key, CaffeineCacheTest::getValueFromDB);
-
- // 删除key
- loadingCache.invalidate(key);
- }
-
- private static String getValueFromDB(String key) {
- return "v";
- }
- }
Ehcache adalah kerangka caching dalam proses Java murni yang dikenal karena kinerjanya yang cepat dan efisien. Ini dikenal luas dan berfungsi sebagai penyedia cache default untuk Hibernate.
Keuntungan : Ehcache menyediakan berbagai algoritma penggusuran cache, termasuk LFU (paling jarang digunakan), LRU (paling jarang digunakan) dan FIFO (masuk pertama, keluar pertama). Ini mendukung berbagai jenis penyimpanan cache, seperti cache in-heap, cache off-heap, dan cache disk, untuk beradaptasi dengan kebutuhan penyimpanan yang berbeda. Selain itu, Ehcache juga mendukung berbagai solusi cluster, yang secara efektif memecahkan masalah berbagi data.
Kekurangan: Meskipun Ehcache unggul dalam banyak aspek, ia sedikit kalah dengan Kafein dalam hal kinerja.
-
- public class EncacheTest {
-
- public static void main(String[] args) throws Exception {
- // 声明一个cacheBuilder
- CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
- .withCache("encacheInstance", CacheConfigurationBuilder
- //声明一个容量为20的堆内缓存
- .newCacheConfigurationBuilder(String.class,String.class, ResourcePoolsBuilder.heap(20)))
- .build(true);
- // 获取Cache实例
- Cache<String,String> myCache = cacheManager.getCache("encacheInstance", String.class, String.class);
- // 写缓存
- myCache.put("key","v");
- // 读缓存
- String value = myCache.get("key");
- // 移除换粗
- cacheManager.removeCache("myCache");
- cacheManager.close();
- }
- }
Cache<Key, Graph> cache = Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.maximumSize(10_000)
.build();
// 查找一个缓存元素, 没有查找到的时候返回null
Graph graph = cache.getIfPresent(key);
// 查找缓存,如果缓存不存在则生成缓存元素, 如果无法生成则返回null
graph = cache.get(key, k -> createExpensiveGraph(key));
// 添加或者更新一个缓存元素
cache.put(key, graph);
// 移除一个缓存元素
cache.invalidate(key);
Antarmuka Cache menyediakan kemampuan untuk menemukan, memperbarui, dan menghapus elemen cache menggunakan pencarian eksplisit. Ketika elemen yang di-cache tidak dapat dibuat atau pengecualian dilemparkan selama proses pembuatan dan pembuatan elemen gagal, cache.get dapat mengembalikan null .
LoadingCache<Key, Graph> cache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(key -> createExpensiveGraph(key));
// 查找缓存,如果缓存不存在则生成缓存元素, 如果无法生成则返回null
Graph graph = cache.get(key);
// 批量查找缓存,如果缓存不存在则生成缓存元素
Map<Key, Graph> graphs = cache.getAll(keys);
LoadingCache adalah implementasi cache dari Cache dengan kemampuan CacheLoader yang ditambahkan.
Jika cache tidak ada, elemen cache yang sesuai akan dihasilkan melalui CacheLoader.load.
AsyncCache<Key, Graph> cache = Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.maximumSize(10_000)
.buildAsync();
// 查找一个缓存元素, 没有查找到的时候返回null
CompletableFuture<Graph> graph = cache.getIfPresent(key);
// 查找缓存元素,如果不存在,则异步生成
graph = cache.get(key, k -> createExpensiveGraph(key));
// 添加或者更新一个缓存元素
cache.put(key, graph);
// 移除一个缓存元素
cache.synchronous().invalidate(key);
AsyncCache adalah bentuk Cache asinkron, yang memberikan kemampuan bagi Executor untuk menghasilkan elemen cache dan mengembalikan CompletableFuture. Implementasi kumpulan thread default adalah ForkJoinPool.commonPool(), namun Anda juga dapat menyesuaikan pilihan kumpulan thread dengan mengganti dan mengimplementasikan metode Caffeine.executor(Executor).
AsyncLoadingCache<Key, Graph> cache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(10, TimeUnit.MINUTES)
// 你可以选择: 去异步的封装一段同步操作来生成缓存元素
.buildAsync(key -> createExpensiveGraph(key));
// 你也可以选择: 构建一个异步缓存元素操作并返回一个future
.buildAsync((key, executor) -> createExpensiveGraphAsync(key, executor));
// 查找缓存元素,如果其不存在,将会异步进行生成
CompletableFuture<Graph> graph = cache.get(key);
// 批量查找缓存元素,如果其不存在,将会异步进行生成
CompletableFuture<Map<Key, Graph>> graphs = cache.getAll(keys);
AsyncLoadingCache adalah bentuk LoadingCache asinkron, yang menyediakan fungsi pemuatan asinkron untuk menghasilkan elemen cache.
// 基于缓存内的元素个数进行驱逐
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.maximumSize(10_000)
.build(key -> createExpensiveGraph(key));
// 基于缓存内元素权重进行驱逐
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.maximumWeight(10_000)
.weigher((Key key, Graph graph) -> graph.vertices().size())
.build(key -> createExpensiveGraph(key));
// 基于固定的过期时间驱逐策略
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.expireAfterAccess(5, TimeUnit.MINUTES)
.build(key -> createExpensiveGraph(key));
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(key -> createExpensiveGraph(key));
// 基于不同的过期驱逐策略
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.expireAfter(new Expiry<Key, Graph>() {
public long expireAfterCreate(Key key, Graph graph, long currentTime) {
// Use wall clock time, rather than nanotime, if from an external resource
long seconds = graph.creationDate().plusHours(5)
.minus(System.currentTimeMillis(), MILLIS)
.toEpochSecond();
return TimeUnit.SECONDS.toNanos(seconds);
}
public long expireAfterUpdate(Key key, Graph graph,
long currentTime, long currentDuration) {
return currentDuration;
}
public long expireAfterRead(Key key, Graph graph,
long currentTime, long currentDuration) {
return currentDuration;
}
})
.build(key -> createExpensiveGraph(key));
// 当key和缓存元素都不再存在其他强引用的时候驱逐
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.weakKeys()
.weakValues()
.build(key -> createExpensiveGraph(key));
// 当进行GC的时候进行驱逐
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.softValues()
.build(key -> createExpensiveGraph(key));
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.maximumSize(10_000)
.refreshAfterWrite(1, TimeUnit.MINUTES)
.build(key -> createExpensiveGraph(key));
Strategi penyegaran hanya dapat digunakan di LoadingCache. Berbeda dengan eviction, jika elemen cache ditanyakan selama penyegaran, nilai lamanya akan tetap dikembalikan, dan nilai baru yang disegarkan tidak akan dikembalikan hingga penyegaran elemen selesai.
Cache<Key, Graph> graphs = Caffeine.newBuilder()
.maximumSize(10_000)
.recordStats()
.build();
Pengumpulan data dapat diaktifkan menggunakan metode Caffeine.recordStats(). Metode Cache.stats() akan mengembalikan objek CacheStats, yang berisi beberapa indikator statistik, seperti:
hitRate(): Tingkat hit cache kueri
evictionCount(): Jumlah cache yang dikeluarkan
averageLoadPenalty(): Waktu rata-rata yang diperlukan untuk memuat nilai baru
Dengan RESTful Controller yang disediakan oleh SpringBoot, Anda dapat dengan mudah menanyakan penggunaan Cache.