DataSource的缓存(基于时间最大值v1)

最近做一个根据前端的url,username,password,sql的信息创建数据源进而获得Connection进行JDBC操作sql。
Connection缓存已经使用数据库连接池实现了,想把DataSource缓存起来,不用每次进来都创建数据源。

一、理论:
1.FIFO(First Input First Output):
特点:先进先出,符合公平性,实现简单。

数据结构:使用对列

淘汰原则:如果一个数据最先进入缓存中,则应该最早淘汰掉。也就是说,当缓存满的时候,应当把最先进入缓存的数据给淘汰掉。

2.LRU(Least Recently Used):
特点:按照时间长短,最不经常使用的缓存数据,先淘汰。

数据结构:链表和hashmap

淘汰原则:如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。

3.LFU(Least Frequently Used):
特点:按照访问次数,最近最少使用的缓存数据,先淘汰。

数据结构:数组、hashmap、堆

淘汰原则:如果一个数据在最近一段时间内使用次数很少,那么在将来一段时间内被使用的可能性也很小。

https://blog.csdn.net/zhaoyunfullmetal/article/details/48497499
https://blog.csdn.net/u013314786/article/details/80658738

https://blog.csdn.net/zhengzhaoyang122/article/details/82184029


import javax.sql.DataSource;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * @Title: Memory
 * @ProjectName database
 * @Description: 数据源缓存
 * @Author wangben
 * @Date 2019/7/2- 14:48
 */
public class DataSourceCache {

    /**
     * 缓存最大个数
     */
    private static final Integer CACHE_MAX_NUMBER = 5;
    /**
     * 当前缓存个数
     */
     public static  Integer CURRENT_SIZE = 0;

    /**
     * 这个记录了缓存使用的最后一次的记录,最近使用的在最前面
     */
    private static final List CACHE_USE_LOG_LIST = new LinkedList<>();
    /**
     * 键值对集合
     */
    private final static Map map = new HashMap<>(5);
    /**
     * 定时器线程池,用于清除过期缓存
     */
    private final static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();

    /**
     * 添加缓存
     *
     * @param key  键
     * @param data 值
     */
    public synchronized static void put(String key, DataSource data) {
        DataSourceCache.put(key, data, 0);
    }

    /**
     * 添加缓存
     *
     * @param key    键
     * @param data   值
     * @param expire 过期时间,单位:毫秒, 0表示无限长
     */
    public synchronized static void put(String key, DataSource data, long expire) {
        //清除原键值对
        DataSourceCache.remove(key);
        //判断缓存数量是否超过容量
        checkSize();
        //将key缓存到linkedlist里
        saveCacheUseLog(key);
        //设置过期时间
        if (expire > 0) {
            Future future = executor.schedule(new Runnable() {
                @Override
                public void run() {
                    //过期后清除该键值对
                    synchronized (DataSourceCache.class) {
                        map.remove(key);
                    }
                }
            }, expire, TimeUnit.MILLISECONDS);
            map.put(key, new Entity(data, future));
        } else {
            //不设置过期时间
            map.put(key, new Entity(data, null));
        }
        //缓存数量+1;
        CURRENT_SIZE = CURRENT_SIZE + 1;

    }

    /**
     * 读取缓存
     *
     * @param key 键
     * @return
     */
    public  static  T get(String key) {
        Entity entity = map.get(key);
        return entity == null ? null : (T) entity.getDataSource();
    }

    /**
     * 清除缓存
     *
     * @param key 键
     * @return
     */
    public synchronized static  T remove(String key) {
        //清除原缓存数据
        Entity entity = map.remove(key);
        if (entity == null) {
            return null;
        }
        //清除原键值对定时器
        if (entity.future != null) {
            entity.future.cancel(true);
        }
        CURRENT_SIZE = CURRENT_SIZE - 1;
        return (T) entity.getDataSource();
    }

    private static  class Entity {
        /**
         * 键值对的value
         */
        public DataSource dataSource;
        /**
         * 定时器Future
         */
        public Future future;

        public DataSource getDataSource() {
            return dataSource;
        }


        public Entity(DataSource dataSource, Future future) {
            this.dataSource = dataSource;
            this.future = future;
        }
    }




    /**
     * 判断缓存在不在
     */
    private static boolean checkCache(String cacheKey) {
        Entity cacheObj = map.get(cacheKey);
        if (cacheObj == null) {
            return false;
        }
        return true;
    }
    /**
     * 删除最近最久未使用的缓存
     */
    private static void deleteLRU() {
        String cacheKey = null;
        synchronized (CACHE_USE_LOG_LIST) {
            if (CACHE_USE_LOG_LIST.size() >= CACHE_MAX_NUMBER ) {
                cacheKey = CACHE_USE_LOG_LIST.remove(CACHE_USE_LOG_LIST.size() - 1);
            }
        }
        if (cacheKey != null) {
            remove(cacheKey);
        }
    }
    /**
     * 保存缓存的使用记录
     */
    private static synchronized void saveCacheUseLog(String cacheKey) {
        synchronized (CACHE_USE_LOG_LIST) {
            CACHE_USE_LOG_LIST.remove(cacheKey);
            CACHE_USE_LOG_LIST.add(0,cacheKey);
        }
    }

