Redis安全
kaico2018 人气:0缓存穿透
高并发情况下查询一个不存在的key
产生的背景(原因):
缓存穿透是指使用不存在的key进行大量的高并发查询,导致缓存无法命中,每次请求都要都要穿透到后端数据库查询,使得数据库的压力非常大,甚至导致数据库服务压死;
解决方案:
- 接口层实现api限流、用户授权、id检查等;
- 从缓存和数据库都取不到数据的话,一样将数据库空值放入缓存中,设置30s有效期避免使用同一个id对数据库攻击压力大;
- 布隆过滤器
缓存击穿
高并发情况下查询的一个key突然过期
产生背景(原因):
在高并发的情况下,当一个缓存key过期时,因为访问该key请求较大,多个请求同时发现缓存过期,因此对多个请求同时数据库查询、同时向Redis写入缓存数据,这样会导致数据库的压力非常大;
解决方案:
- 使用分布式锁
保证在分布式情况下,使用分布式锁保证对于每个key同时只允许只有一个线程查询到后端服务,其他没有获取到锁的权限,只需要等待即可;这种高并发压力直接转移到分布式锁上,对分布式锁的压力非常大。
- 使用本地锁
使用本地锁与分布式锁机制一样,只不过分布式锁适应于服务集群、本地锁仅限于单个服务使用。
- 软过过期
设置热点数据永不过期或者异步延长过期时间;
缓存雪崩
高并发情况下大量的key 集中失效
产生背景(原因):
缓存雪崩指缓存服务器重启或者大量的缓存集中在某个时间段失效,突然给数据库产生了巨大的压力,甚至击垮数据库的情况。
解决思路:对不用的数据使用不同的失效时间,加上随机数
布隆过滤器
布隆过滤器适用于判断某个数据是否在集合中存在,不一定百分百准备, Bloom Filter基本实现原理采用位数组与联合函数一起实现;
布隆过滤器最大的问题:就是可能会存在一个误判的问题,如果向误判概率越低,则二进制数组会越大,同时也会非常占用空间
基于布隆过滤器解决缓存穿透问题
maven依赖
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>22.0</version> </dependency>
测试代码
public class BlongTest { /** * 在布隆中存放100万条数据 */ private static Integer size = 1000000; public static void main(String[] args) { BloomFilter<Integer> integerBloomFilter = BloomFilter.create(Funnels.integerFunnel(), size, 0.01); for (int i = 0; i < size; i++) { integerBloomFilter.put(i); } // 从布隆中查询数据是否存在 ArrayList<Integer> strings = new ArrayList<>(); for (int j = size; j < size + 10000; j++) { if (integerBloomFilter.mightContain(j)) { strings.add(j); } } System.out.println("误判数量:" + strings.size()); } }
解决缓存击穿代码
@RequestMapping("/getOrder") public OrderEntity getOrder(Integer orderId) { if (integerBloomFilter != null) { if (!integerBloomFilter.mightContain(orderId)) { System.out.println("从布隆过滤器中检测到该key不存在"); return null; } } // 1.先查询Redis中数据是否存在 OrderEntity orderRedisEntity = (OrderEntity) redisTemplateUtils.getObject(orderId + ""); if (orderRedisEntity != null) { System.out.println("直接从Redis中返回数据"); return orderRedisEntity; } // 2. 查询数据库的内容 System.out.println("从DB查询数据"); OrderEntity orderDBEntity = orderMapper.getOrderById(orderId); if (orderDBEntity != null) { System.out.println("将Db数据放入到Redis中"); redisTemplateUtils.setObject(orderId + "", orderDBEntity); } return orderDBEntity; } /** * 添加订单id到布隆过滤器中 */ @RequestMapping("/dbToBulong") public String dbToBulong() { List<Integer> orderIds = orderMapper.getOrderIds(); integerBloomFilter = BloomFilter.create(Funnels.integerFunnel(), orderIds.size(), 0.01); for (int i = 0; i < orderIds.size(); i++) { integerBloomFilter.put(orderIds.get(i)); } return "success"; }
加载全部内容