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 ...
随机推荐
- C#实现迭代器
迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示.C#中使用IEnumerator接口实现,Java中使用Iterator接口实现,其中原理都差 ...
- chrome文件上传 /获取文件路径c:/fakepath的解决办法
jsp页面 <td style="text-align: left;padding-left: 20px;"> <img name="image&quo ...
- rs232转以太网
rs232转以太网 rs232转以太网ZLAN5103可以实现RS232/485/422和TCP/IP之间进行透明数据转发.方便地使得串口设备连接到以太网和Internet,实现串口设备的网络化升级. ...
- python 登陆三次错误退出
登陆出现三次错误,退出程序 1 #登陆 2 def Login(): 3 a = input() 4 if a == 'Kate': 5 b = input() 6 if b == '666666': ...
- css中伪元素before或after中content的特殊用法attr【转】
[原文]https://segmentfault.com/a/1190000002750033 CSS中主要的伪元素有四个:before/after/first-letter/first-line,在 ...
- MySQL数据库基础-2范式
数据库结构设计 范式 设计数据库的规范 第12345范式,凡是之间有依赖关系. 关系模型的发明者埃德加·科德最早提出这一概念,并于1970 年代初定义了第一范式.第二范式和第三范式的概念 设计关系数据 ...
- Codeforces Round 665 赛后解题报告(暂A-D)
Codeforces Round 665 赛后解题报告 A. Distance and Axis 我们设 \(B\) 点 坐标为 \(x(x\leq n)\).由题意我们知道 \[\mid(n-x)- ...
- Vue留言 checked框案列
在命令行窗口输入vue create "工程名"命令 来创建vue脚手架
- Linux入门到放弃之六《磁盘和文件系统管理一》
要求:创建卷组名为 mail_store:逻辑卷名 mail,从卷组mail_store上划出50GB空间, 使用mkfs命令创建ext3文件系统,并实现开机自动挂载,挂载点/mailbox: (1) ...
- BootStrapValidator表单验证插件的学习和使用
BootStrapValidator表单验证插件的学习和使用 引入标签 <script type="text/javascript" src="https://cd ...