Κοινή χρήση τεχνολογίας

Java τοπική προσωρινή μνήμη (ρύθμιση υψηλής απόδοσης)

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Στον κόσμο της Java, η τεχνολογία προσωρινής αποθήκευσης χρησιμοποιείται ευρέως για τη βελτίωση της απόδοσης της εφαρμογής και χωρίζεται κυρίως σε δύο κατηγορίες: απομακρυσμένη προσωρινή αποθήκευση και τοπική προσωρινή αποθήκευση. Η απομακρυσμένη προσωρινή αποθήκευση, με την εξαιρετική απόδοση και την ευελιξία της, υλοποιείται συχνά μέσω δημοφιλών λύσεων όπως το Redis και το Memcached. Η τοπική κρυφή μνήμη, με τα χαρακτηριστικά ελαφριάς και γρήγορης πρόσβασης, αντιπροσωπεύεται από τεχνολογίες όπως HashMap, Guava Cache, Caffeine και Ehcache.

Θα εμβαθύνουμε στα μυστήρια της απομακρυσμένης προσωρινής αποθήκευσης σε μελλοντικές αναρτήσεις ιστολογίου, αλλά σήμερα, ας εστιάσουμε στην τοπική προσωρινή αποθήκευση. Αυτό το άρθρο θα σας οδηγήσει πρώτα στην τοπική τεχνολογία προσωρινής αποθήκευσης για να σας παρέχει μια ολοκληρωμένη επισκόπηση. Στη συνέχεια, θα εμβαθύνουμε στην τεχνολογία προσωρινής αποθήκευσης, γνωστή ως ο βασιλιάς της απόδοσης, και θα εξερευνήσουμε τις αρχές και τις μεθόδους υλοποίησης πίσω από αυτήν. Τέλος, μέσω μιας σειράς πρακτικών περιπτώσεων, θα δείξουμε πώς να χρησιμοποιείτε αποτελεσματικά αυτές τις τεχνολογίες τοπικής προσωρινής αποθήκευσης υψηλής απόδοσης στην καθημερινή εργασία για να βελτιώσετε την αποδοτικότητα ανάπτυξης και την απόδοση της εφαρμογής σας.

1. Τοπική προσωρινή μνήμη Java

1.1 HashMap

Χρησιμοποιώντας την υποκείμενη υλοποίηση του Map, μπορούμε να αποθηκεύσουμε τα αντικείμενα που θα αποθηκευτούν στην προσωρινή μνήμη απευθείας στη μνήμη, η οποία είναι μια άμεση και αποτελεσματική μέθοδος.

Πλεονέκτημα: Αυτή η μέθοδος είναι απλή και άμεση, χωρίς να βασίζεται σε εξωτερικές βιβλιοθήκες και είναι πολύ κατάλληλη για εφαρμογές με απλές απαιτήσεις προσωρινής αποθήκευσης και απλά σενάρια.

Μειονεκτήματα: Ωστόσο, αυτή η μέθοδος δεν διαθέτει μηχανισμό αυτόματης εξάλειψης της κρυφής μνήμης Εάν πρέπει να εφαρμοστούν πιο προηγμένες στρατηγικές προσωρινής αποθήκευσης, ενδέχεται να απαιτηθούν υψηλότερα προσαρμοσμένα κόστη ανάπτυξης.

  1. public class LRUCache extends LinkedHashMap {
  2. /**
  3. * 可重入读写锁,保证并发读写安全性
  4. */
  5. private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
  6. private Lock readLock = readWriteLock.readLock();
  7. private Lock writeLock = readWriteLock.writeLock();
  8. /**
  9. * 缓存大小限制
  10. */
  11. private int maxSize;
  12. public LRUCache(int maxSize) {
  13. super(maxSize + 1, 1.0f, true);
  14. this.maxSize = maxSize;
  15. }
  16. @Override
  17. public Object get(Object key) {
  18. readLock.lock();
  19. try {
  20. return super.get(key);
  21. } finally {
  22. readLock.unlock();
  23. }
  24. }
  25. @Override
  26. public Object put(Object key, Object value) {
  27. writeLock.lock();
  28. try {
  29. return super.put(key, value);
  30. } finally {
  31. writeLock.unlock();
  32. }
  33. }
  34. @Override
  35. protected boolean removeEldestEntry(Map.Entry eldest) {
  36. return this.size() > maxSize;
  37. }
  38. }

1.2 Cache γκουάβα

Το Guava Cache είναι μια τεχνολογία προσωρινής αποθήκευσης που αναπτύχθηκε από την Google που βασίζεται στον αλγόριθμο αντικατάστασης LRU (που χρησιμοποιήθηκε λιγότερο πρόσφατα). Ωστόσο, με την άνοδο της καφεΐνης, το Guava Cache σταδιακά έσβησε από τα μάτια των ανθρώπων. Η καφεΐνη όχι μόνο κληρονομεί τα πλεονεκτήματα του Guava Cache, αλλά την ξεπερνά και σε πολλές πτυχές. Παρόλο που δεν παρέχεται συγκεκριμένο δείγμα κώδικα εδώ, οι αναγνώστες που ενδιαφέρονται για το Guava Cache μπορούν να επισκεφτούν τον επίσημο ιστότοπο του για περισσότερες πληροφορίες.

