前台请求,后台先从缓存中取数据,取到直接返回结果,取不到时从数据库中取,数据库取到更新缓存,并返回结果,数据库也没取到,那直接返回空结果。
一:缓存穿透描述:
缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。
一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,就应该去后端系统查找(比如DB).
一些恶意的请求会故意查询不存在的key,请求量很大,就会对后端系统造成很大的压力.这就叫做缓存穿透.
解决方案1.对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者将查询的这个key对应的数据insert之后清理缓存
2.对一定不存在的key进行过滤.可以把所有的可能存在的key放到一个大的Bitmap中,查询时通过这个Bitmap过滤.
二:缓存击穿描述:
缓存击穿是指缓存中没有但数据库中有的数据 (一般是缓存时间到期) ,这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力 。
解决方案1.设置热点数据永远不过期。
2.加互斥锁,互斥锁参考代码如下
public static String getData(String key) throws InterruptedException
{
//从缓存读取数据
String result = getDataFromRedis(key);
//缓存中不存在数据
if(result == null)
{
//去获取锁,获取成功,去数据库获取数据
if(reenlock.tryLock())
{
//从数据库中获取数据
result = getDotaFromMlysql(key);
//更新缓存数据
if(result != null)
{
setDataToCache(key,result);
}
//释放锁
reenLock.unlock();
}
//获取失败
else
{
//暂停100ms再重新去获取数据
Thread.sleep(100);
result = getData(key);
}
}
return result;
}
说明:
1)缓存中有数据,直接走上述代码13行后就返回结果了
2)缓存中没有数据,第1个进入的线程,获取锁并从数据库去取数据,没释放锁之前,其他并行进入的线程会等待100ms,再重新去缓存取数据。这样就防止都去数据库重复取数据,重复往缓存中更新数据情况出现。
3)当然这是简化处理,理论上如果能根据key值加锁就更好了,就是线程A从数据库取key1的数据并不妨碍线程B取key2的数据,上面代码明显做不到这点。
三:缓存雪崩描述:
缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机,当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,会给后端系统带来很大压力.导致系统崩溃.。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
解决方案1.在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量.比如对某个key只允许一个线程查询数据和写缓存,其他线程等待.
2.做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期.
3.不同的key设置不同的过期时间,让缓存失效的时间点尽量均匀.



