缓存穿透
缓存击穿
缓存雪崩
缓存穿透,它就是指当用户在查询一条数据的时候,而此时数据库和缓存却没有关于这条数据的任何记录,而这条数据在缓存中没找到就会向数据库请求获取数据。它拿不到数据时,就会一直查询数据库,这样会对数据库的访问造成很大的压力。
举个栗子:用户查询一个 id = -1 的商品信息,一般数据库 id 值都是从 1 开始自增,很明显这条信息是不在数据库中,当没有信息返回时,会一直向数据库查询,给当前数据库的造成很大的访问压力。
该如何解决这个问题呢?
缓存空对象(代码维护简单,但是效果不是很好)。
布隆过滤器(代码维护比较复杂,效果挺好的)。
缓存空对象它就是指一个请求发送过来,如果此时缓存中和数据库都不存在这个请求所要查询的相关信息,那么数据库就会返回一个空对象,并将这个空对象和请求关联起来存到缓存中,当下次还是这个请求过来的时候,这时缓存就会命中,就直接从缓存中返回这个空对象,这样可以减少访问数据库的压力,提高当前数据库的访问性能。
如果大量不存在的请求过来,那么这时候缓存岂不是会缓存许多空对象了吗~~~,如果时间一长这样会导致缓存中存在大量空对象,这样不仅会占用许多的内存空间,还会浪费许多资源呀。该如何解决呢?
设置一个过期时间
setex key seconds valule:设置键值对的同时指定过期时间(s)
1.定义
内部是使用bit数组的形式来进行key值的映射存储,使用hash进行存入的key的运算,得到bit数组的下标,然后将其下标对应的值改为1,在判断某个key存在不存在时,需要先对key进行hash运算,找到对应的下标,下标对应值为1,则存在,为0则不存在。
2.误判
因为hash运算的结果会存在hash碰撞,导致运算后对应的bit数组的下标可能存在相同,所以会存在误判的现象但是,误判只会发生在,不存在的可能会误判为存在,存在不会误判为不存在
3.精确度
和hash函数的个数以及数组的长度有关,
bit数组的长度越长,误判率就越低,
hash运算的个数越多,效率就越低,
hash运算的个数越少,误判率就越高,
因此在使用时要从两者综合考虑。
布隆过滤器,默认错误率是 0.01 ,默认初始容量是 100,可以在初始启动的时候通过启动参数来进行配置
./redis-server ./redis.conf --loadmodule ./redisbloom.so INITIAL_SIZE 400 ERROR_RATE 0.004
4.删除
因为可能存在hash碰撞,如果可以删除,则可能会导致删除的不是对应的数据,所以不存在删除
缓存击穿是指有某个key经常被查询,经常被用户特殊关怀,用户非常 love 它,也就类比“熟客” 或者 一个key经常不被访问。但是这时候,如果这个key在缓存的过期时间失效的时候或者这是个冷门key时,这时候突然有大量有关这个key的访问请求,这样会导致大并发请求直接穿透缓存,请求数据库,瞬间对数据库的访问压力增大。
归纳起来:造成缓存击穿的原因有两个。
(1)一个“冷门”key,突然被大量用户请求访问。
(2)一个“热门”key,在缓存中时间恰好过期,这时有大量用户来进行访问。
加锁。对于key过期的时候,在key要查询数据库的时候加上一把锁,这时只能让第一个请求进行查询数据库,然后把从数据库中查询到的值存储到缓存中,对于剩下的相同的key,则可以直接从缓存中获取结果。
单机环境下:直接使用常用的锁即可(如:Lock、Synchronized等),分布式环境下可以使用分布式锁,如:基于数据库、基于Redis或者zookeeper 的分布式锁。
缓存雪崩是指在某一个时间段内,缓存集中过期失效,如果这个时间段内有大量请求,而查询数据量巨大,所有的请求都会达到存储层,存储层的调用量会暴增,引起数据库压力过大甚至宕机。
举个栗子:我们基本上都经历过购物狂欢节,假设商家举办 23:00 - 24:00 商品打骨折促销活动。程序在设计的时候,在 23:00 把商家打骨折的商品放到缓存中,并通过redis的expire设置了过期时间为1小时。这个时间段许多用户访问这些商品信息、购买等等。但是刚好到了24:00点的时候,恰好还有许多用户在访问这些商品,这时候对这些商品的访问都会落到数据库上,导致数据库要抗住巨大的压力,稍有不慎会导致,数据库直接宕机。
redis有可能挂掉,搭建redis集群(一主多从或者多主多从),这样一台挂掉之后其他的还可以继续工作。
缓存失效后,通过加锁或者队列来控制读取数据库的线程数量,对某个key只允许一个线程查询数据和写缓存,其他线程等待。
数据预热,就是在正式部署之前,先把可能使用的数据预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。
均匀的设置key的过期时间,尽量避免大量的key在同一时间点过期。