【JAVA今法修真】 第四章 redis特性 击穿雪崩!

2022-03-07 13:56:09 浏览数 (1)

“明日便是决赛了,咋只会用法器没练过法术呢。”。

选手休息室内,一脸嫉妒的陈彦宇盯着正在擦拭(炫耀)法器的李小庚,揶揄道。

陈彦宇,通辽州可汗市人,三年前突破筑基期,在去往天道剑宗的路上被凶人掳走,待醒来时已经出现在了万法仙门里。当时身边站着一个声音像极了绑架自己的凶人的人,他自称是万法仙门教研室主任。据那人描述当时正在行侠仗义,发现陈彦宇被邪恶的天道剑宗的邪修抓住,便怒从心生,与其大战了三百回合,最后解救下自己,同时并且为了补偿自己便强行将自己收入门下。

“你知道人与动物最大的区别是什么吗?”李小庚没有理会这个未来手下败将的挑衅。

“你知道作为数据库类型的法器,最害怕的是什么吗?”陈彦宇见李小庚如此目中无人,没有再在休息室待下去,转头走向擂台:“希望你回去不要被你师父打屁股。”

“额,也不是不行。”

“?”


决赛现场,因为是筑基期比赛,所以来的观众并不多,主要分为三部分:李小庚的迷妹和少量迷弟,之前的陈彦宇和李小庚的手下败将,还有级别不够无法观看结丹期比赛的炼气期弟子。

“李小庚师兄好帅!李小庚师兄必胜!”

“哼,要不是我当初棋差一招,今天台上的两人之中必有我。”

“解说呢?解说快点就位,马上开始了!”

作为传统老牌强派,万法仙门门派大比的基本秩序还是有的,至少不会出现什么卖瓜子花生潜水艇之类的流动小贩。

擂台上。

李小庚还是那一袭白衣(云霄殿专属校服),配上背后若隐若现的如羽毛般的红白飞剑,如同天使降临,一瞥一笑皆是画卷。

陈彦宇那边则显得普通很多,普通的万法仙门弟子服装加上手上普通的小盒子。

望着擂台下最大的组织“李小庚后援会”,陈彦宇心中暗暗想到:“三十年河东,三十年河西,平日里只教你李小庚放火,今日我陈彦宇也要点灯!”

“李小庚,你可知JMeter?”陈彦宇举起手中的小盒子,活脱脱就像银角大王拿着葫芦。

“知道怎样,不知又怎样?”李小庚撇了撇灰蒙蒙的盒子,又看了看自己背后的天使之翼,笑麻了。

“好!”陈彦宇被李小庚的嚣张给震惊到了,不气反笑:“我今日便好好给你讲讲什么是JMeter。”

JMeter忽然自己漂浮在空中,陈彦宇则像是在进行钢琴演奏一般,双手在上面挥舞出了残影,无数只有修真人士才能看见的电磁波从盒子上向李小庚激荡而出。

代码语言:javascript复制
jmeter -n -t testplan/RedisLock.jmx -l testplan/result/result.txt -e -o testplan/webreport

“不知道你用了那么久的数据库类法器,知不知道什么叫击穿、雪崩和穿透!”陈彦宇仿佛已经看见了自己的胜利。

作为一个正常的筑基期弟子,参加普通的门派大比,一路打败各殿优秀弟子终于走到了决赛,却发现决赛的对手不仅帅的惨绝人寰,还从一开始就拿出了很多金丹真人都没有的大杀器redis集群,对于任何人来说都是崩溃的。不过,针对这种数据库类型的法器,陈彦宇还是有过了解的,只需要通过大量的数据击穿对方一点,便能够让其超载重启。那个时候,这个自己和小白脸就可以正面对决了,甚至法器超载还会影响对方的处理能力。

“看来你对redis的了解实在是太少了。”李小庚甚至直接背对着陈彦宇,背后红白小剑组成的翅膀无风自动,完全无视了陈彦宇的攻击。

一、redis解决缓存雪崩、击穿、穿透问题

1、1 缓存雪崩

顾名思义,大家应该都见过雪崩,无论是现实中还是网络上。那场景,颇有种天崩地裂的感觉,而对于数据库来说,缓存雪崩,也说得上是一种天崩地裂了。 同一时间Redis缓存大面积失效,那一瞬间Redis跟不存在一样,这个时候数据直接请求到数据库。你想想,缓存的意义就是减少DB,如果缓存没有了,大量的请求还不直接打爆数据库? 缓存雪崩如何出现的?

  • 大量的缓存同时失效,可能是同时间生成,同时间到期
  • 缓存同时被删除
  • 缓存层出现了错误,不能正常工作了

