一、Redis介紹
什么是Redis?
redis是一個key-value存儲系統(tǒng)。和Memcached類似,它支持存儲的value類型相對更多,包括string(字符串)、list(鏈表)、set(集合)、zset(sorted set --有序集合)和hash(哈希類型)。這些數(shù)據(jù)類型都支持push/pop、add/remove及取交集并集和差集及更豐富的操作,而且這些操作都是原子性的。在此基礎上,redis支持各種不同方式的排序。與memcached一樣,為了保證效率,數(shù)據(jù)都是緩存在內存中。區(qū)別的是redis會周期性的把更新的數(shù)據(jù)寫入磁盤或者把修改操作寫入追加的記錄文件,并且在此基礎上實現(xiàn)了master-slave(主從)同步。
它有什么特點?
(1)Redis數(shù)據(jù)庫完全在內存中,使用磁盤僅用于持久性。
(2)相比許多鍵值數(shù)據(jù)存儲,Redis擁有一套較為豐富的數(shù)據(jù)類型。
(3)Redis可以將數(shù)據(jù)復制到任意數(shù)量的從服務器。
Redis 優(yōu)勢?
(1)異??焖伲篟edis的速度非???,每秒能執(zhí)行約11萬集合,每秒約81000+條記錄。
(2)支持豐富的數(shù)據(jù)類型:Redis支持最大多數(shù)開發(fā)人員已經(jīng)知道像列表,集合,有序集合,散列數(shù)據(jù)類型。這使得它非常容易解決各種各樣的問題,因為我們知道哪些問題是可以處理通過它的數(shù)據(jù)類型更好。
(3)操作都是原子性:所有Redis操作是原子的,這保證了如果兩個客戶端同時訪問的Redis服務器將獲得更新后的值。
(4)多功能實用工具:Redis是一個多實用的工具,可以在多個用例如緩存,消息,隊列使用(Redis原生支持發(fā)布/訂閱),任何短暫的數(shù)據(jù),應用程序,如Web應用程序會話,網(wǎng)頁命中計數(shù)等。
Redis 缺點?
(1)單線程
(2)耗內存
二、使用實例
本文使用maven+eclipse+sping
1、引入jar包
!--Redis start -->
dependency>
groupId>org.springframework.data/groupId>
artifactId>spring-data-redis/artifactId>
version>1.6.1.RELEASE/version>
/dependency>
dependency>
groupId>redis.clients/groupId>
artifactId>jedis/artifactId>
version>2.7.3/version>
/dependency>
!--Redis end -->
2、配置bean
在application.xml加入如下配置
!-- jedis 配置 -->
bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig" >
property name="maxIdle" value="${redis.maxIdle}" />
property name="maxWaitMillis" value="${redis.maxWait}" />
property name="testOnBorrow" value="${redis.testOnBorrow}" />
/bean >
!-- redis服務器中心 -->
bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" >
property name="poolConfig" ref="poolConfig" />
property name="port" value="${redis.port}" />
property name="hostName" value="${redis.host}" />
property name="password" value="${redis.password}" />
property name="timeout" value="${redis.timeout}" >/property>
/bean >
bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" >
property name="connectionFactory" ref="connectionFactory" />
property name="keySerializer" >
bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
/property>
property name="valueSerializer" >
bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
/property>
/bean >
!-- cache配置 -->
bean id="methodCacheInterceptor" class="com.mucfc.msm.common.MethodCacheInterceptor" >
property name="redisUtil" ref="redisUtil" />
/bean >
bean id="redisUtil" class="com.mucfc.msm.common.RedisUtil" >
property name="redisTemplate" ref="redisTemplate" />
/bean >
其中配置文件redis一些配置數(shù)據(jù)redis.properties如下:
#redis中心
redis.host=10.75.202.11
redis.port=6379
redis.password=123456
redis.maxIdle=100
redis.maxActive=300
redis.maxWait=1000
redis.testOnBorrow=true
redis.timeout=100000
# 不需要加入緩存的類
targetNames=xxxRecordManager,xxxSetRecordManager,xxxStatisticsIdentificationManager
# 不需要緩存的方法
methodNames=
#設置緩存失效時間
com.service.impl.xxxRecordManager= 60
com.service.impl.xxxSetRecordManager= 60
defaultCacheExpireTime=3600
fep.local.cache.capacity =10000
要掃這些properties文件,在application.xml加入如下配置
!-- 引入properties配置文件 -->
bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
property name="locations">
list>
value>classpath:properties/*.properties/value>
!--要是有多個配置文件,只需在這里繼續(xù)添加即可 -->
/list>
/property>
/bean>
3、一些工具類
(1)RedisUtil
上面的bean中,RedisUtil是用來緩存和去除數(shù)據(jù)的實例
package com.mucfc.msm.common;
import java.io.Serializable;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.apache.log4j.Logger;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
/**
* redis cache 工具類
*
*/
public final class RedisUtil {
private Logger logger = Logger.getLogger(RedisUtil.class);
private RedisTemplateSerializable, Object> redisTemplate;
/**
* 批量刪除對應的value
*
* @param keys
*/
public void remove(final String... keys) {
for (String key : keys) {
remove(key);
}
}
/**
* 批量刪除key
*
* @param pattern
*/
public void removePattern(final String pattern) {
SetSerializable> keys = redisTemplate.keys(pattern);
if (keys.size() > 0)
redisTemplate.delete(keys);
}
/**
* 刪除對應的value
*
* @param key
*/
public void remove(final String key) {
if (exists(key)) {
redisTemplate.delete(key);
}
}
/**
* 判斷緩存中是否有對應的value
*
* @param key
* @return
*/
public boolean exists(final String key) {
return redisTemplate.hasKey(key);
}
/**
* 讀取緩存
*
* @param key
* @return
*/
public Object get(final String key) {
Object result = null;
ValueOperationsSerializable, Object> operations = redisTemplate
.opsForValue();
result = operations.get(key);
return result;
}
/**
* 寫入緩存
*
* @param key
* @param value
* @return
*/
public boolean set(final String key, Object value) {
boolean result = false;
try {
ValueOperationsSerializable, Object> operations = redisTemplate
.opsForValue();
operations.set(key, value);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 寫入緩存
*
* @param key
* @param value
* @return
*/
public boolean set(final String key, Object value, Long expireTime) {
boolean result = false;
try {
ValueOperationsSerializable, Object> operations = redisTemplate
.opsForValue();
operations.set(key, value);
redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
public void setRedisTemplate(
RedisTemplateSerializable, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
}
(2)MethodCacheInterceptor
切面MethodCacheInterceptor,這是用來給不同的方法來加入判斷如果緩存存在數(shù)據(jù),從緩存取數(shù)據(jù)。否則第一次從數(shù)據(jù)庫取,并將結果保存到緩存 中去。
package com.mucfc.msm.common;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.log4j.Logger;
public class MethodCacheInterceptor implements MethodInterceptor {
private Logger logger = Logger.getLogger(MethodCacheInterceptor.class);
private RedisUtil redisUtil;
private ListString> targetNamesList; // 不加入緩存的service名稱
private ListString> methodNamesList; // 不加入緩存的方法名稱
private Long defaultCacheExpireTime; // 緩存默認的過期時間
private Long xxxRecordManagerTime; //
private Long xxxSetRecordManagerTime; //
/**
* 初始化讀取不需要加入緩存的類名和方法名稱
*/
public MethodCacheInterceptor() {
try {
File f = new File("D:\\lunaJee-workspace\\msm\\msm_core\\src\\main\\java\\com\\mucfc\\msm\\common\\cacheConf.properties");
//配置文件位置直接被寫死,有需要自己修改下
InputStream in = new FileInputStream(f);
// InputStream in = getClass().getClassLoader().getResourceAsStream(
// "D:\\lunaJee-workspace\\msm\\msm_core\\src\\main\\java\\com\\mucfc\\msm\\common\\cacheConf.properties");
Properties p = new Properties();
p.load(in);
// 分割字符串
String[] targetNames = p.getProperty("targetNames").split(",");
String[] methodNames = p.getProperty("methodNames").split(",");
// 加載過期時間設置
defaultCacheExpireTime = Long.valueOf(p.getProperty("defaultCacheExpireTime"));
xxxRecordManagerTime = Long.valueOf(p.getProperty("com.service.impl.xxxRecordManager"));
xxxSetRecordManagerTime = Long.valueOf(p.getProperty("com.service.impl.xxxSetRecordManager"));
// 創(chuàng)建list
targetNamesList = new ArrayListString>(targetNames.length);
methodNamesList = new ArrayListString>(methodNames.length);
Integer maxLen = targetNames.length > methodNames.length ? targetNames.length
: methodNames.length;
// 將不需要緩存的類名和方法名添加到list中
for (int i = 0; i maxLen; i++) {
if (i targetNames.length) {
targetNamesList.add(targetNames[i]);
}
if (i methodNames.length) {
methodNamesList.add(methodNames[i]);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object value = null;
String targetName = invocation.getThis().getClass().getName();
String methodName = invocation.getMethod().getName();
// 不需要緩存的內容
//if (!isAddCache(StringUtil.subStrForLastDot(targetName), methodName)) {
if (!isAddCache(targetName, methodName)) {
// 執(zhí)行方法返回結果
return invocation.proceed();
}
Object[] arguments = invocation.getArguments();
String key = getCacheKey(targetName, methodName, arguments);
System.out.println(key);
try {
// 判斷是否有緩存
if (redisUtil.exists(key)) {
return redisUtil.get(key);
}
// 寫入緩存
value = invocation.proceed();
if (value != null) {
final String tkey = key;
final Object tvalue = value;
new Thread(new Runnable() {
@Override
public void run() {
if (tkey.startsWith("com.service.impl.xxxRecordManager")) {
redisUtil.set(tkey, tvalue, xxxRecordManagerTime);
} else if (tkey.startsWith("com.service.impl.xxxSetRecordManager")) {
redisUtil.set(tkey, tvalue, xxxSetRecordManagerTime);
} else {
redisUtil.set(tkey, tvalue, defaultCacheExpireTime);
}
}
}).start();
}
} catch (Exception e) {
e.printStackTrace();
if (value == null) {
return invocation.proceed();
}
}
return value;
}
/**
* 是否加入緩存
*
* @return
*/
private boolean isAddCache(String targetName, String methodName) {
boolean flag = true;
if (targetNamesList.contains(targetName)
|| methodNamesList.contains(methodName)) {
flag = false;
}
return flag;
}
/**
* 創(chuàng)建緩存key
*
* @param targetName
* @param methodName
* @param arguments
*/
private String getCacheKey(String targetName, String methodName,
Object[] arguments) {
StringBuffer sbu = new StringBuffer();
sbu.append(targetName).append("_").append(methodName);
if ((arguments != null) (arguments.length != 0)) {
for (int i = 0; i arguments.length; i++) {
sbu.append("_").append(arguments[i]);
}
}
return sbu.toString();
}
public void setRedisUtil(RedisUtil redisUtil) {
this.redisUtil = redisUtil;
}
}
4、配置需要緩存的類或方法
在application.xml加入如下配置,有多個類或方法可以配置多個
!-- 需要加入緩存的類或方法 -->
bean id="methodCachePointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor" >
property name="advice" >
ref local="methodCacheInterceptor" />
/property>
property name="patterns" >
list>
!-- 確定正則表達式列表 -->
value>com\.mucfc\.msm\.service\.impl\...*ServiceImpl.*/value >
/list>
/property>
/bean >
5、執(zhí)行結果:
寫了一個簡單的單元測試如下:
@Test
public void getSettUnitBySettUnitIdTest() {
String systemId = "CES";
String merchantId = "133";
SettUnit configSettUnit = settUnitService.getSettUnitBySettUnitId(systemId, merchantId, "ESP");
SettUnit configSettUnit1 = settUnitService.getSettUnitBySettUnitId(systemId, merchantId, "ESP");
boolean flag= (configSettUnit == configSettUnit1);
System.out.println(configSettUnit);
logger.info("查找結果" + configSettUnit.getBusinessType());
// localSecondFIFOCache.put("configSettUnit", configSettUnit.getBusinessType());
// String string = localSecondFIFOCache.get("configSettUnit");
logger.info("查找結果" + string);
}
這是第一次執(zhí)行單元測試的過程:
MethodCacheInterceptor這個類中打了斷點,然后每次查詢前都會先進入這個方法
依次運行,發(fā)現(xiàn)沒有緩存,所以會直接去查數(shù)據(jù)庫
打印了出來的SQL語句:
第二次執(zhí)行:
因為第一次執(zhí)行時,已經(jīng)寫入緩存了。所以第二次直接從緩存中取數(shù)據(jù)
3、取兩次的結果進行地址的對比:
發(fā)現(xiàn)兩個不是同一個對象,沒錯,是對的。如果是使用ehcache的話,那么二者的內存地址會是一樣的。那是因為redis和ehcache使用的緩存機制是不一樣的。ehcache是基于本地電腦的內存使用緩存,所以使用緩存取數(shù)據(jù)時直接在本地電腦上取。轉換成java對象就會是同一個內存地址,而redis它是在裝有redis服務的電腦上(一般是另一臺電腦),所以取數(shù)據(jù)時經(jīng)過傳輸?shù)奖镜?,會對應到不同的內存地址,所以?=來比較會返回false。但是它確實是從緩存中去取的,這點我們從上面的斷點可以看到。
以上就是本文的全部內容,希望對大家的學習有所幫助。
您可能感興趣的文章:- Spring實戰(zhàn)之方法級別緩存用法示例
- Spring實戰(zhàn)之緩存使用condition操作示例
- Spring實戰(zhàn)之緩存使用key操作示例
- Spring實戰(zhàn)之類級別緩存實現(xiàn)與使用方法
- Spring Boot集成Ehcache緩存解決方式
- spring結合redis如何實現(xiàn)數(shù)據(jù)的緩存
- 詳解Spring緩存注解@Cacheable,@CachePut , @CacheEvict使用
- Spring Boot 基于注解的 Redis 緩存使用詳解
- SpringBoot項目中使用redis緩存的方法步驟
- Spring Boot 中使用cache緩存的方法
- Spring實戰(zhàn)之清除緩存操作示例