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 ...
随机推荐
- 制作iconfont放到自己的公共组件库
我们公司的icon是UI提供svg,我们转成iconfont. 这里就不详细说明怎么制作svg,可以上网搜一下,https://www.iconfont.cn/help/detail?spm=a313 ...
- Go 并发操作
goroutine 在其他的编程语言中,线程调度是交由os来进行处理的. 但是在Go语言中,会对此做一层封装,Go语言中的并发由goroutine来实现,它类似于用户态的线程,更类似于其他语言中的协程 ...
- uc浏览器手机版,页面图片不显示
uc浏览器手机版,有时候上面的轮播广告看不到 原因:uc浏览器会拦截所有带ad的标签 例如: <div id="adDiv"> <img src="/r ...
- 营口6378.7939(薇)xiaojie:营口哪里有xiaomei
营口哪里有小姐服务大保健[微信:6378.7939倩儿小妹[营口叫小姐服务√o服务微信:6378.7939倩儿小妹[营口叫小姐服务][十微信:6378.7939倩儿小妹][营口叫小姐包夜服务][十微信 ...
- jquery的实时触发事件(textarea实时获取中文个数)
jquery的实时触发事件(textarea实时获取中文个数) (2014-09-16 11:49:50) 转载▼ 标签: 实时触发事件 中文个数 onpropertychange oninput o ...
- go 下载图片
package main import ( "net/http" "fmt" "io/ioutil" "strings" ...
- Curl可以模拟浏览器
curl直接访问被拒绝 [22:10:00 root@C7 ~]#curl -I www.163.com HTTP/1.1 403 Forbidden Date: Wed, 24 Jun 2020 0 ...
- Vue踩坑日记-You may use special comments to disable some warnings. Use // eslint-disable-next-line to ignore the next line. Use /* eslint-disable */ to ignore all warnings in a file.
记录时间:2019年4月24日16:55:54 在build/webpack.base.conf.js文件中,注释或者删除掉:module->rules中有关eslint的规则
- Vue 过滤器入门
Vue 允许自定义过滤器,可被用于一些常见的文本格式化 过滤器可以用在两个地方:双花括号插值和 v-bind 表达式 过滤器应该被添加在JavaScript表达式的尾部,由"管道" ...
- 010_Java历史及特性
目录 Java历史 C语言 1972年 C++ 1982年 Java 1995年 Applet 图形界面的程序 J2SE Java 2 标准版 桌面 J2ME Java 2 移动版 手机 J2EE J ...