解决办法:

  • 1、批量往Redis存数据的时候,把每个KEY失效时间都增加随机值,保证不会同时失效
  • 2、设置热点数据永不过期,有更新操作就更新缓存(但是这个方法不好,永不过期导致缓存大量堆积,很多缓存不一定有用)
  • 3、Redis集群部署,将热点数据均匀分布在不同Redis库中也能避免全部失效,避免了Redis出现问题导致缓存雪崩

1、2缓存击穿

有一个Key非常热点,在不停扛着大并发,大并发集中对这一点进行访问,当这个Key失效的瞬间、大量并发击穿缓存,直接访问数据库。 其实缓存击穿,真的算不上什么特别大的问题,毕竟不是每个公司都在同一个Key上都有那么大的热点,只需要设置好过期时间,稳定好Redis集群,缓存击穿不难避免。 解决办法:

  • 1、设置热点数据永不过期
  • 2、增加互斥锁【简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存】

在我的经验来看,设置互斥锁显然没有必要,一个热点永不过期就能解决的问题,为什么还要用到锁?这不是平白增加复杂度吗?也许在特殊场景能看到,但是对于我这个小白来说,仅仅能在各位大牛的博客里看到这个观点。

1、3缓存穿透

从名字上来看,缓存击穿和缓存穿透很像,实际上页比较像,但是既然区分了出来,自然有一些不同的地方 缓存穿透的概念很简单,用户想要查询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中,于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透。 但是,缓存穿透真正要防止的是黑客。 如果一个黑客每次故意查询一个在缓存内必然不存在的数据,导致每次请求都要去存储层去查询,这样缓存就失去了意义。如果在大流量下数据库可能挂掉,这也是缓存击穿。 解决办法:

  • 1、增加参数校验
  • 2、从网关层Nginx增加配置项,对单个IP每秒访问次数超出阈值的拉黑处理
  • 3、Bloom Filter 能很好地防止缓存穿透、他的原理也很简单的就是利用高效的数据结构和算法快速判断出你这个Key是否在数据库,不存在直接return、存在就直接去DB刷新KV再return

redis利用布隆过滤器来防止缓存击穿,主要是通过将已存在的缓存放到布隆过滤器中,当黑客访问不存在的缓存时迅速返回避免缓存及DB挂掉。

1、4布隆过滤器

布隆过滤器本质上布隆过滤器是一种数据结构,比较巧妙的概率型数据结构(probabilistic data structure),特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”。 布隆过滤器是由一个很长的bit数组和一系列哈希函数组成的。 数组的每个元素都只占1bit空间,并且每个元素只能为0或1。 布隆过滤器拥有k个哈希函数,当一个元素加入布隆过滤器时,会使用k个哈希函数对其进行k次计算,得到k个哈希值,并且根据得到的哈希值,在位数组中把对应下标的值置位1。 判断某个数是否在布隆过滤器中,就对该元素进行k次哈希计算,得到的值在位数组中判断每个元素是否都为1,如果每个元素都为1,就说明这个值在布隆过滤器中。 当插入的元素越来越多时,当一个不在布隆过滤器中的元素,经过同样规则的哈希计算之后,得到的值在位数组中查询,有可能这些位置因为其他的元素先被置1了。 所以布隆过滤器存在误判的情况,但是如果布隆过滤器判断某个元素不在布隆过滤器中,那么这个值就一定不在。 让我们继续深入

1、5布谷鸟过滤器

为了解决布隆过滤器不能删除元素的问题, 论文《Cuckoo Filter:Better Than Bloom》作者提出了布谷鸟过滤器。相比布隆过滤器,布谷鸟过滤器有以下几点:查询性能更强、空间利用效率更高、支持反向操作(删除)以及计数。 布谷鸟过滤器名称源于采取了一种和布谷鸟一样的养娃方法,最原始的布谷鸟哈希方法是使用两个哈希函数对一个key进行哈希,得到桶中的两个位置。

  • 如果两个位置都为为空则将key随机存入其中一个位置
  • 如果只有一个位置为空则存入为空的位置
  • 如果都不为空,则随机踢出一个元素,踢出的元素再重新计算哈希找到相应的位置

布谷鸟过滤器源于布谷鸟哈希算法,他巧妙的设计了一个独特的 hash 函数,使得可以根据 p1 和 元素指纹 直接计算出 p2,而不需要完整的 x 元素。 从下面的公式中可以看出,当我们知道 fp 和 p1,就可以直接算出 p2。同样如果我们知道 p2 和 fp,也可以直接算出 p1。 fp = fingerprint(x) p1 = hash(x) p2 = p1 ^ hash(fp) // 异或 所以我们根本不需要知道当前的位置是 p1 还是 p2,只需要将当前的位置和 hash(fp) 进行异或计算就可以得到对偶位置。而且只需要确保 hash(fp) != 0 就可以确保 p1 != p2,如此就不会出现自己踢自己导致死循环的问题。

