基于Java的轻量级缓存组件,实现LRU、LFU、FIFO等缓存清除算法

该组件使用简单、可靠

类图如下:
在这里插入图片描述

例如:

        DawnSimpleCache dawnSimpleCache = new DawnSimpleCache(
                "testCache", 2, DawnSimpleCache.LRU, 1F);
        // 设置缓存数据
        dawnSimpleCache.setCache("test1", "xxx", 200, TimeUnit.SECONDS);
        // 从缓存获取数据
        dawnSimpleCache.getFromCache("test1");
    /**
     * 构造
     *
     * @param name            缓存名称
     * @param maxSize         最大的缓存数量
     * @param strategy        缓存策略,LRU_LFU 1, LFU 2, FIFO 3
     * @param clearProportion 清除的比重,0-1之间
     */
    public DawnSimpleCache(String name, int maxSize, int strategy, double clearProportion) 

该组件实现了以下算法

  • LRU (Least recently used) 最近最少使用,如果数据最近被访问过,那么将来被访问的几率也更高。

  • LFU (Least frequently used) 最不经常使用,如果一个数据在最近一段时间内使用次数很少,
    那么在将来一段时间内被使用的可能性也很小。

  • FIFO (Fist in first out) 先进先出, 如果一个数据最先进入缓存中,则应该最早淘汰掉。

  • LRU_LFU 综合 LRU 和 LFU 的优点,但是计算较复杂

核心代码如下:

DawnCache

/**
 * DAWN 缓存通用接口
 *
 * @author hengyumo
 * @since 2021-06-06
 */
public interface DawnCache {

    /**
     * 从缓存中取值
     *
     * @param key 键
     * @return Pair<Boolean, Optional < ?>>, Boolean是标识位,标识是否缓存中存在这个Key
     * 该设计是为了避免空值时出现缓存穿透的可能
     */
    Pair<Boolean, Optional<?>> getFromCache(String key);

    /**
     * 删除某个前缀开头的键对应的那些缓存
     *
     * @param keyPrefix 键的前缀
     * @return 删除的数量
     */
    int deleteCache(String keyPrefix);

    /**
     * 设置缓存
     *
     * @param key        键
     * @param value      值
     * @param timeToLive 有效时间,0时为永久
     * @param timeUnit   有效时间的单位
     */
    void setCache(String key, Object value, long timeToLive, TimeUnit timeUnit);

}

DawnSimpleCache

package cn.hengyumo.dawn.core.cache.types;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.util.Pair;
import org.springframework.util.Assert;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

/**
 * 使用 Java 内置的 MAP 实现的缓存组件,适用于一些轻量级缓存
 *
 * @author hengyumo
 * @since 2021-06-06
 */
@Slf4j
public class DawnSimpleCache implements DawnCache {

    @AllArgsConstructor
    @Getter
    @Setter
    private static class CacheHitInfo {
        // 缓存启用时间
        private long startTime;
        // 最后一次使用时间
        private long lastTime;
        // 命中次数
        private long hitCount;
        // 计分
        private double score;
        // 存活次数
        private int liveCount;
    }
    
    private static class MyPair<E1, E2> {
        private final Object[] objects = new Object[2];
        
        private MyPair(){}
        
        public static <E1,E2> MyPair<E1,E2> of(E1 e1, E2 e2) {
            MyPair<E1, E2> pair = new MyPair<>();
            pair.objects[0] = e1;
            pair.objects[1] = e2;
            return pair;
        }
        public void setFirst(E1 e1) { 
            objects[0] = e1;
        }
        public void setSecond(E2 e2) { 
            objects[1] = e2;
        }
        public E1 getFirst() {
            //noinspection unchecked
            return (E1) objects[0];
        }
        public E2 getSecond() {
            //noinspection unchecked
            return (E2) objects[1];
        }
    }

    /**
     * 最大数量的默认值,200
     */
    public final static int MAX_SIZE = 200;

