使用Spring Ehcache二级缓存优化查询性能
最近在对系统进行优化的时候,发现有些查询查询效率比较慢,耗时比较长,
通过压测发现,主要耗费的性能 消耗在 查询数据库,查询redis
数据库:连接池有限,且单个查询不能消耗大量的连接池,占用大量IO,否则会引起整个应用的IO异常及连接池异常及数据库相关性能压力,导致无法访问
redis:reidis是单线程的,如果大量的查询都会存 取 缓存,这样会导致IO异常及导致redis 慢查询,redis拥堵,redis崩溃等问题.
注意: 下图的相关改造,适用于不太频繁改动的数据,但是会大量频繁访问的相关基础数据,
如 图片,广告,基础数据,相关配置数据,数据字典等相关数据, 不适合动态要求较高的数据.且对数据一致性延时性较高的数据
下图是优化之前的调用示意图

优化之后的示意图
redis是分布式缓存,根据单独的key可以查询到具体的内容数据,但是由于其分布式,需要访问网络需要消耗IO性能,在高并发电商环境的情况下,对带宽对IO对服务器都是考验
使用EHCACHE作为内存缓存,优先访问内存缓存,不存在的情况下,优先将redis缓存数据放到每个应用独自的EHCACHE的缓存中,通过配置EHCACHE的生命周期,定期自动清理缓存,来达到提高性能的

