redis缓存击穿、缓存穿透、缓存雪崩、缓存一致性解决方案的代码实现

2023-11-18

1.0 缓存击穿

        概念

  一些redis的key过期,同时大量数据请求过期的key或者redis不存在的key,导致大量请求打到数据库,导致数据库瘫痪!!!

        解决方案

        1. 设置热点数据永不过期。

        2. 对热点数据加锁(分布式锁)。

        代码实现(初始化项目)

商品表

 各层级

package com.fei.pojo.entity;


import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * (Goods)表实体类
 *
 * @author makejava
 * @since 2023-04-13 22:09:35
 */
@SuppressWarnings("serial")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Goods  {
    //商品id@TableId
    private Integer id;

    //商品名称
    private String name;
    //商品价格
    private Integer price;
    //商品描述
    private String describe;
    //0-上架 1-下架
    private Integer state;

}

        service

package com.fei.service.impl;

import com.fei.mapper.GoodMapper;
import com.fei.pojo.entity.Goods;
import com.fei.service.GoodService;
import com.fei.util.RedisCache;
import org.springframework.stereotype.Service;

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

@Service("goodService")
public class GoodServiceImpl implements GoodService {
    @Resource
    GoodMapper goodMapper;

    @Resource
    RedisCache redisCache;
    @Override
    public Goods selectGoodById(Integer id) {
        //方案一  最初始化的状态
        //判断缓存中是否存在
        Goods good = (Goods)redisCache.getCacheObject(id+"");
        if(Objects.isNull(good)){
            Goods goods = goodMapper.selectGoodById(id);
            //判断查询商品对象是否为空
            if(!Objects.isNull(goods)) {
                redisCache.setCacheObject(goods.getId().toString(), goods, 2, TimeUnit.HOURS);
            }
        }
        return goodMapper.selectGoodById(id);
    }

    @Override
    public List<Goods> selectGoods() {
        return goodMapper.selectGoods();
    }
}

1.1 避免缓存击穿代码实现

        判断是否为热点数据-->是热点数据申请锁-->申请成功进行加分布式锁-->去数据库查该热点数据-->将数据设置为永不过期-->释放锁-->申请失败则进行自旋去申请锁

        首先分布式锁存在一些问题

        1. 锁的内容出现异常导致一直被占用 

                可以通过设置redis过期时间来解决

        2. A锁没有过期,B锁把A锁释放

                可以用UUID解决

        3. 分布式锁,设置锁,判断锁,删除锁存在原子性问题

                可以使用lua脚本解决

在resouce下写lua脚本

if redis.call("get",KEYS[1])==ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end

注入到spring中

    @Bean
    public DefaultRedisScript<Boolean> script(){
        DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>();
        //lock.lua脚本位于 application.yml同级目录
        redisScript.setLocation(new ClassPathResource("lock.lua"));
        redisScript.setResultType(Boolean.class);
        return redisScript;
    }

查询商品解决缓存击穿代码如下

package com.fei.service.impl;

import com.fei.controller.UserController;
import com.fei.mapper.GoodMapper;
import com.fei.pojo.entity.Goods;
import com.fei.service.GoodService;
import com.fei.util.RedisCache;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;

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

@Service
public class GoodServiceImpl implements GoodService {
    private Logger logger = LoggerFactory.getLogger(GoodServiceImpl.class);
    @Resource
    GoodMapper goodMapper;
    @Resource
    RedisCache redisCache;

    @Resource
    RedisTemplate redisTemplate;

    @Autowired
    RedisScript<Boolean> script;

