Java实现本地小数据量缓存尝试与实践&设计思考
话不多说先贴代码
/**
* 缓存工具
*/
public class ConcurrentHashMapCacheUtils{ /**
* 当前缓存个数
*/
public static Integer CURRENT_SIZE = 0; /**
* 时间一分钟
*/
static final Long ONE_MINUTE = 60 * 1000L; /**
* 缓存超时
*/
private static final Long TTL_TIME = 60 * 1000L; /**
* 缓存对象
*/
private static final ConcurrentHashMap<String, CacheObj> CACHE_OBJECT_MAP = new ConcurrentHashMap<>(); /**
* 清理过期缓存是否在运行
*/
private static volatile Boolean CLEAN_THREAD_IS_RUN = false; /**
* 设置缓存
*/
public static void setCache(String cacheKey, String cacheValue, long cacheTime) {
Long ttlTime = null;
if (cacheTime <= 0L) {
if (cacheTime == -1L) {
ttlTime = -1L;
} else {
return;
}
}
CURRENT_SIZE = CURRENT_SIZE + 1;
if (ttlTime == null) {
ttlTime = System.currentTimeMillis() + cacheTime;
}
CacheObj cacheObj = new CacheObj(cacheValue, ttlTime);
CACHE_OBJECT_MAP.put(cacheKey, cacheObj);
} /**
* 设置缓存
*/
public static void setCache(String cacheKey, String cacheValue) {
setCache(cacheKey, cacheValue, TTL_TIME);
} public static long getCurrentSize(){
return CACHE_OBJECT_MAP.mappingCount();
} public static List<String> getRecentApp(){
List<String> list = new ArrayList<>(16);
for (String key:CACHE_OBJECT_MAP.keySet()){
list.add(key);
}
return list;
} /**
* 获取缓存
*/
public static String getCache(String cacheKey) {
startCleanThread();
if (checkCache(cacheKey)) {
return CACHE_OBJECT_MAP.get(cacheKey).getCacheValue();
}
return null;
} /**
* 删除某个缓存
*/
public static void deleteCache(String cacheKey) {
Object cacheValue = CACHE_OBJECT_MAP.remove(cacheKey);
if (cacheValue != null) {
CURRENT_SIZE = CURRENT_SIZE - 1;
}
}
/**
* 判断缓存在不在,过没过期
*/
private static boolean checkCache(String cacheKey) {
CacheObj cacheObj = CACHE_OBJECT_MAP.get(cacheKey);
if (cacheObj == null) {
return false;
}
if (cacheObj.getTtlTime() == -1L) {
return true;
}
if (cacheObj.getTtlTime() < System.currentTimeMillis()) {
deleteCache(cacheKey);
return false;
}
return true;
} /**
* 删除过期的缓存
*/
static void deleteTimeOut() {
List<String> deleteKeyList = new LinkedList<>();
for(Map.Entry<String, CacheObj> entry : CACHE_OBJECT_MAP.entrySet()) {
if (entry.getValue().getTtlTime() < System.currentTimeMillis() && entry.getValue().getTtlTime() != -1L) {
deleteKeyList.add(entry.getKey());
}
}
for (String deleteKey : deleteKeyList) {
deleteCache(deleteKey);
}
} /**
* 设置清理线程的运行状态为正在运行
*/
static void setCleanThreadRun() {
CLEAN_THREAD_IS_RUN = true;
} /**
* 开启清理过期缓存的线程
*/
private static void startCleanThread() {
if (!CLEAN_THREAD_IS_RUN) {
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNamePrefix("clean-cache-pool-").build();
ThreadPoolExecutor cleanThreadPool = new ThreadPoolExecutor(
8,
16,
60L,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(8),
namedThreadFactory
);
cleanThreadPool.execute(new CleanTimeOutThread()); } } } class CacheObj {
/**
* 缓存对象
*/
private String cacheValue;
/**
* 缓存过期时间
*/
private Long ttlTime; CacheObj(String cacheValue, Long ttlTime) {
this.cacheValue = cacheValue;
this.ttlTime = ttlTime;
} String getCacheValue() {
return cacheValue;
} Long getTtlTime() {
return ttlTime;
} @Override
public String toString() {
return "CacheObj {" +
"cacheValue = " + cacheValue +
", ttlTime = " + ttlTime +
'}';
}
} /**
* 每一分钟清理一次过期缓存
*/
class CleanTimeOutThread implements Runnable{ private static Logger logger = LoggerFactory.getLogger(CleanTimeOutThread.class); @Override
public void run() {
ConcurrentHashMapCacheUtils.setCleanThreadRun();
while (true) {
ConcurrentHashMapCacheUtils.deleteTimeOut();
try {
Thread.sleep(ConcurrentHashMapCacheUtils.ONE_MINUTE);
} catch (InterruptedException e) {
logger.error("Time-out Cache has not been cleaned!{}", e.getMessage());
}
if(1==2){
break;
}
}
} }
1、背景
在公司对某个开源组件的使用中,频繁出现客户端无法请求到数据的情况,经排查是发生了并发数过大数据库性能瓶颈的情况。
于是有了对服务端的优化喝如下的思考。
2、设计思考
2.1、是否选择缓存
直接查询DB还是添加缓存,这个取决于系统的并发数,如果系统并发数数据库性能足以支持,则无使用缓存的必要。
如果选择使用缓存,则需要面对的一个风险是:
服务启动/重启的瞬间会出现大量对于数据库的请求,容易发生缓存的击穿/雪崩情况。
关于这种情况我做了专门的优化来避免出现缓存击穿/雪崩,这一段的代码后面优化后再上。
2.2、缓存种类的选择
2.2.1、Java内存
优点:
速度快
无额外网络开销
系统复杂度低
缺点:
受限于热点数据数量,对应用内存大小有要求
大量缓存同时失效会发生雪崩导致服务性能瞬间下降
存在击穿风险
多实例存在缓存一致性问题,可能出现对一条数据的重复查询
2.2.2、redis
优点:
支持大量数据缓存,扩展性好
在多实例时不需要考虑缓存一致性问题
缺点:
系统依赖redis,如果redis不可用会导致系统不可用
存在击穿风险
Java实现本地小数据量缓存尝试与实践&设计思考的更多相关文章
- Win环境下Oracle小数据量数据库的物理备份
Win环境下Oracle小数据量数据库的物理备份 环境:Windows + Oracle 单实例 数据量:小于20G 重点:需要规划好备份的路径,建议备份文件和数据库文件分别存在不同的存储上. 1.开 ...
- day06 内存地址 小数据池缓存机制
1. 内存相关 示例一 v1=[11,22,33] v2=[11,22,33] #值相等 内存地址不等 v1=11 v2=11 #按理说内存地址应该不等,但是python为了优化使其内存地址相等 v1 ...
- java 导出Excel 大数据量,自己经验总结!
出处: http://lyjilu.iteye.com/ 分析导出实现代码,XLSX支持: /** * 生成<span style="white-space: normal; back ...
- 【Easyexcel】java导入导出超大数据量的xlsx文件 解决方法
解决方法: 使用easyexcel解决超大数据量的导入导出xlsx文件 easyexcel最大支持行数 1048576. 官网地址: https://alibaba-easyexcel.github. ...
- windows 系统下,小数据量Oracle用户物理备份
环境:windows Server 2003 oracle 10g,系统间备份 目标系统创建共享文件,原系统挂载共享目录 写批处理脚本,用任务计划定时调用 Rem * 由于系统实时性要求不是很高,数据 ...
- 小数据量的Key-Value查找类的实现
平时写程序时经常要把一些Key与Value保存起来,但一般数据量都不大,故不想用TStringHash来做.而用TStringList来做,还要写一个"=",挺别扭!而且数据类型还 ...
- java 导出Excel 大数据量,自己经验总结!(二)
在上一次的基础上加上了样式,以及中文列名 package com.tommy.fundation.util; import java.io.OutputStream; import java.util ...
- hihocoder #1062 : 最近公共祖先·一(小数据量 map+set模拟+标记检查 *【模板】思路 )
#1062 : 最近公共祖先·一 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Ho最近发现了一个神奇的网站!虽然还不够像58同城那样神奇,但这个网站仍然让小Ho乐在 ...
- poj 1679 The Unique MST 【次小生成树+100的小数据量】
题目地址:http://poj.org/problem?id=1679 2 3 3 1 2 1 2 3 2 3 1 3 4 4 1 2 2 2 3 2 3 4 2 4 1 2 Sample Outpu ...
随机推荐
- 【转】Linux-CentOS7设置程序开启自启步骤!
链接:https://blog.csdn.net/wang123459/article/details/79063703
- 正式班D9
2020.10.16星期五 正式班D9 一.vmware workstation的使用 虚拟机管理软件 定义 虚拟机(Virtual Machine)软件是一套特殊的软件,它可以作为操作系统独立运行, ...
- 基于python实现顺序存储的队列代码
""" 队列-顺序存储 seqqueue.py 代码实现 """ # 自定义异常类 class QueueError(Exception): ...
- day69:Vue:组件化开发&Vue-Router&Vue-client
目录 组件化开发 1.什么是组件? 2.局部组件 3.全局组件 4.父组件向子组件传值 5.子组件往父组件传值 6.平行组件传值 Vue-Router的使用 Vue自动化工具:Vue-Client 组 ...
- ansible使用playbook的简单例子(ansible2.9.7)
一,ansible使用playbook的优点 1,用ansible执行一些简单的任务,使用ad-hoc命令就可以解决问题 如果执行复杂的功能,需要大量的操作,执行的ad-hoc命令会不够方便,这时我们 ...
- nginx优化: timeout超时配置
一,为什么要做连接超时设置? nginx在保持着与客户端的连接时,要消耗cpu/内存/网络等资源, 如果能在超出一定时间后自动断开连接, 则可以及时释放资源,起到优化性能.提高效率的作用 说明:刘宏缔 ...
- poj3178 Roping the Field (计算几何 + dp)
Roping the Field Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 858 Accepted: 250 De ...
- Linux入门到放弃之四《磁盘管理》
一,磁盘管理 1.添加一个新磁盘/dev/sdb,用fdisk工具给磁盘分区,要求:一个主分区,一个扩展分区,两个逻辑分区: (1)去虚拟机设置添加一块硬盘,大小自定义 (2)重启系统 命令:rebo ...
- apktool的下载,安装,反编译和重新打包
一.环境要求 安装java 1.8 以上 命令行运行 java -version 返回版本大于1.8 如果没有,请安装java 1.8 二.下载与安装 下载apktool_x.x.x.jar到本地 官 ...
- MapReduce工作原理详解
文章概览: 1.MapReduce简介 2.MapReduce有哪些角色?各自的作用是什么? 3.MapReduce程序执行流程 4.MapReduce工作原理 5.MapReduce中Shuffle ...