Redis应用

秒杀

  1. 关于 Postman 和 JMeter 的选择

    推荐选择 JMeter,因为 Postman 只能支持串行接口测试

    JMeter 推荐参考文章:

    Postman 关于能否并发:https://www.zhihu.com/question/280155125

  2. 出现的问题

    • 超时:节省每次连接 Redis 服务带来的消耗,使用连接池解决

    • 超卖:使用乐观锁,事务提交,解决超出库存的问题

    • 库存遗留问题:由于乐观锁的机制造成仍有库存遗留

      通过 LUA 脚本解决争抢问题,实际上是 Redis 利用其单线程的特性,

      用任务队列的方式解决多任务并发问题


缓存穿透

  1. 概念

    使用 Redis 大部分情况都是通过 key 查询对应的值,

    假如发送的请求传进来的 key 是不存在 Redis 中的,

    那么就查不到缓存,查不到缓存就会去数据库查询,

    假如有大量这样的请求,这些请求像“穿透”了缓存一样直接打在数据库上。

  2. 解决方案

    1. 把无效的 key 存进 Redis 中

      如果Redis查不到数据,数据库也查不到,我们把这个 key 值保存进 Redis,

      设置 value=”null”,当下次再通过这个 key 查询时就不需要再查询数据库,

      这种方式是有问题的,若不存在的 Key 值每次都是随机的,存进Redis也没有意义。

    2. 设置可访问的名单(白名单)

    3. 采用布隆过滤器(Boom Filter)

      布隆过滤器的作用是某个 key 不存在,那么就一定不存在,

      它说某个 key 存在,那么很大可能是存在(存在一定的误判率),

      于是我们可以在缓存之前再加一层布隆过滤器,

      在查询的时候先去布隆过滤器查询 key 是否存在,如果不存在就直接返回。

    4. 进行实时监控

      当发现 Redis 的命中率开始急速降低时,需要排查访问对象和访问的数据,

      以及运维人员配合,可以设置黑名单限制服务。


缓存击穿

  1. 概念

    缓存击穿是一个热点的 key ,有大并发集中对其进行访问,

    突然间这个 key 失效了,导致大并发全部打在数据库上,

    导致数据库压力剧增,这种现象就叫做缓存击穿。

  2. 解决方案

    1. 预先设置热门数据

      如果业务允许的话,对于热点的 key 可以设置永不过期,

      或者加大热门数据 key 的过期时长。

    2. 实时调整

      现场监控哪些数据热门,实时调整 key 的过期时长。

    3. 使用互斥锁

      如果缓存失效的情况,只有拿到锁才可以查询数据库,

      降低了在同一时刻打在数据库上的请求,防止数据库打死,

      当然这样会导致系统的性能变差。


缓存雪崩

  1. 概念

    当某一个时刻出现大规模的缓存失效的情况,

    那么就会导致大量的请求直接打在数据库上面,导致数据库压力巨大,

    如果在高并发的情况下,可能瞬间就会导致数据库宕机,

    这时如果马上重启数据库,马上又会有新的流量把数据库打死,这就是缓存雪崩。

  2. 解决方案

    1. 构建多级缓存架构

      nginx + redis + 其他缓存

    2. 使用锁或队列

      用加锁或队列的方式保证不会有大量的线程对数据库一次性读写,

      从而避免失效时大量的并发请求落到底层存储系统上,不适用高并发情况

    3. 设置过期标志更新缓存

      如果过期会触发通知另外的线程在后台去更新实际 key 的缓存

    4. 将缓存失效时间分散

      在失效时间上加上一个随机值,避免因为相同的过期时间导致缓存雪崩

    5. 熔断机制

      当流量到达一定的阈值时,就直接返回“系统拥挤”之类的提示,

      防止过多的请求打在数据库上,至少能保证一部分用户是可以正常使用,

      其他用户多刷新几次也能得到结果。


分布式锁

  1. 问题描述

    随着业务发展的需要,原单体单机部署的系统被演化成分布式集群系统后,

    由于分布式系统多线程、多进程并且分布在不同机器上,

    这使原单机部署情况下的并发控制锁策略失效,

    单纯的 Java API 并不能提供分布式锁的能力,为了解决这个问题,

    需要一种跨 JVM 的互斥机制来控制共享资源的访问

  2. 实现方案
    1. 基于数据库实现分布式锁
    2. 基于缓存(Redis 等)
    3. 基于 Zookeeper
  3. 使用 redis 实现分布式锁
    1. 设置锁和过期时间
    2. UUID 防止误删
    3. LUA 保证删除原子性