    /**
     * 检查大小
     * 当当前大小如果已经达到最大大小
     * 首先删除过期缓存,如果过期缓存删除过后还是达到最大缓存数目
     * 删除最久未使用缓存
     */
    private static void checkSize() {
        if (CURRENT_SIZE >= CACHE_MAX_NUMBER) {
            deleteLRU();
        }
    }


}
package com.wang.database.db.jdbc;

import com.wang.database.db.config.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Service;

import javax.sql.DataSource;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Title: DbJdbcUtil
 * @ProjectName demo
 * @Description: 对外提供接口,用于获取数据源,执行sql
 * @Author wangben
 * @Date 2019/7/1- 11:00
 */
@Slf4j
@Service
@PropertySource("classpath:jdbc.properties")
public class DbJdbcUtil extends ConnectionManager{


    @Autowired
    private MysqlConfig mysqlConfig;

    @Autowired
    private PostgresqlConfig postgresqlConfig;

    @Autowired
    private OracleConfig oracleConfig;

//    @Value("${mysql.datasource.driverClassName}")
//    String mysqlDriver;
//
//    @Value("${oracle.datasource.driverClassName}")
//    String oracleDriver;
//
//    @Value("${postgresql.datasource.driverClassName}")
//    String postgresqlDriver;


    /** @Description 判断数据库类型,并获得数据源
     * @param url :
     * @param username :
     * @param password :
     * @return javax.sql.DataSource
     * @throws
     * @Author wangben
     * @Date 2019/7/2 14:00
     */
    public  DataSource optDataSource(String url,String username, String password){
        log.info("DButils optDataSource begin");
        DataSource dataSource=null;
        JdbcProperties jdbcProperties=null;
        /*String mysqlDriver="com.mysql.cj.jdbc.Driver";
        String oracleDriver="oracle.jdbc.OracleDriver";
        String postgresqlDriver="org.postgresql.Driver";*/
        try {
            if(url==null&&url.length()==0){
                log.error("Url is null");
                return null;
            }else {
                String[] split = url.split(":");
                jdbcProperties = new JdbcProperties(url,null, username, password);
                String dbType = split[1];
                //去缓存查找
                dataSource = getDataSourceByCache(jdbcProperties, dbType);
            }
        }catch (Exception e){
            log.error("DbJdbcUtil optDataSource error");
        }
        log.info("DButils optDataSource end");
        return dataSource;
    }
    /** @Description 根据数据库连接的相关信息获取缓存里的数据源,没有则添加数据源到缓存。
     * 相关设置:数据源过期时间,等待超时时间,测试数据源连接是否正常。
     * @return javax.sql.DataSource
     * @throws
     * @Author wangben
     * @Date 2019/7/2 14:16
     */
    public DataSource getDataSourceByCache(JdbcProperties jdbcProperties,String dbType){
        log.info("getDataSourceByCache begin");
        //先去缓存查找
        DataSource dataSource = DataSourceCache.get(jdbcProperties.toString());
        System.out.println(dataSource);
        if(dataSource==null){
            //找不到,创建数据源,并添加到缓存
            log.info("Get cache error");
            switch(dbType){
                case "mysql" :
                    dataSource = mysqlConfig.mysqlDataSource(jdbcProperties);
                    break;
                case "postgresql" :
                    dataSource = postgresqlConfig.postgresqlDataSource(jdbcProperties);
                    break;
                case "oracle" :
                    dataSource = oracleConfig.oracleDataSource(jdbcProperties);
                    break;
                default :
                    log.error("DbType is error");
            }
            //添加到缓存
            DataSourceCache.put(jdbcProperties.toString(), dataSource);
            log.info("Add Cache end");
        }else {
            log.info("Get success from the cache");
        }
        log.info("Cache size is "+DataSourceCache.CURRENT_SIZE);
        return dataSource;
    }