    /*
    LRU (Least recently used) 最近最少使用,如果数据最近被访问过,那么将来被访问的几率也更高。
    LFU (Least frequently used) 最不经常使用,如果一个数据在最近一段时间内使用次数很少,
                                那么在将来一段时间内被使用的可能性也很小。
    FIFO (Fist in first out) 先进先出, 如果一个数据最先进入缓存中,则应该最早淘汰掉。
    
    LRU_LFU 综合 LRU 和 LFU 的优点,但是计算较复杂
     */
    public final static int LRU = 1;

    public final static int LFU = 2;

    public final static int FIFO = 3;

    public final static int LRU_LFU = 4;

    /**
     * 默认清除之后剩下的比重 0.8
     */
    public final static float DEFAULT_CLEAR_PROPORTION = 0.8F;

    /**
     * 默认的缓存置换策略,LFU,使用次数最少的缓存将会被优先置换
     */
    public final static int DEFAULT_STRATEGY = LRU_LFU;

    /**
     * 保存缓存数据
     */
    private final Map<String, Object> cache;

    /**
     * 保存缓存对应的过期时间
     */
    private final Map<String, Long> cacheExpireRecord;

    /**
     * 记录缓存的最后一次命中时间,LRU支持(最近最少使用的会被优先淘汰)
     */
    private List<MyPair<String, Long>> cacheHitListForLru;

    /**
     * 记录缓存的命中次数,LFU 支持(最近最不经常使用的会优先被淘汰)
     */
    private List<MyPair<String, Long>> cacheHitListForLfu;

    /**
     * 一个先进先出队列,FIFO 支持(最先缓存的数据会优先被淘汰)
     */
    private List<String> cacheHitListForFifo;

    /**
     * LRU_LFU支持
     */
    private List<MyPair<String, CacheHitInfo>> cacheHitListForLruLfu;

    /**
     * 缓存名称
     */
    private final String name;

    /**
     * 对象的最大数量
     */
    private final int maxSize;

    /**
     * 使用的缓存置换策略
     */
    private final int strategy;

    /**
     * 缓存清除所有过期数据之后,仍超出最大大小时,执行策略进行清除的清除比重
     */
    private final double clearProportion;

    /**
     * 默认构造函数,MAX_SIZE 1000、DEFAULT_STRATEGY LRU
     */
    public DawnSimpleCache() {
        this.cache = new ConcurrentHashMap<>();
        this.cacheExpireRecord = new ConcurrentHashMap<>();
        this.maxSize = MAX_SIZE;
        this.strategy = DEFAULT_STRATEGY;
        this.clearProportion = DEFAULT_CLEAR_PROPORTION;
        this.name = this.getClass().getSimpleName() + System.currentTimeMillis();
        applyStrategy();
    }

    /**
     * 构造
     *
     * @param name            缓存名称
     * @param maxSize         最大的缓存数量
     * @param strategy        缓存策略,LRU_LFU 1, LFU 2, FIFO 3
     * @param clearProportion 清除的比重,0-1之间
     */
    public DawnSimpleCache(String name, int maxSize, int strategy, double clearProportion) {
        this.cache = new ConcurrentHashMap<>();
        this.cacheExpireRecord = new ConcurrentHashMap<>();
        this.name = name;
        this.maxSize = maxSize;
        this.strategy = strategy;
        Assert.isTrue(clearProportion > 0 && clearProportion <= 1, "清除的比重在0<x<=1之间");
        this.clearProportion = clearProportion;
        applyStrategy();
    }

    // 应用设置的缓存清除策略
    private void applyStrategy() {
        switch (this.strategy) {
            case LRU -> cacheHitListForLru = new LinkedList<>();
            case LFU -> cacheHitListForLfu = new LinkedList<>();
            case FIFO -> cacheHitListForFifo = new LinkedList<>();
            case LRU_LFU -> cacheHitListForLruLfu = new LinkedList<>();
        }
    }

