人物背景:老徐,男,本名徐福贵,从事Java相关研发工作多年,职场老油条,摸鱼小能手,虽然岁数不大但长的比较着急,人称老徐。据说之前炒某币败光了所有家产,甚至现在还有欠债。
阿珍,女,本名陈家珍,刚刚入职不久的实习生,虽然是职场菜鸟但聪明好学。据说是学校的四大校花之一,追求她的人从旺角排到了铜锣湾,不过至今还单身。
阿珍:“在高并发下遇到瓶颈的时候,经常会用到缓存来提高整个系统的性能。”
老徐:“嗯,不过缓存能够大大提升整个系统的性能,但同时也引入了更多复杂性。”
阿珍点了点头,说:“是啊,缓存穿透、缓存击穿、缓存雪崩、缓存热点这些东西,这些东西我一直分不清楚,经常混淆。”
老徐立刻自信满满地说:“这个我懂啊,你听我给你娓娓道来。”
缓存穿透
缓存穿透是指在查询缓存数据时,缓存和数据库中都没有对应数据,在缓存中找不到对应的数据,每次都要去数据库中再查询一遍,然后返回数据不存在。
在这个场景中,缓存并没有起到分担数据库访问压力的作用。读取不存在的数据的请求量一般不会太大,但如果出现一些恶意攻击,故意大量访问某些不存在的数据,就会对数据库造成很多压力。
阿珍:“太可怕了,万一遇到了这样攻击,该怎么办呀?”
老徐:“这个很好应对的,一般有两种办法。”
第一个是:如果查询数据库中的数据没有找到,则直接设置一个特定值存到缓存中。之后读取缓存时就会获取到这个特定值,直接返回空值,就不会继续访问数据库了。
第二个是:把已存在数据的key存放在布隆过滤器中。当有新的请求时,先到布隆过滤器中查询是否存在,如果不存在该条数据直接返回;如果存在该条数据再查询缓存查询数据库。
缓存击穿
缓存击穿是指在查询缓存数据时,数据库原本有得数据,但是缓存中没有,生成缓存数据需要耗费较长时间或者大量资源,这时候如果有大量请求该数据,会对数据库甚至系统造成较大压力。
阿珍:“哦?该怎么解决呀?”
老徐:“这个很好解决,一般有两个做法。”
第一个是:对缓存更新操作加入锁的保护,保证只有一个线程能够进行缓存更新的操作,没有获取更新锁的线程要么等待锁释放后重新读取缓存,要么直接返回空值或者默认值。
第二个是:后台作业定时更新缓存,而不是在访问页面时生成缓存数据。这样可以按照一定策略定时更新缓存,不会对存储系统较大的瞬时压力。
缓存雪崩
缓存雪崩是指当大量缓存同时失效或过期后,大量请求直接访问对数据库,甚至耗费较长时间或者大量资源计算缓存结果,引起系统性能的急剧下降。
阿珍抢先说道:“这个我知道怎么解决!”老徐反问:“怎么解决?”
阿珍回答:“同一类型的缓存的过期时间可以设置一个随机值,比如:原来的过期时间是5分钟,在此基础上加0~60秒,那么过期时间就变为在5~6分钟内波动,有效防止都在同一个时间点上大量缓存过期。”
缓存热点
缓存热点是指大部分甚至所有的业务请求都命中同一份缓存数据。
虽然缓存本身的性能比较高,但对于一些特别热点的数据,如果大部分甚至所有的请求都命中同一份缓存数据,则这份数据所在的缓存服务器的压力也会很大。比如,电商的爆品秒杀活动,短时间内被上千万的用户访问。
阿珍:“遇到了这种情况,该怎么办呀?”
老徐:“这个很好解决的,一般有两种办法:复制多份缓存副本和本地内存缓存。”
复制多份缓存副本,就是将请求分散到多个缓存服务器上,减轻缓存热点导致的单台缓存服务器压力。在设计缓存副本的时候,有一个细节需要注意:不同的缓存副本不要设置统一的过期时间,否则就会出现所有缓存副本同时生成同时失效的情况,从而引发缓存的雪崩效应。
把热点数据缓存在客户端的本地内存中,并且设置一个失效时间。对于每次读请求,将首先检查该数据是否存在于本地缓存中,如果存在则直接返回,如果不存在再去访问分布式缓存的服务器。
阿珍用崇拜的眼神看着老徐,说:“老徐,你太牛了,什么都懂!”
老徐不好意思地挠了挠头,说:“也没有了。”
最后,谢谢你这么帅,还给我 点赞和 关注。