Redis

关注公众号 jb51net

关闭
首页 > 数据库 > Redis > Redis高并发缓存

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

作者:枫度柚子

本文主要介绍了浅谈Redis高并发缓存架构性能优化实战,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

场景1: 中小型公司Redis缓存架构以及线上问题实战

线程A在master获取锁之后,master在同步数据到slave时,master突然宕机(此时数据还没有同步到slave),然后slave会自动选举成为新的master,此时线程B获取锁,结果成功了,这样会造成多个线程获取同一把锁

解决方案

场景2: 大厂线上大规模商品缓存数据冷热分离实战

问题: 在高并发场景下,一定要把所有的缓存数据一直保存在缓存不让其失效吗?

虽然一直缓存所有数据没什么大问题,但是考虑到如果数据太多,就会一直占用缓存空间(内存资源非常宝贵),并且数据的维护性也是需要耗时的.

解决方案

流程分析

场景3: 基于DCL机制解决热点缓存并发重建问题实战

DCL(双重检测锁)

问题: 冷门数据突然变成了热门数据,大量的请求突发性的对热点数据进行缓存重建导致系统压力暴增

解决方案

public Product get(Long productId) {
    Product product = null;
    String productCacheKey = RedisKeyPrefixConst.PRODUCT_CACHE + productId;
    //DCL机制:第一次先从缓存里查数据
    product = getProductFromCache(productCacheKey);
    if (product != null) {
        return product;
    }
  
    //加分布式锁解决热点缓存并发重建问题
    RLock hotCreateCacheLock = redisson.getLock(LOCK_PRODUCT_HOT_CACHE_CREATE_PREFIX + productId);
    hotCreateCacheLock.lock();
    // 这个优化谨慎使用,防止超时导致的大规模并发重建问题
    // hotCreateCacheLock.tryLock(1, TimeUnit.SECONDS);
    try {
        //DCL机制:在分布式锁里面第二次查询
        product = getProductFromCache(productCacheKey);
        if (product != null) {
            return product;
        }

        //RLock productUpdateLock = redisson.getLock(LOCK_PRODUCT_UPDATE_PREFIX + productId);
        RReadWriteLock productUpdateLock = redisson.getReadWriteLock(LOCK_PRODUCT_UPDATE_PREFIX + productId);
        RLock rLock = productUpdateLock.readLock();
        //加分布式读锁解决缓存双写不一致问题
        rLock.lock();
        try {
            product = productDao.get(productId);
            if (product != null) {
                redisUtil.set(productCacheKey, JSON.toJSONString(product),
                        genProductCacheTimeout(), TimeUnit.SECONDS);
            } else {
                //设置空缓存解决缓存穿透问题
                redisUtil.set(productCacheKey, EMPTY_CACHE, genEmptyCacheTimeout(), TimeUnit.SECONDS);
            }
        } finally {
            rLock.unlock();
        }
    } finally {
        hotCreateCacheLock.unlock();
    }

    return product;
}

场景4: 突发性热点缓存重建导致系统压力暴增

问题: 假如当前有10w个线程没有拿到锁正在排队,这种情况只能等到获取锁的线程执行完代码释放锁后,那排队的10w个线程才能再次竞争锁。这里需要关注的问题点就是又要再次竞争锁,意味着线程竞争锁的次数可能最少>1,频繁的竞争锁对Redis性能也是有消耗的,有没有更好的办法让每个线程竞争锁的次数尽可能减少呢?

解决方案

场景5: 解决大规模缓存击穿导致线上数据库压力暴增

缓存击穿/缓存失效: 可能同一时间热点数据全部过期而造成缓存查不到数据,请求就会从数据库查询,高并发情况下会导致数据库压力

解决方案

代码案例

private Integer genProductCacheTimeout() {
  //加随机超时机制解决缓存批量失效(击穿)问题
  return PRODUCT_CACHE_TIMEOUT + new Random().nextInt(5) * 60 * 60;
}

场景6: 黑客工资导致缓存穿透线上数据库宕机

缓存穿透: 如果黑客通过脚本文件不停的传一些不存在的参数刷网站的接口,而这种垃圾参数在缓存和数据库又不存在,这样就会一直地查数据库,最终可能导致数据库并发量过大而卡死宕机。

解决方案

布隆过滤器

场景7: 大V直播带货导致线上商品系统崩溃原因分析

问题: 这种场景可能是在某个时刻把冷门商品一下子变成了热门商品。因为冷门的数据可能在缓存时间过期就删除,而此时刚好有大量请求,比如直播期间推送一个商品连接,假如同时有几十万人抢购,而缓存没有的话,意味着所有的请求全部达到了数据库中查询,而对于数据库单节点支撑并发量也就不到1w,此时这么大的请求量,肯定会把数据库整宕机(这种场景比较少,但是小概率还是会有)