    String LOCK_PREFIX = "lock:good:id:";
    @Override
    public Goods selectGoodById(Integer id) {
//        //判断缓存中是否存在
//        Goods good = (Goods)redisCache.getCacheObject(id+"");
//        if(Objects.isNull(good)){
//            Goods goods = goodMapper.selectGoodById(id);
//            //判断查询商品对象是否为空
//            if(!Objects.isNull(goods)) {
//                redisCache.setCacheObject(goods.getId().toString(), goods, 2, TimeUnit.HOURS);
//            }
//        }
//        return goodMapper.selectGoodById(id);

        //判断缓存中是否存在
        Goods good = (Goods)redisCache.getCacheObject(id.toString());
        logger.info("在缓存中取出id=={}的商品信息为{}",id,good);
        if(Objects.isNull(good)){
            //先判断这个id是不是爆款商品
            //这里假设 id = 1 的属于爆款商品
            if(id == 1){
                //分布式锁的key
                String lockKey = LOCK_PREFIX + id;
                // 每一把锁都有自己独有的uuid
                String uuid = UUID.randomUUID().toString();
                //设置分布式锁
                ValueOperations valueOperations = redisTemplate.opsForValue();
                //对锁进行占位
                Boolean isLock = valueOperations.setIfAbsent(lockKey, uuid, 300, TimeUnit.SECONDS);
                logger.info("分布式锁的状态{}",isLock);
                if(isLock){
                    Goods goods = goodMapper.selectGoodById(id);
                    logger.info("获得加锁的key-->{}",valueOperations.get(lockKey));
                    if(!Objects.isNull(goods)){
                        valueOperations.set(goods.getId().toString(),goods);
                        Boolean result=(Boolean)redisTemplate.execute(script, Collections.singletonList(lockKey),uuid);
                        return goods;
                    }
                    return goods;
                }else {
                    logger.info("申请锁失败,进行自旋");
                    try {
                        Thread.sleep(1500);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    return selectGoodById(id);
                }
            }
            Goods goods = goodMapper.selectGoodById(id);
            //判断查询商品对象是否为空
            if(!Objects.isNull(goods)) {
                redisCache.setCacheObject(goods.getId().toString(), goods, 2, TimeUnit.HOURS);
            }
            return goods;
        }
        return good;
    }


    @Override
    public List<Goods> selectGoods() {
        return goodMapper.selectGoods();
    }
}

自己语言叙述为:

        首先判断缓存中是否存在 --> 不存在再判断是否是热点数据 --> 如果是热点数据则使用 分布式锁 用lua脚本保持原子性 存入缓存返回数据--> 不是热点数据则直接查询数据库,存储缓存!!返回数据

2.0 缓存穿透

        描述

当用户发起请求,先去缓存中查找,没找到又去持久层数据库中查找也没找到,这种算是查询失败。在高并发情况下出现大量这种查询失败,导致持久层压力过大,甚至宕机,叫做缓存穿透。

        解决方案

1. 布隆过滤器

2. 将查找的不存在的数据进行缓存null(缓存过期时间设置短一点,节省空间)

2.1 缓存穿透代码的实现

       2.1.1 导入布隆过滤器所需要的依赖

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>21.0</version>
        </dependency>

  2.1.2 写一个布隆过滤器的工具类

package com.fei.util;

import com.fei.mapper.GoodMapper;
import com.fei.pojo.entity.Goods;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;

import java.util.List;

/**
 * 布隆过滤器的工具类
 */
public class BloomFilterUtil {


    //布隆过滤器的容量
    public static final int CATACITY = 1000000;

    private static BloomFilter<Integer>  bloomFilter=BloomFilter.create(Funnels.integerFunnel(),CATACITY);


    public static BloomFilter<Integer> getBloomFilter(){
        return bloomFilter;
    }
    /**
     * 判断是否包含key(有可能有误差)
     * @param i
     * @return
     */
    public static boolean mayContains(Integer i){
        return bloomFilter.mightContain(i);
    }



}

2.1.3

        将商品的id在初始化的时候都加载到布隆过滤器中

package com.fei.config;

import com.fei.mapper.GoodMapper;
import com.fei.pojo.entity.Goods;
import com.fei.service.impl.GoodServiceImpl;
import com.fei.util.BloomFilterUtil;
import com.google.common.hash.BloomFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;

@Component
@Order(3)
public class InitClass implements ApplicationRunner {
    private Logger logger = LoggerFactory.getLogger(InitClass.class);
    @Resource
    GoodMapper goodMapper;
    @Override
    public void run(ApplicationArguments args) throws Exception {
        List<Goods> goodsList = goodMapper.selectGoods();
        logger.info("初始化商品id到布隆过滤器中,商品id---->{}",goodsList);
        BloomFilter<Integer> bloomFilter = BloomFilterUtil.getBloomFilter();
        for(Goods good : goodsList){
            logger.info("初始化商品id到布隆过滤器中,商品id---->{}",good.getId());
            bloomFilter.put(good.getId());
        }
    }
}