Πλεονέκτημα: Το Guava Cache υποστηρίζει τον καθορισμό ορίων μέγιστης χωρητικότητας και παρέχει δύο στρατηγικές λήξης: με βάση τον χρόνο εισαγωγής και τον χρόνο πρόσβασης και υποστηρίζει επίσης ορισμένες βασικές στατιστικές λειτουργίες.

Μειονεκτήματα: Με την κυκλοφορία των Spring Boot 2 και Spring 5, το Guava Cache δεν συνιστάται πλέον και για τα δύο.

1.3 Καφεΐνη

Η καφεΐνη είναι μια τεχνολογία προσωρινής αποθήκευσης ανοιχτού κώδικα που χρησιμοποιεί τον αλγόριθμο W-TinyLFU, ο οποίος είναι μια στρατηγική εξάλειψης της κρυφής μνήμης που συνδυάζει τα πλεονεκτήματα των LRU και LFU (χρησιμοποιείται ελάχιστη συχνότητα). Η απόδοση της cache της καφεΐνης είναι κοντά στη θεωρητική βέλτιστη λύση και μπορεί να θεωρηθεί ως μια αναβαθμισμένη έκδοση του Guava Cache.

  1. public class CaffeineCacheTest {
  2. public static void main(String[] args) throws Exception {
  3. //创建guava cache
  4. Cache<String, String> loadingCache = Caffeine.newBuilder()
  5. //cache的初始容量
  6. .initialCapacity(5)
  7. //cache最大缓存数
  8. .maximumSize(10)
  9. //设置写缓存后n秒钟过期
  10. .expireAfterWrite(17, TimeUnit.SECONDS)
  11. //设置读写缓存后n秒钟过期,实际很少用到,类似于expireAfterWrite
  12. //.expireAfterAccess(17, TimeUnit.SECONDS)
  13. .build();
  14. String key = "key";
  15. // 往缓存写数据
  16. loadingCache.put(key, "v");
  17. // 获取value的值,如果key不存在,获取value后再返回
  18. String value = loadingCache.get(key, CaffeineCacheTest::getValueFromDB);
  19. // 删除key
  20. loadingCache.invalidate(key);
  21. }
  22. private static String getValueFromDB(String key) {
  23. return "v";
  24. }
  25. }

1.4 Ehcache

Το Ehcache είναι ένα καθαρό πλαίσιο προσωρινής αποθήκευσης σε διαδικασία Java, γνωστό για τη γρήγορη και αποτελεσματική απόδοσή του. Είναι ευρέως αναγνωρισμένο και χρησιμεύει ως ο προεπιλεγμένος πάροχος προσωρινής μνήμης για την Αδρανοποίηση.

Πλεονέκτημα : Το Ehcache παρέχει μια ευρεία γκάμα αλγορίθμων εξάλειψης της κρυφής μνήμης, συμπεριλαμβανομένων των LFU (χρησιμοποιείται λιγότερο συχνά), LRU (λιγότερο πρόσφατα χρησιμοποιημένος) και FIFO (πρώτος εισερχόμενος, πρώτη έξοδος). Υποστηρίζει διαφορετικούς τύπους αποθήκευσης κρυφής μνήμης, όπως cache in-heap, cache off-heap και cache δίσκου, για προσαρμογή σε διαφορετικές ανάγκες αποθήκευσης. Επιπλέον, το Ehcache υποστηρίζει επίσης μια ποικιλία λύσεων συμπλέγματος, επιλύοντας αποτελεσματικά το πρόβλημα της κοινής χρήσης δεδομένων.

Μειονεκτήματα: Αν και το Ehcache υπερέχει σε πολλές πτυχές, υπολείπεται ελαφρώς της καφεΐνης όσον αφορά την απόδοση.

  1. public class EncacheTest {
  2. public static void main(String[] args) throws Exception {
  3. // 声明一个cacheBuilder
  4. CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
  5. .withCache("encacheInstance", CacheConfigurationBuilder
  6. //声明一个容量为20的堆内缓存
  7. .newCacheConfigurationBuilder(String.class,String.class, ResourcePoolsBuilder.heap(20)))
  8. .build(true);
  9. // 获取Cache实例
  10. Cache<String,String> myCache = cacheManager.getCache("encacheInstance", String.class, String.class);
  11. // 写缓存
  12. myCache.put("key","v");
  13. // 读缓存
  14. String value = myCache.get("key");
  15. // 移除换粗
  16. cacheManager.removeCache("myCache");
  17. cacheManager.close();
  18. }
  19. }

 

2. Cache υψηλής απόδοσης Καφεΐνη

2.1 Τύπος κρυφής μνήμης

2.1.1 Προσωρινή μνήμη

 