解决方案

场景8: Redis分布式锁解决缓存与数据库双写不一致问题实战

解决方案

Lua脚本设置读写锁

local mode = redis.call('hget', KEYS[1], 'mode');
if (mode == false) 
then redis.call('hset', KEYS[1], 'mode', 'read'); 
redis.call('hset', KEYS[1], ARGV[2], 1); 
redis.call('set', KEYS[2] .. ':1', 1); 
redis.call('pexpire', KEYS[2] .. ':1', ARGV[1]);
redis.call('pexpire', KEYS[1], ARGV[1]); 
return nil; 
end; 
if (mode == 'read') or (mode == 'write' and redis.call('hexists', KEYS[1], ARGV[3]) == 1) 
then local ind = redis.call('hincrby', KEYS[1], ARGV[2], 1); 
local key = KEYS[2] .. ':' .. ind;
redis.call('set', key, 1); 
redis.call('pexpire', key, ARGV[1]); redis.call('pexpire', KEYS[1], ARGV[1]); 
return nil; 
end;
return redis.call('pttl', KEYS[1]);

ReadWriteLock代码案例

@Transactional
public Product update(Product product) {
  Product productResult = null;
  //RLock productUpdateLock = redisson.getLock(LOCK_PRODUCT_UPDATE_PREFIX + product.getId());
  RReadWriteLock productUpdateLock = redisson.getReadWriteLock(LOCK_PRODUCT_UPDATE_PREFIX + product.getId());
  // 添加写锁
  RLock writeLock = productUpdateLock.writeLock();
  //加分布式写锁解决缓存双写不一致问题
  writeLock.lock();
  try {
      productResult = productDao.update(product);
      redisUtil.set(RedisKeyPrefixConst.PRODUCT_CACHE + productResult.getId(), JSON.toJSONString(productResult),
      genProductCacheTimeout(), TimeUnit.SECONDS);
   } finally {
          writeLock.unlock();
   }
  return productResult;
}

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

    //从缓存里查数据
    product = getProductFromCache(productCacheKey);
    if (product != null) {
        return product;
    }

    //加分布式锁解决热点缓存并发重建问题
    RLock hotCreateCacheLock = redisson.getLock(LOCK_PRODUCT_HOT_CACHE_CREATE_PREFIX + productId);
    hotCreateCacheLock.lock();
    // 这个优化谨慎使用,防止超时导致的大规模并发重建问题
    // hotCreateCacheLock.tryLock(1, TimeUnit.SECONDS);
    try {
        product = getProductFromCache(productCacheKey);
        if (product != null) {
            return product;
        }

        //RLock productUpdateLock = redisson.getLock(LOCK_PRODUCT_UPDATE_PREFIX + productId);
        RReadWriteLock productUpdateLock = redisson.getReadWriteLock(LOCK_PRODUCT_UPDATE_PREFIX + productId);
        // 添加读锁
        RLock rLock = productUpdateLock.readLock();
        //加分布式读锁解决缓存双写不一致问题
        rLock.lock();
        try {
            product = productDao.get(productId);
            if (product != null) {
                redisUtil.set(productCacheKey, JSON.toJSONString(product),
                        genProductCacheTimeout(), TimeUnit.SECONDS);
            } else {
                //设置空缓存解决缓存穿透问题
                redisUtil.set(productCacheKey, EMPTY_CACHE, genEmptyCacheTimeout(), TimeUnit.SECONDS);
            }
        } finally {
            rLock.unlock();
        }
    } finally {
        hotCreateCacheLock.unlock();
    }

    return product;
}

场景9: 大促压力暴增导致分布式锁串行争用问题优化

解决方案

场景10: 利用多级缓存解决Redis线上集群缓存雪崩问题

缓存雪崩: 缓存支撑不住或者宕机,然后大量请求涌入数据库。

解决方案

场景11: 一次微博明显热点事件导致系统崩溃原因分析

问题: 比如微博上某一天某个明星事件成为了热点新闻,此时很多吃瓜群众全部涌入这个热点,如果并发每秒达到几十万甚至上百万的并发量,但是Redis服务器单节点只能支撑并发10w而已,那么可能因为这么高的并发量导致很多请求卡死在那,要知道我们其他业务服务也会用到Redis,一旦Redis卡死,就会影响到其他业务,导致整个业务瘫痪,这就是典型的缓存雪崩问题

解决方案: 参考场景10

场景12: 大厂对热点数据处理方案

解决方案

到此这篇关于浅谈Redis高并发缓存架构性能优化实战的文章就介绍到这了,更多相关Redis高并发缓存内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文