• 利用Hutoolcache来改造本地缓存数据


    程序中有个从数据字典表获取数据记录的service -- TDicdataServiceImpl。

    § 利用ScheduledThreadPoolExecutor实现本地数据缓存

    考虑到频繁获取字典数据,后来做了本地缓存。实现方案是利用ScheduledThreadPoolExecutor#schedule 。 在频繁访问这个方法过程中,设定每10分钟清理内存数据。

    package com.cn.yft.ora.service.impl;
    
    import com.cn.yft.ora.dao.TDicdataDAO;
    import com.cn.yft.ora.entity.TDicdata;
    import com.cn.yft.ora.service.TDicdataService;
    import com.yft.util.StringUtil;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import java.util.HashMap;
    import java.util.List;
    import java.util.concurrent.ScheduledThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    @Slf4j
    @Service
    public class TDicdataServiceImpl implements TDicdataService {
    
        private static HashMap<String,List<TDicdata>> cacheByDicmemo = new HashMap<>();
        private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
    @Autowired
    private TDicdataDAO tDicdataDAO; @SuppressWarnings("unchecked") @Override public List<TDicdata> selectByDicmemo(String dicmemo) {if(StringUtil.isEmpty(dicmemo)) return null; List<TDicdata> list = cacheByDicmemo.get(dicmemo); if(list == null || list.size() == 0){ list = this.tDicdataDAO.selectByDicDesc(dicmemo); cacheByDicmemo.put(dicmemo, list); } if(executor.getActiveCount()+executor.getQueue().size()==0) { executor.schedule(this::cacheByDicmemoClean,10, TimeUnit.MINUTES); } return list; }
    @Override
    // @Scheduled(cron = "0 0/10 * * * ?") 注解@Scheduled对spring无效,需在spring.xml单独配置CronTriggerBean public void cacheByDicmemoClean() { if(!cacheByDicmemo.isEmpty()) { int size=cacheByDicmemo.size(); cacheByDicmemo.clear(); log.info("#字典缓存已清空---清空缓存---本地缓存--已清空本地内存数据{}条", size); } } }

    § 利用Hutool-cache来改造本地缓存数据

    说来真巧。上面缓存优化是在2020年年底改造的。 而今适逢一年,旧历2021年的年底,使用Hutool-cache来再做一次改造升级。

    hutool里有如下几种类型的缓存,均派生自实现了cn.hutool.cache.Cache<K, V>接口的抽象类cn.hutool.cache.impl.AbstractCache<K, V>:

    ✅ cn.hutool.cache.impl public class FIFOCache<K, V>
    FIFO(first in first out) 先进先出缓存
    元素不停的加入缓存直到缓存满为止,当缓存满时,清理过期缓存对象,清理后依旧满则删除先入的缓存(链表首部对象)。优点:简单快速 缺点:不灵活,不能保证最常用的对象总是被保留。

    ✅ cn.hutool.cache.impl public class LFUCache<K, V>
    LFU(least frequently used) 最少使用率缓存
    根据使用次数来判定对象是否被持续缓存,使用率是通过访问次数计算的。 当缓存满时清理过期对象。 清理后依旧满的情况下清除最少访问(访问计数最小)的对象并将其他对象的访问数减去这个最小访问数,以便新对象进入后可以公平计数。

    ✅ cn.hutool.cache.impl public class LRUCache<K, V>
    LRU (least recently used)最近最久未使用缓存
    根据使用时间来判定对象是否被持续缓存。当对象被访问时放入缓存,当缓存满了,最久未被使用的对象将被移除。 此缓存基于LinkedHashMap,因此当被缓存的对象每被访问一次,这个对象的key就到链表头部。 这个算法简单并且非常快,他比FIFO有一个显著优势是经常使用的对象不太可能被移除缓存。 缺点是当缓存满时,不能被很快的访问。

    ✅ cn.hutool.cache.impl public class TimedCache<K, V>
    定时缓存 此缓存没有容量限制,对象只有在过期后才会被移除。


    针对本文这个场景,我采用最少使用率缓存——LFUCache。

    package com.cn.yft.ora.service.impl;
    
    import cn.hutool.cache.CacheUtil;
    import cn.hutool.cache.impl.LFUCache;
    import com.cn.yft.ora.dao.TDicdataDAO;
    import com.cn.yft.ora.entity.TDicdata;
    import com.cn.yft.ora.service.TDicdataService;
    import com.yft.util.StringUtil;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import sun.reflect.generics.reflectiveObjects.NotImplementedException;
    
    import java.util.HashMap;
    import java.util.List;
    import java.util.concurrent.ScheduledThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    @Slf4j
    @Service
    public class TDicdataServiceImpl implements TDicdataService {
    
        // 缓存TTL:10分钟
        private static LFUCache<String,List<TDicdata>> cacheByDicmemo = CacheUtil.newLFUCache(16, TimeUnit.MINUTES.toMillis(10));
    
        @Autowired
        private TDicdataDAO tDicdataDAO;
    
        @SuppressWarnings("unchecked")
        @Override
        public List<TDicdata> selectByDicmemo(String dicmemo) {if(StringUtil.isEmpty(dicmemo)) return null;
            List<TDicdata> list = cacheByDicmemo.get(dicmemo);
            if(list == null || list.size() == 0){
                list = this.tDicdataDAO.selectByDicDesc(dicmemo);
                log.debug("获取key={},结果条数={}", dicmemo, list.size());
                cacheByDicmemo.put(dicmemo, list);
            }
            return list;
        }
    @Deprecated // 下面方法不再使用,标记作废 @Override
    public void cacheByDicmemoClean() { throw new NotImplementedException(); } }
  • 相关阅读:
    DBF数据库资料
    服务器更改IP(公网)地址后,Program Neighborhood客户端无法连接服务器
    windows server 2003 无法搜索到自己的解决方法
    windows server 2008系统(sp1) 出现MMC无法创建管理单元的解决方法
    Web方式登录出现如下提示The system was not able to acquire a citrix product license...的原因
    Dell2950服务器windows server 2003安装手记
    配置终端用户的输入法
    DELPHI高精度计时方法,取毫秒级时间精度
    金蝶KIS10专业版客户端打开'91'错误:未设置对象变量或 With block 变量的解决方法
    [转载]用三张图片详解Asp.Net 全生命周期
  • 原文地址:https://www.cnblogs.com/buguge/p/15853551.html
Copyright © 2020-2023  润新知