Use Spring Ehcache secondary cache to optimize query performance

In the recent time to optimize the system and found that the efficiency of queries Some queries slow, time-consuming long,

It found by measuring the pressure, which consumes consumption performance database query, the query redis

Database: connection pool is limited, and a single query can not consume a lot of connection pooling, taking up a lot of IO, otherwise it will cause abnormal IO connection pool and the entire application and database-related performance of abnormal pressure, preventing access

redis: reidis is single-threaded, if a large number of queries will access the cache, which can cause abnormal and lead to redis IO slow queries, redis congestion, redis crashes and other issues.

Note:  the relevant transformation in the figure below, is suitable for less frequent changes of data, but a large number of related infrastructure will be frequently accessed data,

Such as  pictures, advertising, basic data, configuration data, data dictionary and other relevant data, not suitable for high dynamic requirements of data for data consistency and data latency of higher

 The figure is a schematic diagram of call before optimization

Schematic of optimization

redis is distributed cache, according to a separate key can query data to specific content, but because of its distributed, need access to the network need to consume IO performance, and in the case of high power generation business environment, the bandwidth of the server are IO test

EHCACHE use as cache memory, priority access memory cache, the case does not exist, priority will redis cache data into the cache EHCACHE each application alone by configuring EHCACHE life cycle, automatically and periodically clean the cache to improve performance to achieve of

The business of the rectification, the rectification process, using Ehcache, Redis is optimized, while the cache data structure redis made changes to the original data structure redis using the form hset, resulting in pressure when measured, KEY concentrated in the same server, resulting in abnormal redis acquired, so this into a set form, so that the key redis dispersion treatment, is no longer focused on the same key, so that more improved performance

 

So much nonsense, direct that focus on introduced access EHCACHE

The transformation, mainly for basic data access

1: Increase in the resource configuration file for the ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
    <!-- 磁盘缓存位置 -->
    <diskStore path="java.io.tmpdir/ehcache"/>
    <!-- 默认缓存 -->
    <defaultCache
            maxEntriesLocalHeap="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxEntriesLocalDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU"/>
    <cache name="configInfo"
           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/ehcache.xml"/>
    </bean>
</beans>

3:配置注解,构建需要对缓存的方法(以下代码只是示例) 

    @Cacheable(value = "configInfo") 
    public String queryNameByParam(String typeName,String param){ 
        String name = null;
        if(StringUtils.isBlank(typeName) || StringUtils.isBlank(param)){
            return null;
        }
        String key = getRedisKey(typeName,param);//先查询redis缓存,如果redis缓存存在,则使用redis缓存
        name = redisClient.get(key);
        if(StringUtils.isBlank(name)){
            User user = getUserInfo(typeName,param);//查询数据库,查询配置
            if(null!=user && StringUtils.isNotBlank(user.getName())){
                name = user.getName();
                redisBean.set(key,name,60*60*24);//存储到redis中
            }
        }
        return name;
    }

 

关键字 :

@Cacheable(value = "config") ,configInfo必须要与ehcache中的配置要一致,否则会抛出异常等情况

Spring 使用@Cacheable添加缓存是基于面向切面的思想做的, 实际上就是使用Java动态代理,创建实例的时候注入的是代理对象,在代理对象里调用实际的对象,这样就可以在实际的方法执行前,处理一下缓存的逻辑:没有找到缓存就往下执行,执行完把结果加入到缓存中;找到缓存则直接返回缓存的结果,不调用执行实际的方法。

关于Ehcache注解不生效的坑

一个方法A调同一个类里的另一个有缓存注解的方法B,这样是不走缓存的。

使用@Cacheable添加缓存实际上就是使用动态代理做的,在代理的方法前后做缓存的相应处理。单独的去调方法B是有缓存的,但是如果调方法A,A里面再去调B方法,哪怕B方法配置了缓存,也是不会生效的。

解决方法:
 a、不使用注解的方式,直接取 Ehcache 的 CacheManger 对象,把需要缓存的数据放到里面,类似于使用 Map,缓存的逻辑自己控制
 b、把方法A和方法B放到两个不同的类里面,例如:如果两个方法都在service接口里,把方法B放到另一个service里面,这样A方法里调B方法,就可以使用B方法的缓存

 

 4:按照以上方式,那么基本就完成了EHCACHE的基本配置了,EHCACHE就能缓存,可以通过debug的方式进行测试或者加入日志的方式进行测试

 

Guess you like

Origin www.cnblogs.com/lewisat/p/11202714.html