一、Redis介紹
什么是Redis?
redis是一個(gè)key-value存儲(chǔ)系統(tǒng)。和Memcached類(lèi)似,它支持存儲(chǔ)的value類(lèi)型相對(duì)更多,包括string(字符串)、list(鏈表)、set(集合)、zset(sorted set --有序集合)和hash(哈希類(lèi)型)。這些數(shù)據(jù)類(lèi)型都支持push/pop、add/remove及取交集并集和差集及更豐富的操作,而且這些操作都是原子性的。在此基礎(chǔ)上,redis支持各種不同方式的排序。與memcached一樣,為了保證效率,數(shù)據(jù)都是緩存在內(nèi)存中。區(qū)別的是redis會(huì)周期性的把更新的數(shù)據(jù)寫(xiě)入磁盤(pán)或者把修改操作寫(xiě)入追加的記錄文件,并且在此基礎(chǔ)上實(shí)現(xiàn)了master-slave(主從)同步。
它有什么特點(diǎn)?
(1)Redis數(shù)據(jù)庫(kù)完全在內(nèi)存中,使用磁盤(pán)僅用于持久性。
(2)相比許多鍵值數(shù)據(jù)存儲(chǔ),Redis擁有一套較為豐富的數(shù)據(jù)類(lèi)型。
(3)Redis可以將數(shù)據(jù)復(fù)制到任意數(shù)量的從服務(wù)器。
Redis 優(yōu)勢(shì)?
(1)異??焖伲篟edis的速度非??欤棵肽軋?zhí)行約11萬(wàn)集合,每秒約81000+條記錄。
(2)支持豐富的數(shù)據(jù)類(lèi)型:Redis支持最大多數(shù)開(kāi)發(fā)人員已經(jīng)知道像列表,集合,有序集合,散列數(shù)據(jù)類(lèi)型。這使得它非常容易解決各種各樣的問(wèn)題,因?yàn)槲覀冎滥男﹩?wèn)題是可以處理通過(guò)它的數(shù)據(jù)類(lèi)型更好。
(3)操作都是原子性:所有Redis操作是原子的,這保證了如果兩個(gè)客戶端同時(shí)訪問(wèn)的Redis服務(wù)器將獲得更新后的值。
(4)多功能實(shí)用工具:Redis是一個(gè)多實(shí)用的工具,可以在多個(gè)用例如緩存,消息,隊(duì)列使用(Redis原生支持發(fā)布/訂閱),任何短暫的數(shù)據(jù),應(yīng)用程序,如Web應(yīng)用程序會(huì)話,網(wǎng)頁(yè)命中計(jì)數(shù)等。
Redis 缺點(diǎn)?
(1)單線程
(2)耗內(nèi)存
二、使用實(shí)例
本文使用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服務(wù)器中心 -->
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
# 不需要加入緩存的類(lèi)
targetNames=xxxRecordManager,xxxSetRecordManager,xxxStatisticsIdentificationManager
# 不需要緩存的方法
methodNames=
#設(shè)置緩存失效時(shí)間
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>
!--要是有多個(gè)配置文件,只需在這里繼續(xù)添加即可 -->
/list>
/property>
/bean>
3、一些工具類(lèi)
(1)RedisUtil
上面的bean中,RedisUtil是用來(lái)緩存和去除數(shù)據(jù)的實(shí)例
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 工具類(lèi)
*
*/
public final class RedisUtil {
private Logger logger = Logger.getLogger(RedisUtil.class);
private RedisTemplateSerializable, Object> redisTemplate;
/**
* 批量刪除對(duì)應(yīng)的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);
}
/**
* 刪除對(duì)應(yīng)的value
*
* @param key
*/
public void remove(final String key) {
if (exists(key)) {
redisTemplate.delete(key);
}
}
/**
* 判斷緩存中是否有對(duì)應(yīng)的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;
}
/**
* 寫(xiě)入緩存
*
* @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;
}
/**
* 寫(xiě)入緩存
*
* @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,這是用來(lái)給不同的方法來(lái)加入判斷如果緩存存在數(shù)據(jù),從緩存取數(shù)據(jù)。否則第一次從數(shù)據(jù)庫(kù)取,并將結(jié)果保存到緩存 中去。
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; // 緩存默認(rèn)的過(guò)期時(shí)間
private Long xxxRecordManagerTime; //
private Long xxxSetRecordManagerTime; //
/**
* 初始化讀取不需要加入緩存的類(lèi)名和方法名稱
*/
public MethodCacheInterceptor() {
try {
File f = new File("D:\\lunaJee-workspace\\msm\\msm_core\\src\\main\\java\\com\\mucfc\\msm\\common\\cacheConf.properties");
//配置文件位置直接被寫(xiě)死,有需要自己修改下
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(",");
// 加載過(guò)期時(shí)間設(shè)置
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;
// 將不需要緩存的類(lèi)名和方法名添加到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();
// 不需要緩存的內(nèi)容
//if (!isAddCache(StringUtil.subStrForLastDot(targetName), methodName)) {
if (!isAddCache(targetName, methodName)) {
// 執(zhí)行方法返回結(jié)果
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);
}
// 寫(xiě)入緩存
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、配置需要緩存的類(lèi)或方法
在application.xml加入如下配置,有多個(gè)類(lèi)或方法可以配置多個(gè)
!-- 需要加入緩存的類(lèi)或方法 -->
bean id="methodCachePointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor" >
property name="advice" >
ref local="methodCacheInterceptor" />
/property>
property name="patterns" >
list>
!-- 確定正則表達(dá)式列表 -->
value>com\.mucfc\.msm\.service\.impl\...*ServiceImpl.*/value >
/list>
/property>
/bean >
5、執(zhí)行結(jié)果:
寫(xiě)了一個(gè)簡(jiǎn)單的單元測(cè)試如下:
@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("查找結(jié)果" + configSettUnit.getBusinessType());
// localSecondFIFOCache.put("configSettUnit", configSettUnit.getBusinessType());
// String string = localSecondFIFOCache.get("configSettUnit");
logger.info("查找結(jié)果" + string);
}
這是第一次執(zhí)行單元測(cè)試的過(guò)程:
MethodCacheInterceptor這個(gè)類(lèi)中打了斷點(diǎn),然后每次查詢前都會(huì)先進(jìn)入這個(gè)方法

依次運(yùn)行,發(fā)現(xiàn)沒(méi)有緩存,所以會(huì)直接去查數(shù)據(jù)庫(kù)

打印了出來(lái)的SQL語(yǔ)句:

第二次執(zhí)行:
因?yàn)榈谝淮螆?zhí)行時(shí),已經(jīng)寫(xiě)入緩存了。所以第二次直接從緩存中取數(shù)據(jù)

