signed

QiShunwang

“诚信为本、客户至上”

redis的使用场景

2021/4/26 15:29:13   来源:

(1)作为缓存。
1.搭建一个springboot+mp的工程

2.引入redis相关的依赖

3.配置redis

4.service代码

package com.wyj.service;

import com.ykq.dao.DeptDao;
import com.ykq.entity.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class DeptService {

    @Resource
    private DeptDao deptDao;

    @Autowired
    private RedisTemplate redisTemplate;

    public Dept findById(Integer deptId){
        //1.从缓存中查询该数据
        Object o = redisTemplate.opsForValue().get("findById::" + deptId);
        if(o!=null){//表示从缓存中获取该数据
            return (Dept) o;
        }
        Dept dept = deptDao.selectById(deptId);
        redisTemplate.opsForValue().set("findById::"+deptId,dept);//把查询的结果放入缓存
        return dept;
    }


    //数据库和缓存同步问题!
    public int delete(Integer deptId){
        redisTemplate.delete("findById::"+deptId);//删除缓存
        int i = deptDao.deleteById(deptId);
        return i;
    }

    public int update(Dept dept){
        redisTemplate.delete("findById::"+dept.getDeptId());//删除缓存
        int i = deptDao.updateById(dept);
        redisTemplate.opsForValue().set("findById::"+dept.getDeptId(),dept);
        return i;
    }
}

基于spring的缓存注解。

package com.wyj;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootApplication
@MapperScan(basePackages = "com.ykq.dao")
@EnableCaching //开启缓存的注解
public class SpringbootRedis02Application {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootRedis02Application.class, args);
    }

}



package com.wyj.service;

import com.wyj.dao.DeptDao;
import com.wyj.entity.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class DeptService {

    @Resource
    private DeptDao deptDao;

    //该注解作用:会先查询缓存,如果缓存存在,则不会执行代码块。 如果缓存中不存在则执行该方法,并把该方法的返回值存放到redis中
    @Cacheable(cacheNames = "findById",key = "#deptId")  //缓存的key值 为findById
    public Dept findById(Integer deptId){
        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
        Dept dept = deptDao.selectById(deptId);
        return dept;
    }


    //数据库和缓存同步问题!
    // beforeInvocation:是否在方法执行前就清空,缺省为 false,
    // 如果指定为 true,则在方法还没有执行的时候就清空缓存。缺省情况下,如果方法执行抛出异常,则不会清空缓存。
    @CacheEvict(cacheNames = "findById",key = "#deptId")
    public int delete(Integer deptId){
        int i = deptDao.deleteById(deptId);
        return i;
    }

    //这个注解是必须执行方法体,而且会把方法体执行的结果放入到缓存中。 如果发生异常则不操作缓存。
    @CachePut(cacheNames = "findById",key = "#dept.deptId")
    public Dept update(Dept dept){
        int i = deptDao.updateById(dept);
        return dept;
    }
}

(2)作为分布式锁

package com.wyj.distributedlock.service;

import com.wyj.distributedlock.dao.StockDao;
import com.wyj.distributedlock.entity.Stock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Service
public class StockService {

    @Resource
    private StockDao stockDao;

    @Autowired
    private StringRedisTemplate redisTemplate;

    public String decrStock(Integer productId) {//synchronized () 同步方法    同步代码块
        Boolean flag = redisTemplate.opsForValue().setIfAbsent("product::" + productId, "ykq",30, TimeUnit.SECONDS);
        //查询对应的id的库存
         if(flag) {//获取锁了
             try {
                 Stock stock = stockDao.selectById(productId);
                 if (stock.getNum() > 0) {
                     //根据id修改库存
                     stock.setNum(stock.getNum() - 1);
                     stockDao.updateById(stock); //异常发生
//                   int c=10/0;
                     System.out.println("库存剩余:" + (stock.getNum()));
                     return "库存减少成功";
                 } else {
                     return "库存不足";
                 }
             }catch (Exception e){
                  throw  new RuntimeException(e.getMessage());
             }
             finally {
                 redisTemplate.delete("product::" + productId);//释放锁资源 一定再finally
             }
         }else{
             System.out.println("服务器正忙请稍后再试..........");
             return "服务器正忙请稍后再试..........";
         }
    }
}

使用第三方组件redisson-----专门用于解决分布式问题。

       <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.13.4</version>
        </dependency>
 @Bean
    public RedissonClient getRedisson(){
        Config config=new Config();
        config.useSingleServer().setAddress("redis://192.168.213.188:6379");
        RedissonClient redissonClient = Redisson.create(config);
        return redissonClient;
    }
package com.wyj.distributedlock.service;

import com.wyj.distributedlock.dao.StockDao;
import com.wyj.distributedlock.entity.Stock;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;


@Service
public class StockService {

    @Resource
    private StockDao stockDao;

    @Autowired
    private RedissonClient redisson; 

    public String decrStock(Integer productId) {//synchronized () 同步方法    同步代码块
        RLock lock = redisson.getLock("product::" + productId);//获取锁对象
        try {
            lock.tryLock(60,20,TimeUnit.SECONDS); //自己别设置时间。
            Stock stock = stockDao.selectById(productId);
            if (stock.getNum() > 0) {
                //根据id修改库存
                stock.setNum(stock.getNum() - 1);
                stockDao.updateById(stock); //异常发生
//                   int c=10/0;
//                Thread.sleep(35000);
                System.out.println("库存剩余:" + (stock.getNum()));
                return "库存减少成功";
            } else {
                return "库存不足";
            }
        }catch (Exception e){
             throw  new RuntimeException(e.getMessage());
        }
        finally {
            lock.unlock();
        }
    }
}

(3)作为点赞量videaId,0 incr(videaId),排行榜,转发量。

什么是计数器,如电商网站商品的浏览量、视频网站视频的播放数等。为了保证数据实时效,每次浏览都得给+1,并发量高时如果每次都请求数据库操作无疑是种挑战和压力。Redis提供的incr命令来实现计数器功能,内存操作,性能非常好,非常适用于这些计数场景 。 


关系型数据库在排行榜方面查询速度普遍偏慢,所以可以借助redis的SortedSet进行热点数据的排序。 在奶茶活动中,我们需要展示各个部门的点赞排行榜, 所以我针对每个部门做了一个SortedSet,然后以用户的openid作为上面的username,以用户的点赞数作为上面的score, 然后针对每个用户做一个hash, 通过zrangebyscore就可以按照点赞数获取排行榜,然后再根据username获取用户的hash信息,这个当时在实际运用中性能体验也蛮不错的。 

(4)限时业务的运用

redis中可以使用expire命令设置一个键的生存时间,到时间后redis会删除它。利用这一特性可以运用在限时的优惠活动信息、手机验证码等业务场景。