    private void collectHitInfoForLru(String key) {
        MyPair<String, Long> aPair = null;
        Iterator<MyPair<String, Long>> iterator = cacheHitListForLru.iterator();
        for (int i = 0; i < cacheHitListForLru.size(); i++) {
            MyPair<String, Long> pair = iterator.next();
            if (pair.getFirst().equals(key)) {
                aPair = pair;
                // 更新最近使用时间
                pair.setSecond(System.currentTimeMillis());
                break;
            }
        }
        if (aPair == null) {
            MyPair<String, Long> newPair = MyPair.of(key, System.currentTimeMillis());
            cacheHitListForLru.add(newPair);
        }
    }

    private void collectHitInfoForLfu(String key) {
        MyPair<String, Long> aPair = null;
        Iterator<MyPair<String, Long>> iterator = cacheHitListForLfu.iterator();
        for (int i = 0; i < cacheHitListForLfu.size(); i++) {
            MyPair<String, Long> pair = iterator.next();
            if (pair.getFirst().equals(key)) {
                aPair = pair;
                // 缓存命中次数 + 1
                pair.setSecond(pair.getSecond() + 1);
                break;
            }
        }
        if (aPair == null) {
            MyPair<String, Long> newPair = MyPair.of(key, 1L);
            cacheHitListForLfu.add(newPair);
        }
    }

    private void collectHitInfoForFifo(String key) {
        // 如果没找到则加到最后
        boolean found = cacheHitListForFifo.stream()
                .anyMatch(x -> x.equals(key));
        if (!found) {
            cacheHitListForFifo.add(key);
        }
    }

    private void collectHitInfoForLruLfu(String key) {
        MyPair<String, CacheHitInfo> aPair = null;
        Iterator<MyPair<String, CacheHitInfo>> iterator = cacheHitListForLruLfu.iterator();
        for (int i = 0; i < cacheHitListForLruLfu.size(); i++) {
            MyPair<String, CacheHitInfo> pair = iterator.next();
            if (pair.getFirst().equals(key)) {
                aPair = pair;
                // 更新信息
                CacheHitInfo cacheHitInfo = pair.getSecond();
                cacheHitInfo.setHitCount(cacheHitInfo.getHitCount() + 1);
                cacheHitInfo.setLastTime(System.currentTimeMillis());
                break;
            }
        }
        if (aPair == null) {
            CacheHitInfo cacheHitInfo = new CacheHitInfo(
                    System.currentTimeMillis(),
                    System.currentTimeMillis(),
                    1L, 0F, 0);
            MyPair<String, CacheHitInfo> newPair = MyPair.of(key, cacheHitInfo);
            cacheHitListForLruLfu.add(newPair);
        }
    }

    // 执行缓存的命中信息收集
    private synchronized void collectHitInfoForStrategy(String key) {
        switch (this.strategy) {
            case LRU -> collectHitInfoForLru(key);
            case LFU -> collectHitInfoForLfu(key);
            case FIFO -> collectHitInfoForFifo(key);
            case LRU_LFU -> collectHitInfoForLruLfu(key);
        }
    }

    // 清除缓存,从左到右
    private synchronized <T> void clearCache(Iterator<T> iterator, Function<T, String> function, int clearSize) {
        for (int i = 0; i < clearSize; i++) {
            String key = function.apply(iterator.next());
            this.removeCache(key);
        }
    }

    private void clearWithLru(int clearSize) {
        int cacheSize = cache.size();
        // 针对最近使用时间从低到高进行排序
        cacheHitListForLru.sort(Comparator.comparingLong(MyPair::getSecond));
        // 清除对应的缓存
        clearCache(cacheHitListForLru.iterator(), MyPair::getFirst, clearSize);
        // 最后边的数据最近使用,所以,去头即可
        cacheHitListForLru = cacheHitListForLru.subList(clearSize, cacheSize);
    }

