Maison  >  Article  >  Java  >  Pour implémenter le cache local Java, commencez par ces points

Pour implémenter le cache local Java, commencez par ces points

(*-*)浩
(*-*)浩avant
2019-10-11 16:25:413081parcourir

La mise en cache, je crois que tout le monde la connaît. Dans le projet, la mise en cache est définitivement indispensable. Il existe de nombreux outils de mise en cache sur le marché, comme Redis, Guava Cache ou encore EHcache.

Pour implémenter le cache local Java, commencez par ces points

Je pense que tout le monde doit être très familier avec ces outils, nous n'en parlerons donc pas aujourd'hui. Parlons de la façon de mettre en œuvre la mise en cache locale. Se référant aux outils ci-dessus, pour obtenir un meilleur cache local, frère Pingtou estime que nous devrions partir des trois aspects suivants.

1. Sélection des collections de stockage

Pour implémenter la mise en cache locale, le conteneur de stockage doit être une structure de données sous forme de clé/valeur. notre collecte de cartes couramment utilisée. Il existe HashMap, Hashtable et ConcurrentHashMap dans Map parmi lesquels choisir. Si nous ne considérons pas les problèmes de sécurité des données en cas de concurrence élevée, nous pouvons choisir HashMap. Si nous considérons les problèmes de sécurité des données en cas de concurrence élevée, nous pouvons choisir l'un des Hashtable et. ConcurrentHashMap. Collection, mais nous préférons ConcurrentHashMap car les performances de ConcurrentHashMap sont meilleures que celles de Hashtable.

2. Traitement du cache expiré

Le cache étant stocké directement dans la mémoire, si nous ne gérons pas le cache expiré, la mémoire sera occupée par une grande nombre de caches invalides, ce qui n'est pas ce que nous voulons Oui, nous devons donc nettoyer ces caches invalides. Le traitement du cache expiré peut être implémenté en se référant à la stratégie Redis. Redis adopte une stratégie de suppression régulière + élimination paresseuse.

Stratégie de suppression périodique

La stratégie de suppression périodique consiste à détecter les caches expirés à intervalles réguliers et à les supprimer. L'avantage de cette stratégie est qu'elle garantit la suppression des caches expirés. Il existe également des inconvénients. Les caches expirés peuvent ne pas être supprimés à temps. Cela est lié à la fréquence de synchronisation que nous définissons. Un autre inconvénient est que s'il y a beaucoup de données en cache, chaque détection mettra également beaucoup de pression sur la tasse. .

Stratégie d'élimination paresseuse

La stratégie d'élimination paresseuse consiste à déterminer d'abord si le cache a expiré lors de l'utilisation du cache. S'il expire, supprimez-le et revenez vide. L'avantage de cette stratégie est qu'elle ne peut déterminer si le produit a expiré que lors de la recherche, ce qui a moins d'impact sur le CUP. En même temps, cette stratégie présente un inconvénient fatal. Lorsqu'un grand nombre de caches sont stockés, ces caches ne sont pas utilisés et ont expiré, et ils deviendront des caches invalides. Ces caches invalides occuperont une grande partie de votre espace mémoire. et éventuellement provoquer un débordement de la mémoire du serveur.

Nous avons brièvement examiné les deux stratégies de traitement du cache d'expiration de Redis. Chaque stratégie a ses propres avantages et inconvénients. Par conséquent, lors de l’utilisation, nous pouvons combiner les deux stratégies, et l’effet combiné est toujours très idéal.

3. Stratégie d'élimination du cache

L'élimination du cache doit être distinguée du traitement de l'élimination du cache expiré lorsque le nombre de nos caches atteint le nombre de caches que nous avons spécifié. Après tout, notre mémoire n’est pas infinie. Si nous devons continuer à ajouter des caches, nous devons éliminer certains caches des caches existants selon une certaine stratégie pour faire de la place aux caches nouvellement ajoutés. Découvrons plusieurs stratégies d'élimination de cache couramment utilisées.

Politique du premier entré, premier sorti

Lorsque l'espace du cache est insuffisant, les données qui entrent dans le cache en premier seront d'abord effacées pour libérer un nouvel espace pour accepter de nouveaux données. Cette stratégie compare principalement le temps de création des éléments mis en cache. Dans certains scénarios qui nécessitent une efficacité des données relativement élevée, ce type de stratégie peut être considéré comme donnant la priorité à la garantie que les données les plus récentes sont disponibles.

Stratégie la moins utilisée

Qu'il soit expiré ou non, en fonction du nombre de fois que l'élément a été utilisé, les éléments avec moins d'utilisations seront effacés pour libérer de l'espace. Cette stratégie compare principalement le hitCount (nombre de hits) des éléments. Ce type de stratégie peut être sélectionné dans des scénarios où la validité des données haute fréquence est assurée.