 2.1.4 改进后的代码

package com.fei.service.impl;

import com.fei.controller.UserController;
import com.fei.mapper.GoodMapper;
import com.fei.pojo.entity.Goods;
import com.fei.service.GoodService;
import com.fei.util.BloomFilterUtil;
import com.fei.util.RedisCache;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;

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

@Service
public class GoodServiceImpl implements GoodService {
    private Logger logger = LoggerFactory.getLogger(GoodServiceImpl.class);
    @Resource
    GoodMapper goodMapper;
    @Resource
    RedisCache redisCache;

    @Resource
    RedisTemplate redisTemplate;

    @Autowired
    RedisScript<Boolean> script;

    String LOCK_PREFIX = "lock:good:id:";
    @Override
    public Goods selectGoodById(Integer id) {
//        //判断缓存中是否存在
//        Goods good = (Goods)redisCache.getCacheObject(id+"");
//        if(Objects.isNull(good)){
//            Goods goods = goodMapper.selectGoodById(id);
//            //判断查询商品对象是否为空
//            if(!Objects.isNull(goods)) {
//                redisCache.setCacheObject(goods.getId().toString(), goods, 2, TimeUnit.HOURS);
//            }
//        }
//        return goodMapper.selectGoodById(id);

        //判断缓存中是否存在
        Goods good = (Goods)redisCache.getCacheObject(id.toString());
        //布隆过滤器过滤
        logger.info("在缓存中取出id=={}的商品信息为{}",id,good);
        boolean isExist = BloomFilterUtil.mayContains(id);
        if(isExist) {
            logger.info("布隆过滤器是否存在值------>{}", isExist);
            if (Objects.isNull(good)) {
                //先判断这个id是不是爆款商品
                //这里假设 id = 1 的属于爆款商品
                if (id == 1) {
                    //分布式锁的key
                    String lockKey = LOCK_PREFIX + id;
                    // 每一把锁都有自己独有的uuid
                    String uuid = UUID.randomUUID().toString();
                    //设置分布式锁
                    ValueOperations valueOperations = redisTemplate.opsForValue();
                    //对锁进行占位
                    Boolean isLock = valueOperations.setIfAbsent(lockKey, uuid, 300, TimeUnit.SECONDS);
                    logger.info("分布式锁的状态{}", isLock);
                    if (isLock) {
                        Goods goods = goodMapper.selectGoodById(id);
                        logger.info("获得加锁的key-->{}", valueOperations.get(lockKey));
                        if (!Objects.isNull(goods)) {
                            valueOperations.set(goods.getId().toString(), goods);
                            Boolean result = (Boolean) redisTemplate.execute(script, Collections.singletonList(lockKey), uuid);
                            return goods;
                        }
                        return goods;
                    } else {
                        logger.info("申请锁失败,进行自旋");
                        try {
                            Thread.sleep(1500);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        return selectGoodById(id);
                    }
                }
                Goods goods = goodMapper.selectGoodById(id);
                //判断查询商品对象是否为空
                if (!Objects.isNull(goods)) {
                    redisCache.setCacheObject(goods.getId().toString(), goods, 2, TimeUnit.HOURS);
                } else {
                    //缓存空对象
                    //因为布隆过滤器可能有错误值进来,所以这里缓存null
                    redisCache.setCacheObject(id.toString(), null, 200, TimeUnit.SECONDS);
                }
                return goods;
            }
        }else {
            //布隆过滤器不存在
            //缓存空对象
            redisCache.setCacheObject(id.toString(), null, 200, TimeUnit.SECONDS);
        }
        return good;
    }


    @Override
    public List<Goods> selectGoods() {
        return goodMapper.selectGoods();
    }
}

3.0 缓存雪崩

        概念

缓存层出现错误,不能正确运行,导致所有请求打到存储层,造成存储层挂掉的情况。

        解决方案

1. 建立redis集群,达到高可用

2. 加随机因子(根据商品的冷热程度)

3. 限流降级(已经发生的情况下)

4. 加锁排队(同缓存击穿)

3.1 代码的实现

        这里主要写2. 加随机因子预防缓存雪崩,主要根据热度的不同加不同随机因子

 这里假设id =1 的为爆款  id=2的为除爆款外的第二梯度,id = 3的为第三梯度

实际开发中可以按商品类别分类热门程度等等

        实现加随机因子代码如下:

package com.fei.service.impl;

import com.fei.controller.UserController;
import com.fei.mapper.GoodMapper;
import com.fei.pojo.entity.Goods;
import com.fei.service.GoodService;
import com.fei.util.BloomFilterUtil;
import com.fei.util.RedisCache;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;

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

@Service
public class GoodServiceImpl implements GoodService {
    private Logger logger = LoggerFactory.getLogger(GoodServiceImpl.class);
    @Resource
    GoodMapper goodMapper;
    @Resource
    RedisCache redisCache;

    @Resource
    RedisTemplate redisTemplate;

    @Autowired
    RedisScript<Boolean> script;

    String LOCK_PREFIX = "lock:good:id:";
    @Override
    public Goods selectGoodById(Integer id) {
//        //判断缓存中是否存在
//        Goods good = (Goods)redisCache.getCacheObject(id+"");
//        if(Objects.isNull(good)){
//            Goods goods = goodMapper.selectGoodById(id);
//            //判断查询商品对象是否为空
//            if(!Objects.isNull(goods)) {
//                redisCache.setCacheObject(goods.getId().toString(), goods, 2, TimeUnit.HOURS);
//            }
//        }
//        return goodMapper.selectGoodById(id);

        //判断缓存中是否存在
        Goods good = (Goods)redisCache.getCacheObject(id.toString());
        //布隆过滤器过滤
        logger.info("在缓存中取出id=={}的商品信息为{}",id,good);
        boolean isExist = BloomFilterUtil.mayContains(id);
        if(isExist) {
            logger.info("布隆过滤器是否存在值------>{}", isExist);
            if (Objects.isNull(good)) {
                //先判断这个id是不是爆款商品
                //这里假设 id = 1 的属于爆款商品
                if (id == 1) {
                    //分布式锁的key
                    String lockKey = LOCK_PREFIX + id;
                    // 每一把锁都有自己独有的uuid
                    String uuid = UUID.randomUUID().toString();
                    //设置分布式锁
                    ValueOperations valueOperations = redisTemplate.opsForValue();
                    //对锁进行占位
                    Boolean isLock = valueOperations.setIfAbsent(lockKey, uuid, 300, TimeUnit.SECONDS);
                    logger.info("分布式锁的状态{}", isLock);
                    if (isLock) {
                        Goods goods = goodMapper.selectGoodById(id);
                        logger.info("获得加锁的key-->{}", valueOperations.get(lockKey));
                        if (!Objects.isNull(goods)) {
                            valueOperations.set(goods.getId().toString(), goods);
                            Boolean result = (Boolean) redisTemplate.execute(script, Collections.singletonList(lockKey), uuid);
                            return goods;
                        }
                        return goods;
                    } else {
                        logger.info("申请锁失败,进行自旋");
                        try {
                            Thread.sleep(1500);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        return selectGoodById(id);
                    }
                }
                //非爆款
                Goods goods = goodMapper.selectGoodById(id);
                Random random = new Random();
                int timeOut = 0;
                //判断查询商品对象是否为空
                if (!Objects.isNull(goods)) {
                    if(id == 2){
                        timeOut = 3600+random.nextInt(1800);
                        redisCache.setCacheObject(goods.getId().toString(), goods, timeOut, TimeUnit.SECONDS);
                    }
                    if(id == 3){
                        timeOut = 1800 + random.nextInt(300);
                        redisCache.setCacheObject(goods.getId().toString(), goods, timeOut, TimeUnit.SECONDS);
                    }
                    
                } else {
                    //缓存空对象
                    //因为布隆过滤器可能有错误值进来,所以这里缓存null
                    redisCache.setCacheObject(id.toString(), null, 200, TimeUnit.SECONDS);
                }
                return goods;
            }
        }else {
            //布隆过滤器不存在
            //缓存空对象
            redisCache.setCacheObject(id.toString(), null, 200, TimeUnit.SECONDS);
        }
        return good;
    }


    @Override
    public List<Goods> selectGoods() {
        return goodMapper.selectGoods();
    }
}

4.0 缓存一致性问题