Cache<Key, Graph> cache = Caffeine.newBuilder()    .expireAfterWrite(10, TimeUnit.MINUTES)    .maximumSize(10_000)    .build();
// 查找一个缓存元素, 没有查找到的时候返回nullGraph graph = cache.getIfPresent(key);// 查找缓存,如果缓存不存在则生成缓存元素,  如果无法生成则返回nullgraph = cache.get(key, k -> createExpensiveGraph(key));// 添加或者更新一个缓存元素cache.put(key, graph);// 移除一个缓存元素cache.invalidate(key);

Η διεπαφή προσωρινής μνήμης παρέχει τη δυνατότητα εύρεσης, ενημέρωσης και αφαίρεσης στοιχείων που έχουν αποθηκευτεί στην κρυφή μνήμη χρησιμοποιώντας ρητές αναζητήσεις. Όταν το αποθηκευμένο στοιχείο δεν μπορεί να δημιουργηθεί ή δημιουργείται εξαίρεση κατά τη διαδικασία δημιουργίας και η δημιουργία του στοιχείου αποτυγχάνει, το cache.get μπορεί να επιστρέψει null.

2.1.2 Φόρτωση προσωρινής μνήμης

LoadingCache<Key, Graph> cache = Caffeine.newBuilder()    .maximumSize(10_000)    .expireAfterWrite(10, TimeUnit.MINUTES)    .build(key -> createExpensiveGraph(key));
// 查找缓存,如果缓存不存在则生成缓存元素,  如果无法生成则返回nullGraph graph = cache.get(key);// 批量查找缓存,如果缓存不存在则生成缓存元素Map<Key, Graph> graphs = cache.getAll(keys);

 

Το LoadingCache είναι μια εφαρμογή προσωρινής μνήμης μιας προσωρινής μνήμης με την δυνατότητα προσθήκης CacheLoader.
Εάν η προσωρινή μνήμη δεν υπάρχει, το αντίστοιχο στοιχείο κρυφής μνήμης θα δημιουργηθεί μέσω του CacheLoader.load.

2.1.3 Async Cache

AsyncCache<Key, Graph> cache = Caffeine.newBuilder()    .expireAfterWrite(10, TimeUnit.MINUTES)    .maximumSize(10_000)    .buildAsync();
// 查找一个缓存元素, 没有查找到的时候返回nullCompletableFuture<Graph> graph = cache.getIfPresent(key);// 查找缓存元素,如果不存在,则异步生成graph = cache.get(key, k -> createExpensiveGraph(key));// 添加或者更新一个缓存元素cache.put(key, graph);// 移除一个缓存元素cache.synchronous().invalidate(key);

Το AsyncCache είναι η ασύγχρονη μορφή της προσωρινής μνήμης, παρέχοντας τη δυνατότητα στον Executor να δημιουργεί στοιχεία κρυφής μνήμης και να επιστρέφει το CompletableFuture. Η προεπιλεγμένη υλοποίηση της ομάδας νημάτων είναι η ForkJoinPool.commonPool(), αλλά μπορείτε επίσης να προσαρμόσετε την επιλογή της ομάδας νημάτων παρακάμπτοντας και εφαρμόζοντας τη μέθοδο Caffeine.executor(Executor).

2.1.4 Ασύγχρονη φόρτωση προσωρινής μνήμης

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 είναι η ασύγχρονη μορφή του LoadingCache, η οποία παρέχει τη λειτουργία του ασύγχρονου φορτίου για τη δημιουργία στοιχείων κρυφής μνήμης.

2.2 Στρατηγική έξωσης

  • Βάσει χωρητικότητας

// 基于缓存内的元素个数进行驱逐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));

2.3 Μηχανισμός ανανέωσης

LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()    .maximumSize(10_000)    .refreshAfterWrite(1, TimeUnit.MINUTES)    .build(key -> createExpensiveGraph(key));

Η στρατηγική ανανέωσης μπορεί να χρησιμοποιηθεί μόνο στο LoadingCache, σε αντίθεση με το eiction, εάν το στοιχείο της κρυφής μνήμης ζητηθεί κατά τη διάρκεια της ανανέωσης, η παλιά του τιμή θα επιστραφεί και η ανανεωμένη νέα τιμή δεν θα επιστραφεί μέχρι να ολοκληρωθεί η ανανέωση του στοιχείου.

2.4 Στατιστικά στοιχεία

Cache<Key, Graph> graphs = Caffeine.newBuilder() .maximumSize(10_000) .recordStats() .build();

Η συλλογή δεδομένων μπορεί να ενεργοποιηθεί χρησιμοποιώντας τη μέθοδο Caffeine.recordStats(). Η μέθοδος Cache.stats() θα επιστρέψει ένα αντικείμενο CacheStats, το οποίο θα περιέχει ορισμένους στατιστικούς δείκτες, όπως:

  • hitRate(): Ρυθμός επιτυχίας της κρυφής μνήμης ερωτήματος

  • evictionCount(): Αριθμός κρυφών μνήμων που αποβάλλονται

  • averageLoadPenalty(): Ο μέσος χρόνος που χρειάζεται για να φορτωθεί μια νέα τιμή

Με τον ελεγκτή RESTful που παρέχεται από το SpringBoot, μπορείτε εύκολα να ρωτήσετε τη χρήση της προσωρινής μνήμης.