    private void clearWithLfu(int clearSize) {
        int cacheSize = cache.size();
        // 针对使用次数从低到高进行排序
        cacheHitListForLfu.sort(Comparator.comparingLong(MyPair::getSecond));
        // 清除对应的缓存
        clearCache(cacheHitListForLfu.iterator(), MyPair::getFirst, clearSize);
        // 最后边的数据使用最频繁,所以,去头即可
        cacheHitListForLfu = cacheHitListForLfu.subList(clearSize, cacheSize);
    }

    private void clearWithFifo(int clearSize) {
        int cacheSize = cache.size();
        // 清除对应的缓存
        clearCache(cacheHitListForFifo.iterator(), x -> x, clearSize);
        // 最左边的数据最先进入,所以去头
        cacheHitListForFifo = cacheHitListForFifo.subList(clearSize, cacheSize);
    }

    private void clearWithLruLfu(int clearSize) {
        int cacheSize = cache.size();
        // 计算得分、排序
        cacheHitListForLruLfu.forEach(x -> {
            CacheHitInfo cacheHitInfo = x.getSecond();
            // 得分,命中次数 / 最后一次命中至今的毫秒数
            // LRU_LFU耗时:60622 write:299 read:5000 del:100 score:16.49566164098842
            // 得分,命中次数 ^ 2 / 最后一次命中至今的毫秒数
            // LRU_LFU耗时:54182 write:267 read:5000 del:100 score:18.456313904986896
            // LRU_LFU耗时:57845 write:285 read:5000 del:100 score:17.287578874578614
            // 得分,命中次数 ^ 3 / 最后一次命中至今的毫秒数
            // LRU_LFU耗时:57157 write:282 read:5000 del:100 score:17.495669821719126
            // 得分,命中次数 ^ 4 / 最后一次命中至今的毫秒数
            // LRU_LFU耗时:59370 write:293 read:5000 del:100 score:16.843523665150748

            // 得分,命中次数 ^ 0.5 / 最后一次命中至今的毫秒数
            // LRU_LFU耗时:55969 write:276 read:5000 del:100 score:17.867033536421946
            // LRU_LFU耗时:58623 write:289 read:5000 del:100 score:17.05815123756887
            double a = Math.pow((double) cacheHitInfo.getHitCount(), 2);
            cacheHitInfo.setScore((a / (double) ((System.currentTimeMillis() - cacheHitInfo.getLastTime()))));
            cacheHitInfo.setLiveCount(cacheHitInfo.getLiveCount() + 1);
        });
        // 针对得分从低到高进行排序
        cacheHitListForLruLfu.sort(Comparator.comparingDouble(x -> x.getSecond().getScore()));
        // 清除对应的缓存
        clearCache(cacheHitListForLruLfu.iterator(), MyPair::getFirst, clearSize);
        // 最后边的数据使用最频繁,所以,去头即可
        cacheHitListForLruLfu = cacheHitListForLruLfu.subList(clearSize, cacheSize);
    }

    // 执行缓存清除策略
    private synchronized void clearWithStrategy(int clearSize) {
        switch (this.strategy) {
            case LRU -> clearWithLru(clearSize);
            case LFU -> clearWithLfu(clearSize);
            case FIFO -> clearWithFifo(clearSize);
            case LRU_LFU -> clearWithLruLfu(clearSize);
        }
    }

    // 清除缓存命中记录,连同缓存数据
    private synchronized void removeCacheAndCacheHit(String key) {
        switch (this.strategy) {
            case LRU -> cacheHitListForLru.removeIf(x -> x.getFirst().equals(key));
            case LFU -> cacheHitListForLfu.removeIf(x -> x.getFirst().equals(key));
            case FIFO -> cacheHitListForFifo.removeIf(x -> x.equals(key));
            case LRU_LFU -> cacheHitListForLruLfu.removeIf(x -> x.getFirst().equals(key));
        }
        removeCache(key);
    }