Stratégie la moins récemment utilisée

Qu'elle soit expirée ou non, en fonction du dernier horodatage utilisé de l'élément, effacez l'élément avec l'horodatage le plus utilisé pour libérer de l'espace. Cette stratégie compare principalement l'heure à laquelle le cache a été utilisé pour la dernière fois par get. Il est plus applicable dans les scénarios de données chaudes, et la priorité est donnée à garantir la validité des données chaudes.

Stratégie d'élimination aléatoire

Éliminez de manière aléatoire un cache, qu'il soit expiré ou non. S'il n'y a aucune exigence concernant les données mises en cache, vous pouvez envisager d'utiliser cette stratégie.

Stratégie de non-élimination

Lorsque le cache atteint la valeur spécifiée, aucun cache ne sera éliminé, mais aucun nouveau cache ne sera ajouté. Aucun autre cache ne pourra être ajouté. jusqu'à ce qu'un cache soit éliminé.

Les trois points ci-dessus doivent être pris en compte pour implémenter un cache local. Après avoir lu ceci, nous devrions savoir comment implémenter un cache local.

Implémenter le cache local

Dans cette démo, nous utilisons ConcurrentHashMap comme collection de stockage, afin de pouvoir garantir la sécurité du cache même dans des situations de forte concurrence. Pour le traitement du cache expiré, j'ai utilisé ici uniquement la stratégie de suppression planifiée et je n'ai pas utilisé la stratégie de suppression planifiée + élimination paresseuse. Vous pouvez l'essayer vous-même et utiliser ces deux stratégies pour le traitement du cache expiré. En termes d'expulsion du cache, j'utilise ici une stratégie de moindre utilisation. Bon, maintenant que nous connaissons la sélection technique, jetons un œil à l'implémentation du code.

Classe d'objet Cache

public class Cache implements Comparable<Cache>{
    // 键
    private Object key;
    // 缓存值
    private Object value;
    // 最后一次访问时间
    private long accessTime;
    // 创建时间
    private long writeTime;
    // 存活时间
    private long expireTime;
    // 命中次数
    private Integer hitCount;
    ...getter/setter()...

Ajouter un cache

/**
 * 添加缓存
 *
 * @param key
 * @param value
 */
public void put(K key, V value,long expire) {
    checkNotNull(key);
    checkNotNull(value);
    // 当缓存存在时,更新缓存
    if (concurrentHashMap.containsKey(key)){
        Cache cache = concurrentHashMap.get(key);
        cache.setHitCount(cache.getHitCount()+1);
        cache.setWriteTime(System.currentTimeMillis());
        cache.setAccessTime(System.currentTimeMillis());
        cache.setExpireTime(expire);
        cache.setValue(value);
        return;
    }
    // 已经达到最大缓存
    if (isFull()) {
        Object kickedKey = getKickedKey();
        if (kickedKey !=null){
            // 移除最少使用的缓存
            concurrentHashMap.remove(kickedKey);
        }else {
            return;
        }
    }
    Cache cache = new Cache();
    cache.setKey(key);
    cache.setValue(value);
    cache.setWriteTime(System.currentTimeMillis());
    cache.setAccessTime(System.currentTimeMillis());
    cache.setHitCount(1);
    cache.setExpireTime(expire);
    concurrentHashMap.put(key, cache);
}

Obtenir le cache

/**
 * 获取缓存
 *
 * @param key
 * @return
 */
public Object get(K key) {
    checkNotNull(key);
    if (concurrentHashMap.isEmpty()) return null;
    if (!concurrentHashMap.containsKey(key)) return null;
    Cache cache = concurrentHashMap.get(key);
    if (cache == null) return null;
    cache.setHitCount(cache.getHitCount()+1);
    cache.setAccessTime(System.currentTimeMillis());
    return cache.getValue();
}

Obtenir le cache le moins utilisé

/**
     * 获取最少使用的缓存
     * @return
     */
    private Object getKickedKey() {
        Cache min = Collections.min(concurrentHashMap.values());
        return min.getKey();
    }

Méthode de détection du cache expiré

/**
 * 处理过期缓存
 */
class TimeoutTimerThread implements Runnable {
    public void run() {
        while (true) {
            try {
                TimeUnit.SECONDS.sleep(60);
                expireCache();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 创建多久后,缓存失效
     *
     * @throws Exception
     */
    private void expireCache() throws Exception {
        System.out.println("检测缓存是否过期缓存");
        for (Object key : concurrentHashMap.keySet()) {
            Cache cache = concurrentHashMap.get(key);
            long timoutTime = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime()
                    - cache.getWriteTime());
            if (cache.getExpireTime() > timoutTime) {
                continue;
            }
            System.out.println(" 清除过期缓存 : " + key);
            //清除过期缓存
            concurrentHashMap.remove(key);
        }
    }
}

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer