Redis 高并发缓存架构实战与性能优化

前置知识

1、缓存击穿、缓存失效的基本概念
2、什么样的 数据 需要加分布式锁
3、课上代码

05-一线大厂Redis高并发缓存架构实战与性能优化

对于 公司 中 简单的增删改查 做 高性能处理 , 采用递进的方式一步步优化。

  1. 普通Redis用法: 新增、修改、删除设置缓存 , 查询是获取缓存 ,如果没获取到取数据库 。
  2. 实现简单的数据冷热分离: 采用 对 key 加 过期时间 、 查询时 做 缓存读延期 处理。( 这种 针对于 大规模 数据 , 如京东商品 可能 上亿, 但是 只有1% 的商品经常访问 。 )
  3. 对于数据的批量更新问题: 批量更新会导致大量数据的数据同时过期, ( 数据库压力可能增大,导致数据库抖动 这就是缓存击穿 )在设置过期时间时添加 随机过期时间 解决缓存击穿问题
  4. 解决缓存穿透问题: 如果数据已经删除,但是还有请求 到查询接口,数据库与Redis都查不到。 解决方案:设置空缓存,并设置一个相对较短的过期时间。( 即使有几十万 空数据缓存 , 那么 在 几分钟后都失效了,不会占用 缓存空间)
  5. 冷门数据缓存重建问题: DCL(double check look)机制实现缓存重建
  6. 使用分布式锁解决缓存数据库双写不一致
  7. 如果读多写少 使用读写锁 优化分布式锁,
  8. 优化DCL锁,使用tryLock(权衡使用与否)
  9. 对于单节点 redis 并发 差不多十万,如果是上百万并发同时访问redis 不一定能抗住,会导致缓存雪崩。需要本地缓存架构解决,如Guava、Ehcache。

代码

复制代码
@Service
public class ProductService {

    @Autowired
    private ProductDao productDao;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private Redisson redisson;

    public static final Integer PRODUCT_CACHE_TIMEOUT = 60 * 60 * 24;
    public static final String EMPTY_CACHE = "{}";
    public static final String LOCK_PRODUCT_HOT_CACHE_PREFIX = "lock:product:hot_cache:";
    public static final String LOCK_PRODUCT_UPDATE_PREFIX = "lock:product:update:";
    public static Map<String, Product> productMap = new ConcurrentHashMap<>();

    @Transactional
    public Product create(Product product) {
        Product productResult = productDao.create(product);
        redisUtil.set(RedisKeyPrefixConst.PRODUCT_CACHE + productResult.getId(), JSON.toJSONString(productResult),
                genProductCacheTimeout(), TimeUnit.SECONDS);
        return productResult;
    }

    @Transactional
    public Product update(Product product) {
        Product productResult = null;
        //RLock updateProductLock = redisson.getLock(LOCK_PRODUCT_UPDATE_PREFIX + product.getId());
        RReadWriteLock readWriteLock = redisson.getReadWriteLock(LOCK_PRODUCT_UPDATE_PREFIX + product.getId());
        RLock writeLock = readWriteLock.writeLock();
        writeLock.lock();
        try {
            productResult = productDao.update(product);
            redisUtil.set(RedisKeyPrefixConst.PRODUCT_CACHE + productResult.getId(), JSON.toJSONString(productResult),
                    genProductCacheTimeout(), TimeUnit.SECONDS);
            productMap.put(RedisKeyPrefixConst.PRODUCT_CACHE + productResult.getId(), product);
        } finally {
            writeLock.unlock();
        }
        return productResult;
    }

    public Product get(Long productId) throws InterruptedException {
        Product product = null;
        String productCacheKey = RedisKeyPrefixConst.PRODUCT_CACHE + productId;

        product = getProductFromCache(productCacheKey);
        if (product != null) {
            return product;
        }
        //DCL
        RLock hotCacheLock = redisson.getLock(LOCK_PRODUCT_HOT_CACHE_PREFIX + productId);
        hotCacheLock.lock();
        //boolean result = hotCacheLock.tryLock(3, TimeUnit.SECONDS);
        try {
            product = getProductFromCache(productCacheKey);
            if (product != null) {
                return product;
            }

            //RLock updateProductLock = redisson.getLock(LOCK_PRODUCT_UPDATE_PREFIX + productId);
            RReadWriteLock readWriteLock = redisson.getReadWriteLock(LOCK_PRODUCT_UPDATE_PREFIX + productId);
            RLock rLock = readWriteLock.readLock();
            rLock.lock();
            try {
                product = productDao.get(productId);
                if (product != null) {
                    redisUtil.set(productCacheKey, JSON.toJSONString(product),
                            genProductCacheTimeout(), TimeUnit.SECONDS);
                    productMap.put(productCacheKey, product);
                } else {
                    redisUtil.set(productCacheKey, EMPTY_CACHE, genEmptyCacheTimeout(), TimeUnit.SECONDS);
                }
            } finally {
                rLock.unlock();
            }
        } finally {
            hotCacheLock.unlock();
        }
        return product;
    }


    private Integer genProductCacheTimeout() {
        return PRODUCT_CACHE_TIMEOUT + new Random().nextInt(5) * 60 * 60;
    }

    private Integer genEmptyCacheTimeout() {
        return 60 + new Random().nextInt(30);
    }

    private Product getProductFromCache(String productCacheKey) {
        Product product = productMap.get(productCacheKey);
        if (product != null) {
            return product;
        }

        String productStr = redisUtil.get(productCacheKey);
        if (!StringUtils.isEmpty(productStr)) {
            if (EMPTY_CACHE.equals(productStr)) {
                redisUtil.expire(productCacheKey, genEmptyCacheTimeout(), TimeUnit.SECONDS);
                return new Product();
            }
            product = JSON.parseObject(productStr, Product.class);
            redisUtil.expire(productCacheKey, genProductCacheTimeout(), TimeUnit.SECONDS); //读延期
        }
        return product;
    }

}
相关推荐
天空属于哈夫克38 小时前
企业微信二次开发:精准实现关键词自动回复
架构·企业微信
晨米酱9 小时前
AGENTS.md:Agent 的上下文策略层
面试·架构·agent
这个DBA有点耶9 小时前
MVCC深入:Read View、版本链与快照读——InnoDB并发控制的内核
数据库·mysql·架构
IT大白鼠11 小时前
Redis 系列 · 第 01 篇——认知入门:Redis 是什么
redis·nosql
码流子11 小时前
高速公路安全监测实践:碰撞监测预警+物联网底座,从感知到处置的闭环
大数据·人工智能·物联网·算法·架构
彧azz11 小时前
Linux 环境下 Redis 学习总结:数据类型、持久化、锁、事务、主从与缓存问题
linux·redis·笔记·学习·面试
moMo11 小时前
从固定流程到问题路由:让 LangGraph RAG 按需检索
架构
全栈弄潮儿²⁰²⁴11 小时前
AI Agent 开发实战(30):限流、缓存与成本控制
人工智能·gpt·缓存·agent·限流·agi·成本控制
白远山12 小时前
上海24小时自助健身房解决方案实战指南与经验分享
java·数据库·架构·需求分析
LorryJovens12 小时前
【LAAP科研】双系统具身AGI范式研究——基于LAAP认知架构与Jev概率决策模型的系统性技术调研与范式验证
人工智能·gpt·安全·架构