        概念

Redis缓存一致性解决方案主要思考的是删除缓存和更新数据库的先后顺序

        解决方案

1. 先删除缓存后更新数据库

        存在的问题是可能会数据不一致,一般使用延时双删来解决,即先删除缓存,再更新数据库,休眠X秒后再次淘汰缓存。第二次删除可能导致吞吐率降低,可以考虑进行异步删除。

2. 先更新数据库后删除缓存

        存在的问题是会可能会更新失败,可以采用延时删除。但由于读比写快,发生这一情况概率较小。

4.1 代码实现

        自定义注解

package com.fei.annotation;

import java.lang.annotation.*;

/**
 * 延时双删注解
 */
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.METHOD)
public @interface DelayedDoubleDeletion {
    boolean open() default true;
}

        切面类

package com.fei.pojo;

import com.fei.annotation.DelayedDoubleDeletion;
import com.fei.util.RedisCache;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.CodeSignature;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Set;

@Aspect
@Component
public class DelayedDoubleDeletionAspect {
    private Logger logger = LoggerFactory.getLogger(DelayedDoubleDeletionAspect.class);
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    private RedisCache redisCache;

    /**
    * 切入点
    *切入点,基于注解实现的切入点  加上该注解的都是Aop切面的切入点
    *
    */

    @Pointcut("@annotation(com.fei.annotation.DelayedDoubleDeletion)")
    public void pointCut(){

    }
    /**
    * 环绕通知
    * 环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值。
    * 环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型
    * @param proceedingJoinPoint
    */
    @Around("pointCut()")
    public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint){

        logger.info("----------- 环绕通知 -----------");
        logger.info("环绕通知的目标方法名:{}",proceedingJoinPoint.getSignature().getName());
        Signature signature1 = proceedingJoinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature)signature1;
        //方法对象
        Method targetMethod = methodSignature.getMethod();
        //反射得到自定义注解的方法对象
        DelayedDoubleDeletion annotation =
                targetMethod.getAnnotation(DelayedDoubleDeletion.class);
        Object[] values = proceedingJoinPoint.getArgs();
        String[] names = ((CodeSignature) proceedingJoinPoint.getSignature()).getParameterNames();

        //获取自定义注解的方法对象的参数即name
        boolean isTure = annotation.open();
        if(isTure){
            logger.info("获得参数值---->{}",values[0].toString());
            //先删除缓存
            if(!StringUtils.isEmpty(redisCache.getCacheObject(values[0].toString()))){
                redisCache.deleteObject(values[0].toString());
            }
        }