听完李小庚的介绍后,陈彦宇突然哑然失笑:“原来如此,我以为我的JMeter可以克制数据库,所以大数据攻击可以击穿你的数据库,没想到小丑竟是我自己。”

“其实你不必懊恼,你的想法是没错的,克制数据库最好的武器就是大量的数据,只是要击穿我整个redis集群,你这单台服务器运行的JMeter还是不够看。”陈彦宇安慰道,作为胜利者自然应该有着胜利者的体面。

“即使我有足够的JMeter服务器,你肯定还有后手对吧。”陈彦宇不甘心的问。

“bingo!”李小庚一步一步接近被redis集群锁住气息的陈彦宇,轻轻取下失去主人法力支持只能安静漂浮在空中的盒子:“既然Redis能储存数据,自然也能实时的删除多余的数据。”

二、内存淘汰机制

既然Redis能储存数据,自然也就需要能删除多余的数据,不然,空间都被占满了,新的内容放在哪里?聪明的程序员提出了两个个办法解决Redis的内存问题。

  • 1、定时删除——通过定时器删除过期的数据
  • 2、惰性删除——查询后如果过期就不返回、过期则删除。这种情况容易出现很多冗余数据导致占用大量空间

2、1、定时删除

创建一个定时器,当key设置有过期时间,且过期时间到达时,由定时器任务立即执行对键的删除操作,默认100ms就随机抽一些key判断是否过期,过期的话就删除,用处理器性能换取存储空间(拿时间换空间)

  • 优点:节约内存,到时就删除,快速释放掉不必要的内存占用
  • 缺点:CPU压力很大,无论CPU此时负载量多高,均占用CPU,会影响redis服务器响应时间和指令吞吐量

2、2惰性删除

当Redis中的数据到了过期时间,我们先不做处理。等下次访问该数据时进行一次判断,如果未过期,就正常返回,如果发现数据已过期,立刻删除,然后返回不存在,用存储空间换取处理器性能(拿空间换时间)

  • 优点:节约CPU性能,发现必须删除的时候才删除
  • 缺点:内存压力很大,出现长期占用内存的数据

不知道大家不知道发现了没有,大部分的算法,不是时间换空间,就是空间换时间。刚刚发现这个秘密的我简直惊呆了,这就是人类的终极奥秘之一了,只要我们知道这个诀窍,就能解决大部分的问题。

2、3淘汰机制

在Redis的redis.config文件中还可通过搜索maxmemory-policy来设置淘汰机制 noeviction:当内存使用达到阈值的时候,所有引起申请内存的命令会报错。 allkeys-lru:在主键空间中,优先移除最近未使用的key。 volatile-lru:在设置了过期时间的键空间中,优先移除最近未使用的key。 allkeys-random:在主键空间中,随机移除某个key。 volatile-random:在设置了过期时间的键空间中,随机移除某个key。 volatile-ttl:在设置了过期时间的键空间中,具有更早过期时间的key优先移除。

“你练习过线程池神功,所以你也知道,一个适合的淘汰策略能解决许多的问题,而对于redis也一样,我只需要适当地修改参数,让最近存储的数据都快速淘汰掉,便能防止内存压力过大。”李小庚一个响指解除了redis集群对陈彦宇的锁定,又将JMeter放到他的手中:“说到底,硬件差距太大,再好的策略也没有用。”

“你要做甚?”陈彦宇很不解的看着李小庚的行为:“你是在羞辱我吗?”

“你不是要和我正面对决吗?我给你这个机会!”说罢,redis集群像一群小蜜蜂一样,回到了李长庚的空间戒指内。

陈彦宇已经被李小庚的才学与气度所折服,本来想就此认输,但是为了给对方留下一个好印象,便最大程度运起《Java真经》,全力以赴道:“长庚师兄,虽然我知道无论是否使用法器,你的修为都在我之上,但是你也要小心哦,我不会差你太多的!”

(注:在门派同届内修为更高者为师兄)

您好,我是南橘,万法仙门的掌门,刚刚从九州世界穿越到地球,因为时空乱流的影响导致我的法力全失,现在不得不通过这个平台向广大修真天才们借去力量。 等我回去以后,大家都是万法仙门的长老,我会给大家数不尽的天材地宝,人人如龙,全民飞升。

0 人点赞