1.安装redis
1.下载源码
wget http://download.redis.io/releases/redis-2.8.3.tar.gz
2.编译
解压:tar xzf redis-2.8.3.tar.gz
cd redis-2.8.3
编译:make
3.将编译后的文件考出
cp redis-server /usr/redis
cp redis-benchmark /usr/redis
cp redis-cli /usr/redis
cp redis.conf /usr/redis
4.启动redis
./redis-server redis.conf
2.redis简单使用
1.创建springboot项目
2.引入pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
3.配置yml
server:
port: 9527
spring:
application:
name: redis-demo
# redis配置开始
redis:
# Redis数据库索引(默认为0)
database: 0
# Redis服务器地址
host: localhost
# Redis服务器连接密码(默认为空)
password: 123456
# 配置端口
port: 6379
jedis:
pool:
# 连接池最大连接数(使用负值表示没有限制)
max-active: 1024
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: 10000
# 连接池中的最大空闲连接
max-idle: 200
# 连接池中的最小空闲连接
min-idle: 0
# 连接超时时间(毫秒)
timeout: 10000
block-when-exhausted: true
#redis配置结束
4.配置JedisPool连接池
@Configuration
public class RedisConfig {
private final static Logger logger = LoggerFactory.getLogger(RedisConfig.class);
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.timeout}")
private int timeout;
@Value("${spring.redis.jedis.pool.max-idle}")
private int maxIdle;
@Value("${spring.redis.jedis.pool.max-wait}")
private long maxWaitMillis;
@Value("${spring.redis.password}")
private String password;
@Value("${spring.redis.block-when-exhausted}")
private boolean blockWhenExhausted;
@Bean
public JedisPool redisPoolFactory() throws Exception{
logger.info("JedisPool注入成功!!");
logger.info("redis地址:" + host + ":" + port);
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
// 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true
jedisPoolConfig.setBlockWhenExhausted(blockWhenExhausted);
// 是否启用pool的jmx管理功能, 默认true
jedisPoolConfig.setJmxEnabled(true);
//JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout,password);
JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout);
return jedisPool;
}
}
5.方法类
@Component
public class RedisUtil<T> {
@Autowired
private JedisPool jedisPool;
/**
* 关闭jedis
* @param jedis
*/
private void returnJedis(final Jedis jedis){
if (jedis != null){
jedis.close();
}
}
}
1.使用redis实现布隆过滤器
/** 预计插入量*/
private long expectedInsertions = 10000000;
/** 可接受的错误率*/
private double fpp = 0.001F;
/** bit数组长度*/
private long numBits = optimalNumOfBits(expectedInsertions, fpp);
/**hash函数数量*/
private int numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, numBits);
/**
* 计算bit数组长度
* @param n
* @param p
* @return
*/
private long optimalNumOfBits(long n, double p) {
if (p == 0) {
p = Double.MIN_VALUE;
}
return (long) (-n * Math.log(p) / (Math.log(2) * Math.log(2)));
}
/**
* 计算hash函数个数
* @param n
* @param m
* @return
*/
private int optimalNumOfHashFunctions(long n, long m) {
return Math.max(1, (int) Math.round((double) m / n * Math.log(2)));
}
/**
* 根据key获取bitmap下标
*/
private long[] getIndexs(String key) {
long hash1 = hash(key);
long hash2 = hash1 >>> 16;
long[] result = new long[numHashFunctions];
for (int i = 0; i < numHashFunctions; i++) {
long combinedHash = hash1 + i * hash2;
if (combinedHash < 0) {
combinedHash = ~combinedHash;
}
result[i] = combinedHash % numBits;
}
return result;
}
/**
* 获取一个hash值
*/
private long hash(String key) {
Charset charset = Charset.forName("UTF-8");
return Hashing.murmur3_128().hashObject(key, Funnels.stringFunnel(charset)).asLong();
}
/**
* 判断keys是否存在于布隆过滤器中
*/
public boolean isExistByBloom(String key, String value) {
long[] indexs = getIndexs(value);
boolean result = false;
Jedis jedis = null;
Pipeline pipeline = null;
try {
jedis = jedisPool.getResource();
pipeline = jedis.pipelined();
for (long index : indexs) {
pipeline.getbit(key, index);
}
result = !pipeline.syncAndReturnAll().contains(false);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
pipeline.close();
} catch (IOException e) {
e.printStackTrace();
}
returnJedis(jedis);
}
return result;
}
/**
* 将key存入布隆过滤器
*/
public void putByBloom(String key, String value) {
long[] indexs = getIndexs(value);
Jedis jedis = null;
Pipeline pipeline = null;
try {
jedis = jedisPool.getResource();
pipeline = jedis.pipelined();
for (long index : indexs) {
pipeline.setbit(key, index, true);
}
pipeline.sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
pipeline.close();
} catch (IOException e) {
e.printStackTrace();
}
returnJedis(jedis);
}
}
isExistByBloom():用于判断一个value是否存在于布隆过滤器中
putByBloom():将一个value添加到布隆过滤器中
- 优点
它的优点是空间效率和查询时间都远远超过一般的算法,布隆过滤器存储空间和插入 / 查询时间都是常数O(k)。另外, 散列函数相互之间没有关系,方便由硬件并行实现。布隆过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势。
2. 缺点
但是布隆过滤器的缺点和优点一样明显。误算率是其中之一。随着存入的元素数量增加,误算率随之增加。但是如果元素数量太少,则使用散列表足矣。
(误判补救方法是:再建立一个小的白名单,存储那些可能被误判的信息。)
另外,一般情况下不能从布隆过滤器中删除元素. 我们很容易想到把位数组变成整数数组,每插入一个元素相应的计数器加 1, 这样删除元素时将计数器减掉就可以了。然而要保证安全地删除元素并非如此简单。首先我们必须保证删除的元素的确在布隆过滤器里面. 这一点单凭这个过滤器是无法保证的。另外计数器回绕也会造成问题。(google guava实现的布隆过滤器里面就没有包含删除元素)
3 应用场景:
解决缓存击穿,网页黑名单,垃圾邮件过滤,电话黑名单,url去重,内容推荐等
2.实现分布式锁
/** 加锁*/
public String lock(String key,String value,int seconds){
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
return jedis.set(key,value,"NX","PX",seconds * 1000);
} catch (Exception e) {
e.printStackTrace();
}finally {
returnJedis(jedis);
}
return null;
}
高版本的redis可以使用set()的多参数来实现redis的分布式锁
第一个为key,我们使用key来当锁,因为key是唯一的。
第二个为value 可给value赋值为userId,我们就知道这把锁是哪个请求加的了,在解锁的时候就可以有依据
第三个为nxxx,这个参数我们填的是NX,意思是SET IF NOT EXIST,即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作;
第四个为expx,这个参数我们传的是PX,意思是我们要给这个key加一个过期的设置,具体时间由第五个参数决定。
第五个为time,与第四个参数相呼应,代表key的过期时间。
3.防止缓存雪崩或缓存穿透
- 创建回调函数
public interface RedisCacheLoadBack<T> {
/**
* reids回调函数
* @param <T>
* @return
*/
public <T> T load();
}
- 实现回调接口
@Service
public class RedisCacheTemplateService {
@Autowired
private RedisUtil redisUtil;
public <T> T findCachePreveBreak(String key, int seconds, TypeReference<T> clazz, RedisCacheLoadBack<T> loadBack){
String json = redisUtil.get(key);
if(!StringUtils.isEmpty(json)){
return JSON.parseObject(json, clazz);
}else{
synchronized (this) {
json = redisUtil.get(key);
if(!StringUtils.isEmpty(json)){
return JSON.parseObject(json, clazz);
}
T result = loadBack.load();
if(result!=null&&!"[]".equals(result)&&!"null".equals(result)){
String value = JSON.toJSONString(result, SerializerFeature.DisableCircularReferenceDetect);
if(seconds>0){
redisUtil.setex(key, value, seconds);
}else{
redisUtil.set(key,value);
}
}
return result;
}
}
}
public <T> T findMapCache(String key, String field,TypeReference<T> clazz, RedisCacheLoadBack<T> loadBack){
String json = redisUtil.hget(key,field);
if(!StringUtils.isEmpty(json)){
return JSON.parseObject(json, clazz);
}else{
synchronized (this) {
json = redisUtil.hget(key,field);
if(!StringUtils.isEmpty(json)){
return JSON.parseObject(json, clazz);
}
T result = loadBack.load();
if(result!=null&&!"[]".equals(result)&&!"null".equals(result)){
String value = JSON.toJSONString(result, SerializerFeature.DisableCircularReferenceDetect);
if ("{}".equals(value) || StringUtils.isEmpty(value)){
return result;
}
redisUtil.hset(key,field,value);
}
return result;
}
}
}
- 使用
public Map<String,String> test(){
return redisCacheTemplateService.findCachePreveBreak(RedisKeyConfig.MUTUAL_CHOOSE_CASE_STATISTICS,RedisKeyConfig.THIRTY_MINUTES,new TypeReference<Map<String,String>>(){},new RedisCacheLoadBack<Map<String,String>>(){
@Override
public Map<String,String> load() {
//查询数据库逻辑
Map<String,String> result = new HashMap<String,String>(4);//查询到的数据
String nowTime = ValidateUtil.getTodayStr(ValidateUtil.YYYY_MM_DD_HH_MM);
result.put("updateTime",nowTime);
return result;
}
});
}
4.简单的防频繁刷新
/**
* 防止频繁刷新
* @param key
* @param value
* @param seconds
*/
public void preventOftenRefresh(String key,String value,int seconds) throws ResultException {
//使用分布式锁
String result = lock(key,value,seconds);
if (StringUtils.isEmpty(result)){
throw new ResultException(ReturnInfoEnum.parameterError.getCode(),"正在处理,请稍等");
}
}