    @Override
    public synchronized Pair<Boolean, Optional<?>> getFromCache(String key) {
        boolean cacheContainsKey = true;
        Object value = null;
        Long expireTime = cacheExpireRecord.get(key);
        // 如果存在这个Key
        if (expireTime != null) {
            // 如果数据已经过期,此处使用了延迟删除,只有查询发现过期了才会删除
            // 0L是特殊的,它代表永不过期
            if (expireTime != 0L && expireTime < System.currentTimeMillis()) {
                log.info("SIMPLE CACHE {}缓存过期:{}", name, key);
                removeCacheAndCacheHit(key);
                cacheContainsKey = false;
            } else {
                // 从缓存中获取数据
                value = cache.get(key);
                collectHitInfoForStrategy(key);
            }
        } else {
            cacheContainsKey = false;
        }
        return Pair.of(cacheContainsKey, Optional.ofNullable(value));
    }

    private synchronized void maxSizeExpireCheck() {
        Set<String> keys = cacheExpireRecord.keySet();
        Set<String> keysDel = new HashSet<>();
        for (String key : keys) {
            Long expireTime = cacheExpireRecord.get(key);
            if (expireTime != 0L && expireTime < System.currentTimeMillis()) {
                log.info("SIMPLE CACHE {}缓存过期:{}", name, key);
                keysDel.add(key);
            }
        }
        // 遍历这些Key,依次清除
        keysDel.forEach(this::removeCacheAndCacheHit);
        // 清除之后还是超出最大大小,则进行清除,只留下配置的缓存大小:maxSize * clearProportion
        if (cache.size() > maxSize) {
            clearWithStrategy((cache.size() - maxSize)
                    + (int) (maxSize * (1 - clearProportion)));
        }
    }

    private void removeCache(String key) {
        cacheExpireRecord.remove(key);
        cache.remove(key);
        log.info("SIMPLE CACHE {}缓存清除:{}", name, key);
    }

    @Override
    public synchronized int deleteCache(String keyPrefix) {
        Set<String> keys = new HashSet<>();
        // 先查到以这个前缀开头的所有Key
        cache.forEach((k, v) -> {
            if (k.startsWith(keyPrefix)) {
                keys.add(k);
            }
        });
        // 遍历这些Key,依次清除
        keys.forEach(this::removeCacheAndCacheHit);
        return keys.size();
    }

    @Override
    public synchronized void setCache(String key, Object value, long timeToLive, TimeUnit timeUnit) {
        cache.put(key, value);
        // 记录失效时间
        if (timeToLive <= 0L) {
            cacheExpireRecord.put(key, 0L);
        } else {
            cacheExpireRecord.put(key, System.currentTimeMillis() + timeUnit.toMillis(timeToLive));
        }
        collectHitInfoForStrategy(key);
        if (cacheExpireRecord.size() > maxSize) {
            maxSizeExpireCheck();
        }
    }

    public synchronized int getCacheSize() {
        return cache.size();
    }

    public String getName() {
        return name;
    }

    public int getMaxSize() {
        return maxSize;
    }

    public int getStrategy() {
        return strategy;
    }

    public double getClearProportion() {
        return clearProportion;
    }
}

测试代码

package cn.hengyumo.dawn.example;

import cn.hengyumo.dawn.core.cache.types.DawnSimpleCache;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.data.util.Pair;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.concurrent.TimeUnit;

public class DawnSimpleCacheTest {