对业务进行了整改,整改过程中,使用了Ehcache,Redis进行优化,同时对redis的缓存数据结构进行了更改, 原先redis的数据结构使用了hset的形式,导致在压测的时候,KEY集中在同一个服务器,导致redis获取异常,故本次改成了set形式,使redis的key分散处理,不再集中在相同的key中,这样更能提高性能
废话这么多,直接说重点,介绍接入EHCACHE
本次改造,主要是对基础数据进行接入
1:在resource下增加配置文件为myehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<!-- 磁盘缓存位置 -->
<diskStore path="java.io.tmpdir/myehcache"/>
<!-- 默认缓存 -->
<defaultCache
maxEntriesLocalHeap="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxEntriesLocalDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"/>
<cache name="testcache"
maxElementsInMemory="50000"
eternal="false"
timeToIdleSeconds="30"
timeToLiveSeconds="30"
overflowToDisk="false"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>
ehcache的相关配置参数:
diskStore :指定数据存储位置,可指定磁盘中的文件夹位置
defaultCache :默认的管理策略
以下属性是必须的:
name :Cache的名称,必须是唯一的(ehcache会把这个cache放到HashMap里).maxElementsInMemory:在内存中缓存的element的最大数目.
maxElementsOnDisk:在磁盘上缓存的element的最大数目,默认值为0,表示不限制.
eternal:设定缓存的elements是否永远不过期.如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断.
overflowToDisk:如果内存中数据超过内存限制,是否要缓存到磁盘上.
以下属性是可选的:
timeToIdleSeconds:对象空闲时间,指对象在多长时间没有被访问就会失效.只对eternal为false的有效.默认值0,表示一直可以访问.
timeToLiveSeconds:对象存活时间,指对象从创建到失效所需要的时间.只对eternal为false的有效.默认值0,表示一直可以访问.
diskPersistent:是否在磁盘上持久化.指重启jvm后,数据是否有效.默认为false.
diskExpiryThreadIntervalSeconds:对象检测线程运行时间间隔.标识对象状态的线程多长时间运行一次.
diskSpoolBufferSizeMB: DiskStore使用的磁盘大小,默认值30MB.每个cache使用各自的DiskStore.
memoryStoreEvictionPolicy:如果内存中数据超过内存限制,向磁盘缓存时的策略.默认值LRU,可选FIFO、LFU.
缓存的3种清空策略:
FIFO ,first in first out(先进先出).
LFU , Less Frequently Used(最少使用),意思是一直以来最少被使用的.缓存的元素有一个HIT属性,HIT值最小的将会被清出缓存.
LRU ,Least Recently Used(最近最少使用)(EHCACHE 默认值).缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,
那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存. 注意:
timeToIdleSeconds:
指对象在多长时间没有被访问就会失效.只对eternal为false的有效.默认值0,表示一直可以访问,如果配置了timeToIdleSeconds不为0,且timeToLiveSeconds也不为0,则在这个timeToIdleSeconds时间内没有被访问,也同时会过期释放
timeToLiveSeconds:
指对象从创建到失效所需要的时间.只对eternal为false的有效.默认值0,表示一直可以访问.
如果仅仅配置timeToLiveSeconds,而timeToIdleSeconds配置为0,则表示缓存一直活跃,但是到了这个周期,则自动失效 举例:
timeToIdleSeconds 20 ,timeToLiveSeconds 30 说明:在20秒内,如果没有被访问,则自动过期,如果一直有被访问,在30秒内,依然过期.
timeToIdleSeconds 0 ,timeToLiveSeconds 30 说明:无论有没有被访问,在30秒内,自动过期.
2:在建一个spring-ehcache.xml, 并引用到applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation=" http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache-3.1.xsd"> <!-- 自动扫描注解的bean -->
<context:component-scan base-package="com.beijing.cache" />
<!-- 注册EhCache注解驱动,使spring能识别EhCache的注解 -->
<cache:annotation-driven cache-manager="cacheManager" />
<!-- 注册EhCache缓存管理器 -->
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="ehcache"/>
</bean>
<!-- 构建EhCache的缓存管理工厂 -->
<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:conf/myehcache.xml"/>
</bean>
</beans>
3:配置注解,构建需要对缓存的方法(以下代码只是示例)
@Cacheable(value = "testcache")
public String queryUserInfoByParam(String type,String name){
String name = null;
if(StringUtils.isBlank(type) || StringUtils.isBlank(name)){
return null;
}
String key = getCacheKey(type,name);//先查询redis缓存,如果redis缓存存在,则使用redis缓存
name = redisClient.get(key);
if(StringUtils.isBlank(name)){
User user = getInfo(type,name);//查询数据库,查询配置
if(null!=user && StringUtils.isNotBlank(user.getName())){
name = user.getName();
redisClient.set(key,name,60*30);//存储到redis中
}
}
return name;
}
关键字 :
@Cacheable(value = "testcache") ,testcache必须要与ehcache中的配置要一致,否则会抛出异常等情况
Spring 使用@Cacheable添加缓存是基于面向切面的思想做的, 实际上就是使用Java动态代理,创建实例的时候注入的是代理对象,在代理对象里调用实际的对象,这样就可以在实际的方法执行前,处理一下缓存的逻辑:没有找到缓存就往下执行,执行完把结果加入到缓存中;找到缓存则直接返回缓存的结果,不调用执行实际的方法。
关于Ehcache注解不生效的坑
一个方法A调同一个类里的另一个有缓存注解的方法B,这样是不走缓存的。
使用@Cacheable添加缓存实际上就是使用动态代理做的,在代理的方法前后做缓存的相应处理。单独的去调方法B是有缓存的,但是如果调方法A,A里面再去调B方法,哪怕B方法配置了缓存,也是不会生效的。
4:按照以上方式,那么基本就完成了EHCACHE的基本配置了,EHCACHE就能缓存,可以通过debug的方式进行测试或者加入日志的方式进行测试
使用Spring Ehcache二级缓存优化查询性能的更多相关文章
- Redis 优化查询性能
一次使用 Redis 优化查询性能的实践 应用背景 有一个应用需要上传一组ID到服务器来查询这些ID所对应的数据,数据库中存储的数据量是7千万,每次上传的ID数量一般都是几百至上千数量级别. 以前 ...
- SSH框架搭建步骤总结以及Hibernate二级缓存,查询缓存
二级缓存.查询缓存 一级缓存: 默认启动,生命周期是和session同步的,session独享 二级缓存: 需要加载配置信息,生命周期是和应用服务器同步,session共享 1:在hibernate. ...
- 一次使用 Redis 优化查询性能的实践
因为我的个人网站 restran.net 已经启用,博客园的内容已经不再更新.请访问我的个人网站获取这篇文章的最新内容,一次使用 Redis 优化查询性能的实践 应用背景 有一个应用需要上传一组ID到 ...
- mybatis缓存,包含一级缓存与二级缓存,包括ehcache二级缓存
一,引言 首先我们要明白一点,缓存所做的一切都是为了提高性能.明白了这一点下面我们开始进入正题. 二,mybatis缓存概要 ①.mybatis的缓存有两种,分别是一级缓存和二级缓存.两者都属于查询缓 ...
- 【分布式搜索引擎】Elasticsearch如何部署以及优化查询性能
一.Elasticsearch生产集群如何部署 (1)es生产集群部署5台机器,若每台机器是6核64G的,那么集群总内存是320G (2)假如我们es集群的日增量数据大概是2000万条,每天日增量数据 ...
- Hibernate中 一 二级缓存及查询缓存(1)
最近趁有空学习了一下Hibernate的缓存,其包括一级缓存,二级缓存和查询缓存(有些是参照网络资源的): 一.一级缓存 一级缓存的生命周期和session的生命周期一致,当前sessioin ...
- Hibernate一级缓存、二级缓存以及查询缓存的关系
转载自http://blog.csdn.net/maoyeqiu/article/details/50209893 前两天总结了一下二级缓存和查询缓存的关系,但是又有一个新的问题,就是查询缓存缓存到二 ...
- MyBatis ehcache二级缓存
ehcache二级缓存的开启步骤: 1.导入jar 2.在映射文件中指定用的哪个缓存 3.加一个配置文件,这个配置文件在ehcache jar包中就有 使增删改对二级缓存不刷新: 对一级缓存没有用的, ...
- Hibernate中 一 二级缓存及查询缓存(2)
缓存:缓存是什么,解决什么问题? 位于速度相差较大的两种硬件/软件之间的,用于协调两者数据传输速度差异的结构,均可称之为缓存Cache.缓存目的:让数据更接近于应用程序,协调速度不匹配,使访问速度更 ...
随机推荐
- 【SoloPi】SoloPi使用3-性能测试-启动时间测试
响应耗时计算工具Soloπ响应耗时计算工具,通过录屏分帧的方式自动识别起始点和结束点,精确计算耗时. 特性模拟用户视觉,计算结果更贴近用户体验自动记录点击起始点,自动识别屏幕变化结束点通过OpenCV ...
- Go 操作 Mysql(一)
关于 Go 的标准库 database/sql 和 sqlx database/sql 是 Go 操作数据库的标准库之一,它提供了一系列接口方法,用于访问数据库(mysql,sqllite,oralc ...
- Arc Engine二次开发——弹窗进行属性查询
在Arcmap中使用Sapefile格式的矢量数据时,经常会用到其属性查询的功能,弹出窗体然后用户鼠标点击或手动输入查询条件,进而查询到感兴趣的要素.在AE二次开发中也经常需要这个功能,于是在此记录整 ...
- java保证多线程的执行顺序
1. java多线程环境中,如何保证多个线程按指定的顺序执行呢? 1.1 通过thread的join方法保证多线程的顺序执行, wait是让主线程等待 比如一个main方法里面先后运行thread1, ...
- JAVA多线程之UncaughtExceptionHandler——处理非正常的线程中止
JAVA多线程之UncaughtExceptionHandler——处理非正常的线程中止 背景 当单线程的程序发生一个未捕获的异常时我们可以采用try....catch进行异常的捕获,但是在多线程环境 ...
- unittest 运行slenium(四)---通过指定用例的形式运行用例
一: 说明 跟数据驱动唯一的区别为用例数据获取时,及运行方式不同. 其它都基本相同,可参考https://www.cnblogs.com/xiaodingdong/p/11753220.html 二: ...
- 解析CentOS 7中系统文件与目录管理
Linux目录结构 Linux目录结构是树形的目录结构 根目录 所有分区.目录.文件等的位置起点 整个树形目录结构中,使用独立的一个"/"表示 常见的子目录 目录 目录名称 目录 ...
- Qt中C++与QML交互
###main.c部分int main(int argc, char *argv[]){ QString info1 = "xxxxxxxxxxx"; QString ...
- Windows——系统盘重置密码
一.制作好系统启动U盘 软碟通自己制作即可 二.这进入到安装前界面按Shift+F10调出命令提示符 三.输入regedit后按回车进入注册表编辑器 四. 左键单击选中HKEY_LOCAL_MACHI ...
- reverse函数的实现
用递归的方法实现字符串的倒叙 #include <string.h> void reverse_my(char *a,int len) { int tmp;//中间值 if(len < ...