Redis数据结构存储系统:第二章:如何使用

2022-09-28 14:48:19 浏览数 (1)

Redis与SpringBoot整合:

第一步:在项目中引入

代码语言:javascript复制
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>

第二步:将连接池和配置类创建好

RedisUtil:

代码语言:javascript复制
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class RedisUtil {

    private JedisPool jedisPool;

    public void initPool(String host,int port ,int database){
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxTotal(200);
        poolConfig.setMaxIdle(30);
        poolConfig.setBlockWhenExhausted(true);
        poolConfig.setMaxWaitMillis(10*1000);
        poolConfig.setTestOnBorrow(true);
        jedisPool=new JedisPool(poolConfig,host,port,20*1000);
    }

    public Jedis getJedis(){
        Jedis jedis = jedisPool.getResource();
        return jedis;
    }

}

RedisConfig:

代码语言:javascript复制
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration//Spring容器中的注解
public class RedisConfig {

    //读取配置文件中的redis的ip地址,@Value注入赋值
    @Value("${spring.redis.host:disabled}")
    private String host;

    @Value("${spring.redis.port:0}")
    private int port;

    @Value("${spring.redis.database:0}")
    private int database;

    @Bean//将返回值给Spring,Spring容器中就有了RedisUtil(连接池)
    public RedisUtil getRedisUtil(){
        if(host.equals("disabled")){
            return null;
        }
        RedisUtil redisUtil=new RedisUtil();
        redisUtil.initPool(host,port,database);
        return redisUtil;
    }

}

在哪个项目中使用Redis就在application.properties中配置以下:

客户端登录:

cd /usr/local/redis/bin

./redis-cli -h 192.168.0.100 -p 6379

192.168.0.100:6379> ping PONG

测试一下:

代码语言:javascript复制
@RunWith(SpringRunner.class)
@SpringBootTest
public class GmallManageServiceApplicationTests {

    @Autowired
    RedisUtil redisUtil;

    @Test
    public void contextLoads() {

        Jedis jedis = redisUtil.getJedis();

        String ping = jedis.ping();

        System.out.println(ping);
    }

}

使用r edis 进行业务开发

开始开发先说明redis key的命名规范,由于Redis不像数据库表那样有结构,其所有的数据全靠key进行索引,所以redis数据的可读性,全依靠key。

企业中最常用的方式就是:object:id:field

                比如:sku:1314:info

                           user:1092:password

拿一个之前的例子:

代码语言:javascript复制
public SkuInfo getSkuInfo(String skuId){

        Jedis jedis = redisUtil.getJedis();
        String skuKey= RedisConst.sku_prefix skuId RedisConst.skuInfo_suffix;
        String skuInfoJson = jedis.get(skuKey);
        if(skuInfoJson!=null ){
            System.err.println( Thread.currentThread().getName() ":命中缓存"  );
            SkuInfo skuInfo = JSON.parseObject(skuInfoJson, SkuInfo.class);
            jedis.close();
            return skuInfo;
        }else{
               System.err.println( Thread.currentThread().getName() ":未命中缓存"  );

                System.err.println( Thread.currentThread().getName() ": 查询数据##################### ##" );
                SkuInfo skuInfoDB = getSkuInfoDB(skuId);
                String skuInfoJsonStr = JSON.toJSONString(skuInfoDB);
                jedis.setex(skuKey,RedisConst.skuinfo_exp_sec,skuInfoJsonStr);
                System.err.println( Thread.currentThread().getName() ":数据库更新完毕############### #####" );
                jedis.close();
                return skuInfoDB;
        }
}

以上基本实现使用缓存的方案。

高并发时可能会出现的问题:

但在高并发环境下还有如下三个问题。

  1. 如果redis宕机了,或者链接不上,怎么办?
  2. 如果redis缓存在高峰期到期失效,在这个时刻请求会向雪崩一样,直接访问数据库如何处理?
  3.  如果用户不停地查询一条不存在的数据,缓存没有,数据库也没有,那么会出现什么情况,如何处理?