    @Test
    public void simpleTest() throws InterruptedException {
        DawnSimpleCache dawnSimpleCache = new DawnSimpleCache();
        dawnSimpleCache.setCache("test", "xxx", 2, TimeUnit.SECONDS);
        Pair<Boolean, Optional<?>> value = dawnSimpleCache.getFromCache("test");
        Assert.assertTrue(value.getFirst());
        Assert.assertTrue(value.getSecond().isPresent());
        String val = (String) value.getSecond().orElse(null);
        Assert.assertEquals("xxx", val);
        Thread.sleep(2000);
        value = dawnSimpleCache.getFromCache("test");
        Assert.assertFalse(value.getFirst());
        Assert.assertFalse(value.getSecond().isPresent());
        dawnSimpleCache.setCache("test", "xxx", 2, TimeUnit.SECONDS);
        dawnSimpleCache.deleteCache("test");
        Assert.assertFalse(value.getFirst());
        Assert.assertFalse(value.getSecond().isPresent());
    }

    @Test
    public void testLru() throws InterruptedException {
        DawnSimpleCache dawnSimpleCache = new DawnSimpleCache(
                "testCache", 2, DawnSimpleCache.LRU, 1F);
        dawnSimpleCache.setCache("test1", "xxx", 200, TimeUnit.SECONDS);
        dawnSimpleCache.getFromCache("test1");

        dawnSimpleCache.setCache("test2", "xxx", 200, TimeUnit.SECONDS);
        dawnSimpleCache.getFromCache("test2");
        Thread.sleep(2000);
        dawnSimpleCache.getFromCache("test1");

        dawnSimpleCache.setCache("test3", "xxx", 200, TimeUnit.SECONDS);
        // 这时 test2 应该被清除
        Pair<Boolean, Optional<?>> value = dawnSimpleCache.getFromCache("test2");
        Assert.assertFalse(value.getFirst());
        Assert.assertFalse(value.getSecond().isPresent());
        Assert.assertEquals(2, dawnSimpleCache.getCacheSize());
    }

    @Test
    public void testLfu() {
        DawnSimpleCache dawnSimpleCache = new DawnSimpleCache(
                "testCache", 2, DawnSimpleCache.LFU, 1F);
        dawnSimpleCache.setCache("test1", "xxx", 200, TimeUnit.SECONDS);
        dawnSimpleCache.getFromCache("test1");

        dawnSimpleCache.setCache("test2", "xxx", 200, TimeUnit.SECONDS);
        dawnSimpleCache.getFromCache("test2");
        dawnSimpleCache.getFromCache("test1");
        dawnSimpleCache.getFromCache("test2");

        dawnSimpleCache.setCache("test3", "xxx", 200, TimeUnit.SECONDS);

        // 这时 test3 应该被清除
        Pair<Boolean, Optional<?>> value = dawnSimpleCache.getFromCache("test3");
        Assert.assertFalse(value.getFirst());
        Assert.assertFalse(value.getSecond().isPresent());
        Assert.assertEquals(2, dawnSimpleCache.getCacheSize());
    }

    @Test
    public void testFifo() {

        DawnSimpleCache dawnSimpleCache = new DawnSimpleCache(
                "testCache", 2, DawnSimpleCache.FIFO, 1F);
        dawnSimpleCache.setCache("test1", "xxx", 200, TimeUnit.SECONDS);
        dawnSimpleCache.getFromCache("test1");

        dawnSimpleCache.setCache("test2", "xxx", 200, TimeUnit.SECONDS);
        dawnSimpleCache.getFromCache("test2");
        dawnSimpleCache.getFromCache("test1");
        dawnSimpleCache.getFromCache("test2");

        dawnSimpleCache.setCache("test3", "xxx", 200, TimeUnit.SECONDS);

        // 这时 test1 应该被清除
        Pair<Boolean, Optional<?>> value = dawnSimpleCache.getFromCache("test1");
        Assert.assertFalse(value.getFirst());
        Assert.assertFalse(value.getSecond().isPresent());
        Assert.assertEquals(2, dawnSimpleCache.getCacheSize());
    }