3、取兩次的結(jié)果進(jìn)行地址的對(duì)比:
發(fā)現(xiàn)兩個(gè)不是同一個(gè)對(duì)象,沒(méi)錯(cuò),是對(duì)的。如果是使用ehcache的話,那么二者的內(nèi)存地址會(huì)是一樣的。那是因?yàn)閞edis和ehcache使用的緩存機(jī)制是不一樣的。ehcache是基于本地電腦的內(nèi)存使用緩存,所以使用緩存取數(shù)據(jù)時(shí)直接在本地電腦上取。轉(zhuǎn)換成java對(duì)象就會(huì)是同一個(gè)內(nèi)存地址,而redis它是在裝有redis服務(wù)的電腦上(一般是另一臺(tái)電腦),所以取數(shù)據(jù)時(shí)經(jīng)過(guò)傳輸?shù)奖镜?,?huì)對(duì)應(yīng)到不同的內(nèi)存地址,所以用==來(lái)比較會(huì)返回false。但是它確實(shí)是從緩存中去取的,這點(diǎn)我們從上面的斷點(diǎn)可以看到。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助。
您可能感興趣的文章:- Spring實(shí)戰(zhàn)之方法級(jí)別緩存用法示例
- Spring實(shí)戰(zhàn)之緩存使用condition操作示例
- Spring實(shí)戰(zhàn)之緩存使用key操作示例
- Spring實(shí)戰(zhàn)之類(lèi)級(jí)別緩存實(shí)現(xiàn)與使用方法
- Spring Boot集成Ehcache緩存解決方式
- spring結(jié)合redis如何實(shí)現(xiàn)數(shù)據(jù)的緩存
- 詳解Spring緩存注解@Cacheable,@CachePut , @CacheEvict使用
- Spring Boot 基于注解的 Redis 緩存使用詳解
- SpringBoot項(xiàng)目中使用redis緩存的方法步驟
- Spring Boot 中使用cache緩存的方法
- Spring實(shí)戰(zhàn)之清除緩存操作示例