代码语言:javascript复制
 public SkuInfo getSkuInfo(String skuId){
    SkuInfo skuInfo = null;
    try {
        Jedis jedis = redisUtil.getJedis();
        String skuInfoKey = ManageConst.SKUKEY_PREFIX   skuId   ManageConst.SKUKEY_SUFFIX;
        String skuInfoJson = jedis.get(skuInfoKey);

        if (skuInfoJson == null || skuInfoJson.length() == 0) {
            System.err.println(Thread.currentThread().getName() "缓存未命中!");
            String skuLockKey = ManageConst.SKUKEY_PREFIX   skuId   ManageConst.SKULOCK_SUFFIX;
            String lock = jedis.set(skuLockKey, "OK", "NX", "PX", ManageConst.SKULOCK_EXPIRE_PX);

            if ("OK".equals(lock) ){
                System.err.println(Thread.currentThread().getName() "获得分布式锁!");
                skuInfo = getSkuInfoFromDB(skuId);
                if(skuInfo==null){
                    jedis.setex(skuInfoKey, ManageConst.SKUKEY_TIMEOUT, "empty");
                    return null;
                }


                String skuInfoJsonNew = JSON.toJSONString(skuInfo);
                jedis.setex(skuInfoKey, ManageConst.SKUKEY_TIMEOUT, skuInfoJsonNew);
                jedis.close();
                return skuInfo;
            }else{
                System.err.println(Thread.currentThread().getName() "未获得分布式锁,开始自旋!");
                Thread.sleep(1000);
                jedis.close();
                return   getSkuInfo(  skuId);
            }

        } else if(skuInfoJson.equals("empty")){
            return null;
        } else {
            System.err.println(Thread.currentThread().getName() "缓存已命中!!!!!!!!!!!!!!!!!!!");
            skuInfo = JSON.parseObject(skuInfoJson, SkuInfo.class);
            jedis.close();
            return skuInfo;
        }

    }catch (JedisConnectionException e){
        e.printStackTrace();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    return getSkuInfoFromDB(skuId);
}

最近写的也一样:

controller:

代码语言:javascript复制
    @RequestMapping("{skuId}.html")
    public String item(@PathVariable("skuId") String skuId, ModelMap map, HttpServletRequest request){
        SkuInfo skuInfo = skuService.item(skuId,request.getRemoteAddr());
    }

service接口我就不写了

Serviceimpl:

代码语言:javascript复制
@Override
    public SkuInfo item(String skuId,String ip) {
        System.out.println(ip "访问" skuId "商品");
        SkuInfo skuInfo = null;
        //从redis获取redis的客户端jedis
        Jedis jedis = redisUtil.getJedis();
        // 从缓存中取出skuId的数据
        String skuInfoStr = jedis.get("sku:" skuId ":info");
        //Json格式转成实体类类型
        skuInfo = JSON.parseObject(skuInfoStr, SkuInfo.class);
        //从db中取出sku的数据
        //缓存中没有
        if(skuInfo == null){
            System.out.println(ip "发现缓存中没有" skuId "商品数据,申请分布式锁");
            // 拿到分布式锁
            String OK = jedis.set("sku:"   skuId   ":lock", "1", "nx", "px", 10000);
            if(StringUtils.isBlank(OK)){
                System.out.println(ip "分布式锁申请失败,三秒后开始自旋");
                // 缓存锁被占用,等一会儿继续申请
                try {
                    Thread.sleep(3000);//让它等3秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return item(skuId,ip);//自旋,这里没有启动新线程,item(skuId,ip);才会启动新线程
            }else{
                System.out.println(ip "分布式锁申请成功,访问数据库");
                // 拿到缓存锁,可以访问数据库
                skuInfo = getSkuInfo(skuId);
            }
            System.out.println(ip "成功访问数据库后,归还锁,将" skuId "商品放入缓存");
            jedis.del("sku:" skuId ":lock");
        }
        //关闭redis客户端
        jedis.close();
        return skuInfo;
    }

getSkuInfo方法:

代码语言:javascript复制
    public SkuInfo getSkuInfo(String skuId) {
        SkuInfo skuInfo = new SkuInfo();
        skuInfo.setId(skuId);
        SkuInfo skuInfos = skuInfoMapper.selectOne(skuInfo);

        SkuImage skuImage = new SkuImage();
        skuImage.setSkuId(skuId);
        List<SkuImage> skuImages = skuImageMapper.select(skuImage);

        skuInfos.setSkuImageList(skuImages);

        return skuInfos;
    }

如果对于

String skuInfoStr = jedis.get("sku:" skuId ":info");

String OK = jedis.set("sku:" skuId ":lock", "1", "nx", "px", 10000);

jedis.del("sku:" skuId ":lock");

不太理解,大家可以看看前一章节的博客,或者去官网查看

我这里截下来一部分

0 人点赞