因为一次redis缓存穿透,全面探究redis能做什么

问题背景:
网关支付 , 支付之前需要根据商户去获取支持的银行列表 , 银行列表是商家首次在我们系统里申请开户的时候配置进去的 , 第一次获取列表是通过调远程接口查询数据库获取的 , 获取成功后 , 结果保存在redis缓存中 , 有效期设置6小时 , 以后每次查询都直接访问redis缓存即可 。
出现问题是有一批新商户没有配置银行列表 , 发起支付获取银行列表时 , 首次调用接口获取结果失败 , 没有放置缓存;商户反复提交了4次 , 每次访问缓存不存在 , 然后远程调用接口服务 , 接口返回同样不存在的错误 , 导致第5次远程调用返回500错误 。
这是典型的缓存穿透问题 。 缓存穿透是指用户不断发起请求缓存和数据库中都没有的数据 , 这时的用户很可能是攻击者 , 攻击会导致数据库压力过大 。
解决办法:
从缓存和数据库中都取不到的值 , 这时可以将key-value键值对写为key-null , 缓存有效时间可以设置短点 , 如30秒(设置太长会导致正常情况也没法使用) 。 这样至少30秒内访问redis能获取到空值 , 可以防止用户反复用同一个id暴力攻击数据库 。
缓存除了穿透问题还有击穿和雪崩问题 。 1)、缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期) , 这时由于并发用户特别多 , 同时读一个热点key , 而这个key缓存没读到数据 , 又同时去数据库取数据 , 引起数据库压力瞬间增大 。
最好的例子就是爆款微博热搜 , 大家同时去访问 , 造成的微博瘫痪 。
解决办法:

  1. 设置热点数据永远不过期 。
  2. 加互斥锁 , 用到redis的分布式锁 , 即setnx方法 , 保证对于每个key同时只有一个线程去查询后端服务 , 其他线程没有获得分布式锁的情况下 , 只需要阻塞等待即可 。
2)、缓存雪崩是指缓存中数据大批量到了过期时间 , 而查询数据量巨大 , 引起数据库压力过大甚至宕机 。 和缓存击穿不同的是 , 缓存击穿指并发查同一条数据 , 缓存雪崩是不同数据都过期了 , 很多数据都查不到从而查数据库 。
举例比如双11 , 所有商品都放入缓存 , 设置过期时间为1小时 , 到了凌晨1点 , 商品信息集体失效 , 此刻所有的查询会全部落到数据库上 。
解决方案:
  1. 数据预热 , 在即将发生大并发访问之前 , 手动触发加载缓存不同的key , 过期时间随机设置 , 让缓存失效时间点尽量均匀 , 防止同一时间大量数据过期现象发生 。
  2. 如果缓存数据库是分布式部署 , 将热点数据均匀分布在不同的缓存数据库中 。
  3. 设置热点数据永远不过期 。
至此 , 步入正题 , 全面了解一下redis都可以做什么事?redis的中文网站(对redis)的介绍如下:
Redis 是一个开源(BSD许可)的 , 内存中的数据结构存储系统 , 它可以用作数据库、缓存和消息中间件 。它支持多种类型的数据结构 , 如 字符串(strings) ,散列(hashes) ,列表(lists) ,集合(sets) ,有序集合(sorted sets) 与范围查询 ,bitmaps ,hyperloglogs 和地理空间(geospatial) 索引半径查询 。Redis 内置了 复制(replication) , LUA脚本(Lua scripting) ,LRU驱动事件(LRU eviction) , 事务(transactions) 和不同级别的 磁盘持久化(persistence) ,并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability) 。
和Memcached类似 , 它支持存储的value类型相对更多 , 这些类型大多数支持add/remove、list支持push/pop、set支持取交集并集和差集 , 而且这些操作都是原子性的 。 在此基础上 , zset支持各种不同方式的排序 。 与memcached一样 , 为了保证效率 , 数据都是缓存在内存中 。 区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件 , 实现数据持久化 , 并且在此基础上实现了master-slave(主从)同步 。


推荐阅读