游戏开发工具

缓存击穿(高并发查询某数据,且缓存过期)

概述

指一个非常热点的key,在不停的高并发请求着,那么当这个key在缓存中失效的一瞬间,持续对这个key的高并发就击穿了缓存,直接请求到了数据库,就像在一个屏障上早开了一个洞。

当热点key过期失效的一瞬间,高并发突然融入,会对数据库突然造成巨大的压力,严重的情况甚至会造成数据库宕机。


解决方案

方案一:设置热点数据永不过期

从缓存层面来看,没有设置过期时间,所以不会出现热点key过期后所产生的缓存击穿问题。

1、简单的SET方法:简单的SET是最容易实现的,如下,在Redis中创建一个永不过期的键值对:

SET 'key' 'value'


2、使用SETEX方法:以下是使用Setex方法在Redis中创建一个永不过期的数据:

SETEX 'key' 0 'value'

上述Setex命令将一个值设置到指定的key,并将过期时间设为0,这样即可永不过期。


3、使用Setnx方法:如果你要设置key的永久过期时间,那么可以使用Setnx方法。此方法只有在给定的key不存在时才能成功,如下:

SETNX 'key' 'value'
EXPIRE 'key' 0

上述Setnx 和 EXPIRE命令将一个值设置到指定的key,并将过期时间设为0,这样即可永不过期。



方案二:加互斥锁

使用分布式锁,当缓存数据过期后,保证对每个热点key同时只有一个线程去查询后端服务,并将热点数据添加到缓存。

1.jpg

互斥锁的实现

在redis中插入一条key = lockKey的数据,如果插入成功说明获取到互斥锁,如果插入失败,说明未获取到互斥锁。


Bug现象

第一次查询时可以很流畅的获取到数据,但是第二次查询时请求等不到响应。


原因

使用redis中String结构作为互斥锁,如过lockKey存在,则获取锁失败,否则获取到锁。

redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 10L, TimeUnit.MINUTES)

在redis中缓存商铺信息的key为shopKey,在查询完成释放锁时,本应为 redisTemplate.delete(lockKey)。但是错误的写为redisTemplate.delete(shopKey),导致redis中的shopKey被删除,并且lockKey没有被释放。