Redis入门体验

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项目

image

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添加到布隆过滤器中

  1. 优点

它的优点是空间效率和查询时间都远远超过一般的算法,布隆过滤器存储空间和插入 / 查询时间都是常数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.防止缓存雪崩或缓存穿透
  1. 创建回调函数
public interface RedisCacheLoadBack<T> {

    /**
     * reids回调函数
     * @param <T>
     * @return
     */
    public <T> T load();

}
  1. 实现回调接口
@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;
        }

    }
}

  1. 使用
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(),"正在处理,请稍等");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值