    @Test
    public void testLruLfu() throws InterruptedException {

        DawnSimpleCache dawnSimpleCache = new DawnSimpleCache(
                "testCache", 2, DawnSimpleCache.LRU_LFU, 1F);
        dawnSimpleCache.setCache("test1", "xxx", 200, TimeUnit.SECONDS);
        dawnSimpleCache.getFromCache("test1");
        dawnSimpleCache.setCache("test2", "xxx", 200, TimeUnit.SECONDS);
        dawnSimpleCache.getFromCache("test2");
        dawnSimpleCache.getFromCache("test1");
        Thread.sleep(2000);
        dawnSimpleCache.getFromCache("test2");

        dawnSimpleCache.setCache("test3", "xxx", 200, TimeUnit.SECONDS);

        // test1 3 / 2, test2 3/ 0.., test3 1/0..
        // 这时 test1 应该被清除
        Pair<Boolean, Optional<?>> value = dawnSimpleCache.getFromCache("test1");
        Assert.assertFalse(value.getFirst());
        Assert.assertFalse(value.getSecond().isPresent());
        Assert.assertEquals(2, dawnSimpleCache.getCacheSize());
    }

    @Test
    public void performanceTest() throws InterruptedException {
        // 查询次数
        int readSize = 5000;
        int dataSize = 1000;
        double hot = 0.05;  // 50
        double delProportion = 0.05;
        // 0.51
        double clearProportion = (hot / (hot + (1 - hot) * hot));
        // 二八定律 dataSize * 0.2 + dataSize * 0.8 * 0.2 = dataSize * 0.36 / 0.56
        // 189
        int maxSize = (int) ((int) (dataSize * (hot + (1 - hot) * hot)) / clearProportion);
        // 0.2 / 0.36 = 0.56 缓存中的热数据占比
        int expire = 300_000;
        long searchTime = 200L;
        Random random = new Random(System.currentTimeMillis());
        StringBuilder sb = new StringBuilder();
        List<DawnSimpleCache> cacheList = new ArrayList<>(4);
        cacheList.add(new DawnSimpleCache(
                "LRU", maxSize, DawnSimpleCache.LRU, clearProportion));
        cacheList.add(new DawnSimpleCache(
                "LFU", maxSize, DawnSimpleCache.LFU, clearProportion));
        cacheList.add(new DawnSimpleCache(
                "FIFO", maxSize, DawnSimpleCache.FIFO, clearProportion));
        cacheList.add(new DawnSimpleCache(
                "LRU_LFU", maxSize, DawnSimpleCache.LRU_LFU, clearProportion));
        for (DawnSimpleCache cache : cacheList) {
            long start = System.currentTimeMillis();
            int read = 0;
            int write = 0;
            int del = 0;
            for (int i = 0; i < readSize; i++) {
                double r = random.nextDouble();
                // 删除的可能性
//                if (r < delProportion) {
                if (i % 50 == 1) { // 1/50
                    cache.deleteCache("key" + random.nextInt(dataSize));
                    del ++;
                }
                int index;
                // 80 % 的 查询集中在 20%的数据
                if (r < (1 - hot)) {
                    index = random.nextInt((int) (dataSize * hot));
                } else {
                    index = (int) (dataSize * hot) + random.nextInt((int) (dataSize * (1 - hot)));
                }
                Pair<Boolean, Optional<?>> value = cache.getFromCache("key" + index);
                read ++;
                if (!value.getFirst()) {
                    // 模拟没查到缓存时重新设置
                    Thread.sleep(searchTime);
                    cache.setCache("key" + index, index, expire, TimeUnit.MILLISECONDS);
                    write ++;
                }
            }

            long time = System.currentTimeMillis() - start;
            sb.append(cache.getName())
                    .append("耗时:").append(time)
                    .append(" write:").append(write)
                    .append(" read:").append(read)
                    .append(" del:").append(del)
                    // 快了多少倍
                    .append(" score:").append((double) (read * searchTime)/ (double) time)
                    .append('\n');
        }
        System.out.println("\n\n" + sb);
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值