    /** @Description 执行增删改sql
     * @param dataSource :
     * @param sql :
     * @param params :
     * @return boolean
     * @throws
     * @Author wangben
     * @Date 2019/7/2 13:59
     */
    public  boolean executeSQL(DataSource dataSource ,String sql, String... params) {
        log.info("DButils executeSQL begin");
        boolean flag = false;
        PreparedStatement statement = null;
        try {
            Connection connection = getConnection(dataSource);
            log.info("DButils executeSQL getConnection end");
            statement = connection.prepareStatement(sql);
            log.info("DButils executeSQL getStatement end");
            for (int i = 0, n = params.length; i < n; i++) {
                statement.setString(i + 1, params[i]);
            }
            statement.execute();
            log.info("DButils executeSQL statement execute end");
            flag = true;
        } catch (SQLException e) {
            log.error("DButils executeSQL ----",e);
        } finally {
            closeStatement(statement);
            closeConnection();
        }
        log.info("DButils executeSQL end");
        return flag;
    }


    /** @Description 执行查询sql返回List
     * @param dataSource :
     * @param sql :
     * @return java.util.List
     * @Author wangben
     * @Date 2019/7/2 13:59
     */
    public  List queryListBySql(DataSource dataSource, String sql) {
        log.info("DButils queryListBySql begin");
        ResultSet resultSet = null;
        PreparedStatement statement = null;
        List rlist=new ArrayList();
        try {
            Connection connection = getConnection(dataSource);
            log.info("DButils queryListBySql getConnection end");
            statement = connection.prepareStatement(sql);
            log.info("DButils queryListBySql getStatement end");
            resultSet = statement.executeQuery();
            log.info("DButils queryListBySql getResultSet end");
            if (resultSet != null) {
                ResultSetMetaData rsm = statement.getMetaData();
                int columnsum=rsm.getColumnCount();
                while (resultSet.next()) {
                    Map senmap=new HashMap();
                    for (int i=0;i

更简单的实现:https://blog.csdn.net/zxd8080666/article/details/79614050
补充:高并发下System.currentTimeMillis()并发问题以及优化对比 https://blog.csdn.net/qq_38011415/article/details/82813299

2019年7月16日11:39:57

//用于缓冲数据Map
public static HashMap cacheMap = new HashMap();
//时间key
public final static String TIME_KEY = “cateTime”;
//数据key
private final static String DATE_KEY = “cateList”;
//缓冲时间1小时
public final static long EXPIRATIONTIME = 1000 * 10;

if(jdbcProperties.toString(()+“time”,时间== null || !cacheMap.containsKey(dataKey)){
return false;
}
Long expiryTime = (Long) cacheMap.get(aa);
//如果当前时间大于缓存过期时间就移除map里的数据key
if (SystemClock.millisClock().now() > expiryTime.longValue()) {
cacheMap.remove(dataKey);
return false;
}


import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @Title: SystemClock
 * @ProjectName database
 * @Description:
 *  * 高并发场景下System.currentTimeMillis()的性能问题的优化
 *  * 时间戳打印建议使用
 *  * 调用示例
 *  * Long start = SystemClock.millisClock().now()
 *
 * @Author wangben
 * @Date 2019/7/3- 10:10
 */

public class SystemClock {
    private static final String THREAD_NAME = "system.clock";
    private static final SystemClock MILLIS_CLOCK = new SystemClock(1);
    private final long precision;
    private final AtomicLong now;

    private SystemClock(long precision) {
        this.precision = precision;
        now = new AtomicLong(System.currentTimeMillis());
        scheduleClockUpdating();
    }

    public static SystemClock millisClock() {
        return MILLIS_CLOCK;
    }

    private void scheduleClockUpdating() {
        ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(runnable -> {
            Thread thread = new Thread(runnable, THREAD_NAME);
            thread.setDaemon(true);
            return thread;
        });
        scheduler.scheduleAtFixedRate(() ->
                now.set(System.currentTimeMillis()), precision, precision, TimeUnit.MILLISECONDS);
    }

    public long now() {
        return now.get();
    }
}


        if(DataSourceCache.isInvalid(jdbcProperties.toString()+DataSourceCache.TIME_KEY,jdbcProperties.toString())){
            //存活时间内去查找缓存
            dataSource = (DataSource) DataSourceCache.cacheMap.get(jdbcProperties.toString());
            //更新缓存时间
            DataSourceCache.cacheMap.put(jdbcProperties.toString()+DataSourceCache.TIME_KEY, SystemClock.millisClock().now() + DataSourceCache.EXPIRATIONTIME);
        }
//找不到
 //将查出来的数据放入map
DataSourceCache.cacheMap.put(jdbcProperties.toString(), dataSources);
//设置缓存时间
DataSourceCache.cacheMap.put(jdbcProperties.toString()+DataSourceCache.TIME_KEY, SystemClock.millisClock().now() + DataSourceCache.EXPIRATIONTIME);

用两个map更好,amap(key+标记值,时间);
bmap(key,value);
先根据bmap的过滤掉不存在的key,然后通过key+标记值判断时间是否超期。都通过则拿到value,否则去查,然后添加a,bmap;

你可能感兴趣的:(DB)