最近做一个根据前端的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
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;