        //执行加入双删注解的改动数据库的业务 即controller中的方法业务
        Object proceed = null;
        try {
            proceed = proceedingJoinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        //开一个线程 延迟1秒(此处是1秒举例,可以改成自己的业务)
        // 在线程中延迟删除  同时将业务代码的结果返回 这样不影响业务代码的执行
        new Thread(() -> {
            try {
                Thread.sleep(1000);
                if(!StringUtils.isEmpty(redisCache.getCacheObject(values[0].toString()))){
                    redisCache.deleteObject(values[0].toString());
                }
                logger.info("-----------1秒钟后,在线程中延迟删除完毕 -----------");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        //返回业务代码的值
        return proceed;
        }
}

        在这里我缓存的是id,所以我延时双删,删除的是缓存的id,通常key为id,value为商品信息,这里不多改变,理解意思即可

    @RequestMapping("/updateGood")
    @DelayedDoubleDeletion()
    public ResponseResult updateGood(Integer id,String goodName){
        goodService.updateGood();
        return new ResponseResult(200,"更新成功");

    }

        这里redis缓存相关的问题就结束了!!!

        下一篇 准备写一些关于实际项目中线程池的用法实战!!!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

redis缓存击穿、缓存穿透、缓存雪崩、缓存一致性解决方案的代码实现 的相关文章

  • 是否可以使用带有 FUSE 文件系统的 Linux VFS 缓存?

    默认情况下 Linux VFS 缓存似乎不适用于 FUSE 文件系统 例如 read 调用似乎被系统地转发到 FUSE 文件系统 我在 FUSE 特定的远程文件系统上工作 我需要一个非常积极的缓存 我需要实现自己的页面缓存吗 或者是否可以为
  • Docker&Celery - 错误:Pidfile (celerybeat.pid) 已存在

    应用程序包括 姜戈 雷迪斯 芹菜 码头工人 Postgres 在将项目合并到 docker 之前 一切都运行顺利且正常 但是一旦将其移入容器 就开始出现问题 起初它开始得很好 但过了一会儿我确实收到了以下错误 celery beat 1 E
  • 如何在redis中创建自己的数据库?

    There are 0 to 15 databases in redis 我想使用 redis cli 创建自己的数据库 有什么命令可以实现吗 Redis 数据库并不等同于 MySQL 等 DBMS 中的数据库名称 这是一种为键创建隔离和命
  • connect-redis - 如何保护会话对象免受竞争条件影响

    我使用 nodejs 和 connect redis 来存储会话数据 我将用户数据保存在会话中 并在会话生命周期中使用它 我注意到两个更改会话数据的请求之间可能存在竞争条件 我尝试过使用 redis lock 来锁定会话 但这对我来说有点问
  • Node.js 上通过套接字连接 Redis

    由于共享托管 目标主机上的我的 redis 服务器不在端口上运行 而是在非常特定的套接字上运行 可以通过套接字文件连接到该套接字 只有我的用户可以访问 但是 我还没有找到如何通过套接字指定连接node redis and connect r
  • Stackexchange.redis 缺乏“WAIT”支持

    我在客户端应用程序正在使用的负载均衡器后面有 3 个 Web API 服务器 我正在使用这个库来访问具有一个主服务器和几个从服务器的 Redis 集群 目前不支持 WAIT 操作 我需要此功能来存储新创建的用户会话并等待它复制到所有从属服务
  • 使用 AWS ElastiCache 请求中的 Airflow CROSSSLOT 密钥未散列到同一插槽错误

    我在 AWS ECS 上运行 apache airflow 1 8 1 并且有一个 AWS ElastiCache 集群 redis 3 2 4 运行 2 个分片 2 个启用多可用区的节点 集群 Redis 引擎 我已经验证气流可以毫无问题
  • 库存管理系统的 SQL 与 NoSQL

    我正在开发一个基于 JAVA 的网络应用程序 主要目的是拥有在多个称为渠道的网站上销售的产品的库存 我们将担任所有这些渠道的管理者 我们需要的是 用于管理每个渠道的库存更新的队列 库存表 其中包含每个通道上分配的正确快照 将会话 ID 和其
  • 从redis中检索大数据集

    一台服务器上的应用程序查询另一台服务器上运行的 Redis 查询的结果数据集约为 250kzrangebyscore objects locations inf inf这在应用程序服务器上似乎需要 40 秒 当使用命令执行时redis cl
  • 如何将 ActionController::Live 与 Resque + Redis 一起使用(用于聊天应用程序)

    我正在尝试为我的 Rails 应用程序构建聊天功能 我在用ActionController Live Puma Resque Redis为了这 所以基本上在这种情况下 redissubscribe方法正在后台运行 使用resque 到目前为
  • 在 Kubernetes/Openshift 中将客户端-服务器流量保持在同一区域的最佳方法?

    我们运行兼容 Kubernetes OKD 3 11 的本地 私有云集群 其中后端应用程序与用作缓存和 K V 存储的低延迟 Redis 数据库进行通信 新的架构设计将在两个地理上分布的数据中心 区域 之间平均划分工作节点 我们可以假设节点
  • 如何将node.js管道传输到redis?

    我有很多数据要插入 SET INCR 到redis DB 所以我正在寻找pipeline http redis io topics pipelining 质量插入 http redis io topics mass insert通过node
  • Redis SYNC 套接字上的错误情况:连接被拒绝

    在我的 django 应用程序中使用 celery 和 redis 一切都工作正常 直到我遇到了问题 redis 文件的位置已更改 redis 无法访问它们 经过查找 原来这是由于网络随机攻击造成的 需要添加confg 我添加文件后 一段时
  • socket.io 广播功能 & Redis pub/sub 架构

    如果有人能帮助我解决一个小疑问 我将不胜感激 使用socket io广播功能和在Redis上使用pub sub设计架构有什么区别 例如 在另一个示例中 node js 服务器正在侦听 socket io 针对 键 模型 todo 和值 数据
  • 有没有办法在 ruby​​ 中重新定义 []=+

    我正在尝试编写一个简单的 DSL 针对 Redis 并且我想自己定义 I have def key val redis zadd name val key end 我想定义 def key val redis zincrby name va
  • 如何在Redis中进行持久化存储?

    关闭redis服务器后 使用set存储的值被破坏 在这里我找到了使用持久性存储的方法 有人帮助我 如何使用javascript实现这一点 我想将客户端的一些值存储在 redis 数据库中 并且必须在其他客户端中使用该值 您需要配置 Redi
  • Lua中按字符分割字符串

    我有像这样的字符串 ABC DEF 我需要将它们分开 字符并将两个部分分别分配给一个变量 在 Ruby 中 我会这样做 a b ABC DEF split 显然Lua没有这么简单的方法 经过一番挖掘后 我找不到一种简短的方法来实现我所追求的
  • 为什么 Redis TimeSeries 不捕获聚合中的最后一个元素?

    我试图了解 Redis 的时间序列规则创建的工作原理 但我很困惑为什么 Redis 会忽略聚合中的最后一项 并想知道这是否是预期的行为 我在中创建了示例代码redis cli为了显示 127 0 0 1 6379 gt FLUSHALL O
  • 2 个具有共享 Redis 依赖的 Helm Chart

    目前 我有 2 个 Helm Charts Chart A 和 Chart B Chart A 和 Chart B 对 Redis 实例具有相同的依赖关系 如Chart yaml file dependencies name redis v
  • 为什么Redis中没有有序的hashmap?

    Redis 数据类型 http redis io topics data types包括排序集 http redis io topics data types intro sorted sets以及其他用于键值存储的必要数据结构 但我想知道

随机推荐

  • kubernetes最佳实践(三) - kubedns部署

    1 服务发现 kubernetes 提供了 service 的概念可以通过 VIP 访问 pod 提供的服务 但是在使用的时候还有一个问题 怎么知道某个应用的 VIP 比如我们有两个应用 一个 app 一个 是 db 每个应用使用 rc 进
  • Spring boot的配置文件中属性值有特殊符号,比如@的怎么解决

    用双引号将值引起来就可以识别 例子
  • 【Zabbix实战之运维篇】Zabbix监控模板的配置管理

    Zabbix实战之运维篇 Zabbix监控模板的配置管理 一 检查Zabbix平台的状态 1 检查Zabbix各组件容器状态 2 检查Zabbix的web页面 二 查看系统的默认模板信息 1 查看系统的所有监控模板 2 搜索某个监控模板 3
  • springCloud-系统学习3- 创建微服务工程2

    2 11 Feign应用 是对下面代码的优化 自动根据参数拼接http请求地址 2 11 1 操作 效果 2 12 Feign负载均衡及熔断 Feign集成了ribbon配置项和Hystrix熔断的Fallback配置项 可以使用Feign
  • 基于ARM编译安装docker-harbor

    基于ARM编译安装docker harbor 一 编译内核 此举是为了保证redis镜像可以正常启动 1 安装依赖 yum y install gcc bc gcc c ncurses ncurses devel cmake elfutil
  • Python3 类型转换

    INT 支持转换为INT类型的 仅有 float str bytes 其他类型均不支持 float gt int 会去掉小数点及后面的数值 仅保留整数部分 int 12 94 12 str gt int 如果字符串中有数字 0 9 和正负号
  • 将yyyy-MM-dd hh:mm:ss转化为yyyy-MM-dd

    Date currentTime new Date SimpleDateFormat formatter new SimpleDateFormat yyyy MM dd Date strtodate String datas try str
  • Java异常和处理机制

    棒棒有言 追逐梦想的过程就像是一个人在走一条黑暗 幽深而又漫长的隧道 多少次跌倒又爬起 经历了多少个暗无天日的黑夜与白天 一路上沉淀着难以计数的汗水与泪水 不断地自我暗示 只要自己坚持 只要勇敢地一向往前走 就必须能找到出口 必须会看到光明
  • 读书笔记 摘自:《思维导图攻略:快速上手与落地实践》

    思维导图攻略 快速上手与落地实践 王健文 出版 2019 01 01 7 3万字 内容提要 无落地 不导图 思维导图的学习并不在于思维导图的绘制本身 而是在于实际应用和思维提升 第一章 精英人士自我提升的思维利器 第一节 提升大脑学习力的秘
  • 广州华锐互动:利用VR复原文化遗址,沉浸式体验历史文物古迹的魅力

    在过去的几十年里 科技发展飞速 为我们打开了无数新的视角和可能性 其中 虚拟现实 Virtual Reality 简称VR 技术的崭新应用 为我们提供了一种全新的 近乎身临其境的体验历史的方式 本文将重点探讨VR技术在复原历史古迹方面的应用
  • How to use tar command to complete file compression and decompression in Ubuntu

    TAR 1 GNU TAR Manual TAR 1 NAME tar an archiving utility SYNOPSIS Traditional usage tar A
  • beam search的例子

    看了一下网上对beam search的讲解 感觉都说的太杂了 我试图用一个最简单的例子来帮助读者理解 见下图 假设我有一个模型 能够根据当前词输出下一个词的概率分布 最后依次这样就能生成一大串文本 以上面的图为例 The 的下一个词的最大概
  • Spring StateMachine使用笔记

    Spring StateMachine使用笔记 配置状态机 状态 分层状态 withStates 配置状态 states状态列表 可以使用多个withStates进行parent分层 配置区域 当相同的分层状态机具有多组状态时 每个都具有初
  • 系统移植开发阶段部署

    开发阶段部署阶段 uboot镜像 ubootpak bin flash SD linux内核镜像 uImage tftp下载 根文件系统 rootfs nfs挂载 本文操作需要用到uboot命令进行镜像搬移和根文件系统挂载 uboot中常用
  • EPS学习笔记3----------常用地物采集方法(房屋,斜坡,台阶)

    1 三维模型中房屋绘制方法 面状地物 五点房 不会自动弹出房屋属性录入窗口 任意绘制法 绘制结束弹出房屋属性录入窗口 多点法 1 首先在房屋某一面用鼠标左键选择一点 2 鼠标移到房屋屋檐处 利用shift A将前一节点高程移到屋檐高程 3
  • 前端开发常见面试题第三篇(Vue和React)

    文章目录 1 Vue中直接获取组件内的DOM元素或子组件实例 2 生命周期 3 双向绑定原理 4 v if和v show的区别 5 Vue导航钩子 6 路由跳转的方式 7 vuex常用的是什么 怎么使用 8 父子组件之间通信 9 跨域处理
  • Java排序算法:选择排序

    Java排序算法 选择排序 选择排序它的主要思想是 在未排序的数组中选择最小的元素 然后将其放置在数组的起始位置 再在剩余的未排序数组中选择最小元素 并将其放置在已排序部分的末尾 重复此过程 直到整个数组排序完成 选择排序的步骤如下 1 从
  • 15款业界公认的最佳视频处理软件

    因为需要购买昂贵的视频处理软件和高性能图形计算机 所以视频处理是一项比较耗费金钱的技术活 正是由于这样 一部分人选择使用性能较好的免费在线编辑软件 无需太多视频处理知识便可在浏览器中剪切和编辑视频 然而 当我们无法连接网络或网络很慢的时候
  • IC卡片使用基础

    一 卡片基本概念 集成电路卡 即日常生活中常使用的卡片 可以根据读写特性分为两类 ID卡和IC卡 ID卡 全称身份识别卡 是一种不可写入的感应卡 含固定编号 卡内除了卡号外 无任何保密功能 其 卡号 是公开 裸漏的 IC卡 带有存储器 又称
  • redis缓存击穿、缓存穿透、缓存雪崩、缓存一致性解决方案的代码实现

    1 0 缓存击穿 概念 一些redis的key过期 同时大量数据请求过期的key或者redis不存在的key 导致大量请求打到数据库 导致数据库瘫痪 解决方案 1 设置热点数据永不过期 2 对热点数据加锁 分布式锁 代码实现 初始化项目 商