關(guān)于緩存
緩存是實(shí)際工作中非常常用的一種提高性能的方法于微。而在java中丐怯,所謂緩存耍攘,就是將程序或系統(tǒng)經(jīng)常要調(diào)用的對(duì)象存在內(nèi)存中低飒,再次調(diào)用時(shí)可以快速?gòu)膬?nèi)存中獲取對(duì)象渴频,不必再去創(chuàng)建新的重復(fù)的實(shí)例。這樣做可以減少系統(tǒng)開銷将塑,提高系統(tǒng)效率脉顿。
在增刪改查中,數(shù)據(jù)庫(kù)查詢占據(jù)了數(shù)據(jù)庫(kù)操作的80%以上抬旺,而非常頻繁的磁盤I/O讀取操作弊予,會(huì)導(dǎo)致數(shù)據(jù)庫(kù)性能極度低下。而數(shù)據(jù)庫(kù)的重要性就不言而喻了:
- 數(shù)據(jù)庫(kù)通常是企業(yè)應(yīng)用系統(tǒng)最核心的部分
- 數(shù)據(jù)庫(kù)保存的數(shù)據(jù)量通常非常龐大
- 數(shù)據(jù)庫(kù)查詢操作通常很頻繁开财,有時(shí)還很復(fù)雜
在系統(tǒng)架構(gòu)的不同層級(jí)之間汉柒,為了加快訪問(wèn)速度,都可以存在緩存
image
spring cache 特性與缺憾
現(xiàn)在市場(chǎng)上主流的緩存框架有ehcache责鳍、redis碾褂、memcached。spring cache可以通過(guò)簡(jiǎn)單的配置就可以搭配使用起來(lái)历葛。其中使用注解方式是最簡(jiǎn)單的正塌。
image
Cache注解
從以上的注解中可以看出,雖然使用注解的確方便恤溶,但是缺少靈活的緩存策略乓诽,
緩存策略:
- TTL(Time To Live ) 存活期,即從緩存中創(chuàng)建時(shí)間點(diǎn)開始直到它到期的一個(gè)時(shí)間段(不管在這個(gè)時(shí)間段內(nèi)有沒(méi)有訪問(wèn)都將過(guò)期)
- TTI(Time To Idle) 空閑期咒程,即一個(gè)數(shù)據(jù)多久沒(méi)被訪問(wèn)將從緩存中移除的時(shí)間
項(xiàng)目中可能有很多緩存的TTL不相同鸠天,這時(shí)候就需要編碼式使用編寫緩存。
條件緩存
根據(jù)運(yùn)行流程帐姻,如下@Cacheable
將在執(zhí)行方法之前( #result還拿不到返回值)判斷condition
稠集,如果返回true
,則查緩存饥瓷;
@Cacheable(value = "user", key = "#id", condition = "#id lt 10")
public User conditionFindById(final Long id)
如下@CachePut
將在執(zhí)行完方法后(#result就能拿到返回值了)判斷condition
剥纷,如果返回true
,則放入緩存
@CachePut(value = "user", key = "#id", condition = "#result.username ne 'zhang'")
public User conditionSave(final User user)
如下@CachePut
將在執(zhí)行完方法后(#result就能拿到返回值了)判斷unless
呢铆,如果返回false
晦鞋,則放入緩存;(即跟condition
相反)
@CachePut(value = "user", key = "#user.id", unless = "#result.username eq 'zhang'")
public User conditionSave2(final User user)
如下@CacheEvict
棺克, beforeInvocation=false
表示在方法執(zhí)行之后調(diào)用(#result能拿到返回值了)鳖宾;且判斷condition
,如果返回true
逆航,則移除緩存;
@CacheEvict(value = "user", key = "#user.id", beforeInvocation = false, condition = "#result.username ne 'zhang'")
public User conditionDelete(final User user)
- 小試牛刀渔肩,綜合運(yùn)用:
@CachePut(value = "user", key = "#user.id")
public User save(User user) {
users.add(user);
return user;
}
@CachePut(value = "user", key = "#user.id")
public User update(User user) {
users.remove(user);
users.add(user);
return user;
}
@CacheEvict(value = "user", key = "#user.id")
public User delete(User user) {
users.remove(user);
return user;
}
@CacheEvict(value = "user", allEntries = true)
public void deleteAll() {
users.clear();
}
@Cacheable(value = "user", key = "#id")
public User findById(final Long id) {
System.out.println("cache miss, invoke find by id, id:" + id);
for (User user : users) {
if (user.getId().equals(id)) {
return user;
}
}
return null;
}
配置ehcache與redis
- spring cache集成ehcache因俐,spring-ehcache.xml主要內(nèi)容:
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>${ehcache.version}</version>
</dependency>
<!-- Spring提供的基于的Ehcache實(shí)現(xiàn)的緩存管理器 -->
<!-- 如果有多個(gè)ehcacheManager要在bean加上p:shared="true" -->
<bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:xml/ehcache.xml"/>
</bean>
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="ehcacheManager"/>
<property name="transactionAware" value="true"/>
</bean>
<!-- cache注解,和spring-redis.xml中的只能使用一個(gè) -->
<cache:annotation-driven cache-manager="cacheManager" proxy-target-class="true"/>
- spring cache集成redis,spring-redis.xml主要內(nèi)容:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.8.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.4.2</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<!-- 注意需要添加Spring Data Redis等jar包 -->
<description>redis配置</description>
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.pool.maxIdle}"/>
<property name="maxTotal" value="${redis.pool.maxActive}"/>
<property name="maxWaitMillis" value="${redis.pool.maxWait}"/>
<property name="testOnBorrow" value="${redis.pool.testOnBorrow}"/>
<property name="testOnReturn" value="${redis.pool.testOnReturn}"/>
</bean>
<!-- JedisConnectionFactory -->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${redis.master.ip}"/>
<property name="port" value="${redis.master.port}"/>
<property name="poolConfig" ref="jedisPoolConfig"/>
</bean>
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
p:connectionFactory-ref="jedisConnectionFactory">
<property name="keySerializer">
<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"></bean>
</property>
<property name="valueSerializer">
<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
</property>
<property name="hashKeySerializer">
<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
</property>
<property name="hashValueSerializer">
<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
</property>
</bean>
<!--spring cache-->
<bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager"
c:redisOperations-ref="redisTemplate">
<!-- 默認(rèn)緩存10分鐘 -->
<property name="defaultExpiration" value="600"/>
<property name="usePrefix" value="true"/>
<!-- cacheName 緩存超時(shí)配置抹剩,半小時(shí)撑帖,一小時(shí),一天 -->
<property name="expires">
<map key-type="java.lang.String" value-type="java.lang.Long">
<entry key="halfHour" value="1800"/>
<entry key="hour" value="3600"/>
<entry key="oneDay" value="86400"/>
<!-- shiro cache keys -->
<entry key="authorizationCache" value="1800"/>
<entry key="authenticationCache" value="1800"/>
<entry key="activeSessionCache" value="1800"/>
</map>
</property>
</bean>
<!-- cache注解澳眷,和spring-ehcache.xml中的只能使用一個(gè) -->
<cache:annotation-driven cache-manager="cacheManager" proxy-target-class="true"/>
項(xiàng)目中注解緩存只能配置一個(gè)胡嘿,所以可以通過(guò)以下引入哪個(gè)配置文件來(lái)決定使用哪個(gè)緩存。
<import resource="classpath:spring/spring-ehcache.xml"/>
![image](http://upload-images.jianshu.io/upload_images/10406068-8fbb94211c475235?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
<!-- <import resource="classpath:spring/spring-redis.xml"/>-->
當(dāng)然钳踊,可以通過(guò)其他配置搭配使用兩個(gè)緩存機(jī)制衷敌。比如ecache做一級(jí)緩存,redis做二級(jí)緩存拓瞪。