1.Redis

Redis

1. redis 簡(jiǎn)介

redis 的數(shù)據(jù)是存在內(nèi)存中的,所以讀寫(xiě)速度非陈蠛洌快弓候,因此 redis 被廣泛應(yīng)用于緩存方向休玩。另外,redis 也經(jīng)常用來(lái)做分布式鎖片择。redis 提供了多種數(shù)據(jù)類(lèi)型來(lái)支持不同的業(yè)務(wù)場(chǎng)景潜的。除此之外,redis 支持事務(wù) 字管、持久化啰挪、LUA腳本信不、LRU驅(qū)動(dòng)事件、多種集群方案抽活。

2. 為什么要用 redis、為什么要用緩存

主要從“高性能”和“高并發(fā)”這兩點(diǎn)來(lái)看待這個(gè)問(wèn)題下硕。

  1. 高性能

    假如用戶第一次訪問(wèn)數(shù)據(jù)庫(kù)中的某些數(shù)據(jù)汁胆。這個(gè)過(guò)程會(huì)比較慢,因?yàn)槭菑挠脖P(pán)上讀取的嫩码。將該用戶訪問(wèn)的數(shù)據(jù)存在緩存中,這樣下一次再訪問(wèn)這些數(shù)據(jù)的時(shí)候就可以直接從緩存中獲取了谢谦。操作緩存就是直接操作內(nèi)存萝衩,所以速度相當(dāng)快。如果數(shù)據(jù)庫(kù)中的對(duì)應(yīng)數(shù)據(jù)改變之后猩谊,同步改變緩存中相應(yīng)的數(shù)據(jù)即可。

  2. 高并發(fā)

    直接操作緩存能夠承受的請(qǐng)求是遠(yuǎn)遠(yuǎn)大于直接訪問(wèn)數(shù)據(jù)庫(kù)的墙牌,所以我們可以考慮把數(shù)據(jù)庫(kù)中的部分?jǐn)?shù)據(jù)轉(zhuǎn)移到緩存中去,這樣用戶的一部分請(qǐng)求會(huì)直接到緩存這里而不用經(jīng)過(guò)數(shù)據(jù)庫(kù)喜滨。

3. 為什么要用 redis 而不用 map/guava 做緩存

緩存分為本地緩存和分布式緩存撤防。

以 Java 為例虽风,使用自帶的 map 或者 guava 實(shí)現(xiàn)的是本地緩存,最主要的特點(diǎn)是輕量以及快速寄月,生命周期隨著 jvm 的銷(xiāo)毀而結(jié)束辜膝,并且在多實(shí)例的情況下,每個(gè)實(shí)例都需要各自保存一份緩存漾肮,緩存不具有一致性厂抖。

使用 redis 或 memcached 之類(lèi)的稱(chēng)為分布式緩存,在多實(shí)例的情況下克懊,各實(shí)例共用一份緩存數(shù)據(jù)忱辅,緩存具有一致性七蜘。缺點(diǎn)是需要保持 redis 或 memcached服務(wù)的高可用,整個(gè)程序架構(gòu)上較為復(fù)雜墙懂。

4. redis 的單線程模型

redis 內(nèi)部使用文件事件處理器 file event handler崔梗,這個(gè)文件事件處理器是單線程的,所以 redis 才叫做單線程的模型垒在。它采用 IO 多路復(fù)用機(jī)制同時(shí)監(jiān)聽(tīng)多個(gè) socket蒜魄,根據(jù) socket 上的事件來(lái)選擇對(duì)應(yīng)的事件處理器進(jìn)行處理。

文件事件處理器的結(jié)構(gòu)包含 4 個(gè)部分:

  • 多個(gè) socket
  • IO 多路復(fù)用程序
  • 文件事件分派器
  • 事件處理器(連接應(yīng)答處理器场躯、命令請(qǐng)求處理器谈为、命令回復(fù)處理器)

多個(gè) socket 可能會(huì)并發(fā)產(chǎn)生不同的操作,每個(gè)操作對(duì)應(yīng)不同的文件事件踢关,但是 IO 多路復(fù)用程序會(huì)監(jiān)聽(tīng)多個(gè) socket伞鲫,會(huì)將 socket 產(chǎn)生的事件放入隊(duì)列中排隊(duì),事件分派器每次從隊(duì)列中取出一個(gè)事件签舞,把該事件交給對(duì)應(yīng)的事件處理器進(jìn)行處理秕脓。

Redis客戶端對(duì)服務(wù)端的每次調(diào)用都經(jīng)歷了發(fā)送命令,執(zhí)行命令儒搭,返回結(jié)果三個(gè)過(guò)程吠架。其中執(zhí)行命令階段,所有到達(dá)服務(wù)端的命令不會(huì)立刻執(zhí)行傍药,而是會(huì)進(jìn)入一個(gè)隊(duì)列拐辽,然后逐個(gè)被執(zhí)行俱诸,多個(gè)客戶端發(fā)送的命令的執(zhí)行順序是不確定的睁搭。但是不會(huì)有兩條命令被同時(shí)執(zhí)行介袜,不會(huì)產(chǎn)生并發(fā)問(wèn)題遇伞。

為啥redis 單線程模型也能效率這么高鸠珠?

  1. 純內(nèi)存操作。Redis將所有數(shù)據(jù)放在內(nèi)存中炬太,內(nèi)存的響應(yīng)時(shí)長(zhǎng)大約為100納秒亲族,這是Redis達(dá)到每秒萬(wàn)級(jí)別訪問(wèn)的重要基礎(chǔ)霎迫。
  2. 基于非阻塞的 IO 多路復(fù)用機(jī)制知给。Redis使用epoll作為I/O多路復(fù)用技術(shù)的實(shí)現(xiàn)涩赢,再加上Redis自身的事件處理模型將epoll中的連接筒扒、讀寫(xiě)霎肯、關(guān)閉都轉(zhuǎn)換為事件,不在網(wǎng)絡(luò)I/O上浪費(fèi)過(guò)多的時(shí)間驮俗。
  3. 單線程可以簡(jiǎn)化數(shù)據(jù)結(jié)構(gòu)王凑、算法的實(shí)現(xiàn)索烹,單線程避免了線程切換和競(jìng)態(tài)產(chǎn)生(避免了加鎖百姓、解鎖)的消耗垒拢。

5. redis 和 memcached 的區(qū)別

  1. Redis支持更豐富的數(shù)據(jù)類(lèi)型(支持更復(fù)雜的應(yīng)用場(chǎng)景):Redis不僅僅支持簡(jiǎn)單的k/v類(lèi)型的數(shù)據(jù),同時(shí)還提供list奔垦,set椿猎,zset鸵贬,hash等數(shù)據(jù)結(jié)構(gòu)的存儲(chǔ)阔逼。memcache支持簡(jiǎn)單的數(shù)據(jù)類(lèi)型嗜浮,String危融。
  2. Redis支持?jǐn)?shù)據(jù)的持久化吉殃,可以將內(nèi)存中的數(shù)據(jù)保持在磁盤(pán)中蛋勺,重啟時(shí)可以再次加載進(jìn)行使用抱完,而Memecache把數(shù)據(jù)全部存在內(nèi)存之中。
  3. 集群模式:memcached沒(méi)有原生的集群模式烘贴,需要依靠客戶端來(lái)實(shí)現(xiàn)往集群中分片寫(xiě)入數(shù)據(jù);但是 redis 目前是原生支持 cluster 模式的.
  4. Memcached是多線程,非阻塞IO復(fù)用的網(wǎng)絡(luò)模型;Redis使用單線程的多路 IO 復(fù)用模型捺疼。

6. redis 常見(jiàn)數(shù)據(jù)結(jié)構(gòu)以及使用場(chǎng)景分析

  1. String

    常用命令:set, get, decr, incr, mget 等。

    String數(shù)據(jù)結(jié)構(gòu)是簡(jiǎn)單的key-value類(lèi)型官扣,value其實(shí)不僅可以是String惕蹄,也可以是數(shù)字卖陵。

    常規(guī)key-value緩存應(yīng)用:常規(guī)計(jì)數(shù)泪蔫,如微博數(shù)撩荣,粉絲數(shù)等餐曹。

  2. Hash

    常用命令:hget, hset, hgetall 等凸主。

    hash 是一個(gè) string 類(lèi)型的 field 和 value 的映射表,hash 特別適合用于存儲(chǔ)對(duì)象锋华,后續(xù)操作的時(shí)候毯焕,可以直接僅僅修改這個(gè)對(duì)象中的某個(gè)字段的值婆咸。如我們可以用 hash 數(shù)據(jù)結(jié)構(gòu)來(lái)存儲(chǔ)用戶信息尚骄,商品信息等等倔丈。

    key=JavaUser293847
    value={
        “id”: 1,
        “name”: “SnailClimb”,
        “age”: 22,
        “l(fā)ocation”: “Wuhan, Hubei”
    }
    
  3. List

    常用命令:lpush, rpush, lpop, rpop,lrange等需五。

    list 就是鏈表宏邮,Redis list 的應(yīng)用場(chǎng)景非常多蜜氨,也是Redis最重要的數(shù)據(jù)結(jié)構(gòu)之一记劝,如微博的關(guān)注列表厌丑,粉絲列表怒竿,消息列表等功能都可以用Redis的 list 結(jié)構(gòu)來(lái)實(shí)現(xiàn)耕驰。

    Redis list 的實(shí)現(xiàn)為一個(gè)雙向鏈表朦肘,即可以支持反向查找和遍歷媒抠,更方便操作趴生,不過(guò)帶來(lái)了部分額外的內(nèi)存開(kāi)銷(xiāo)苍匆。

    另外可以通過(guò) lrange 命令浸踩,就是從某個(gè)元素開(kāi)始讀取多少個(gè)元素民轴,可以基于 list 實(shí)現(xiàn)分頁(yè)查詢后裸,這個(gè)很棒的一個(gè)功能微驶∫蚱唬基于 redis 實(shí)現(xiàn)簡(jiǎn)單的高性能分頁(yè)凶杖,可以做類(lèi)似微博那種下拉不斷分頁(yè)的東西(一頁(yè)一頁(yè)的往下走),性能高。

  4. Set

    常用命令:sadd, spop, smembers, sunion 等。

    set 對(duì)外提供的功能與list類(lèi)似是一個(gè)列表的功能,特殊之處在于 set 無(wú)重復(fù)數(shù)據(jù)。并且set提供了判斷某個(gè)成員是否在一個(gè)set集合內(nèi)的重要接口,這個(gè)也是list所不能提供的⌒ǎ可以基于 set 輕易實(shí)現(xiàn)交集、并集、差集的操作。

    如:在微博應(yīng)用中现拒,可以將一個(gè)用戶所有的關(guān)注人存在一個(gè)集合中脱衙,將其所有粉絲存在一個(gè)集合。Redis可以非常方便的實(shí)現(xiàn)如共同關(guān)注、共同粉絲、共同喜好等功能秸苗。這個(gè)過(guò)程也就是求交集的過(guò)程玖瘸,具體命令如下:

    sinterstore key1 key2 key3     將交集存在key1內(nèi)
    
  5. Sorted Set

    常用命令:zadd, zrange, zrem, zcard等弧可。

    和set相比,sorted set增加了一個(gè)權(quán)重參數(shù)score,使得集合中的元素能夠按score進(jìn)行有序排列。

    如:在直播系統(tǒng)中座硕,實(shí)時(shí)排行信息包含直播間在線用戶列表机隙,各種禮物排行榜,彈幕消息(可以理解為按消息維度的消息排行榜)等信息,適合使用 Redis 中的 Sorted Set 結(jié)構(gòu)進(jìn)行存儲(chǔ)。

7. redis 刪除不必要的緩存數(shù)據(jù)

過(guò)期策略 + 內(nèi)存淘汰機(jī)制

7.1 過(guò)期策略

Redis中有個(gè)設(shè)置時(shí)間過(guò)期的功能模庐,即對(duì)存儲(chǔ)在 redis 數(shù)據(jù)庫(kù)中的值可以設(shè)置一個(gè)過(guò)期時(shí)間。作為一個(gè)緩存數(shù)據(jù)庫(kù)顶吮,這是非常實(shí)用的湃交。如我們一般項(xiàng)目中的 token 或者一些登錄信息,尤其是短信驗(yàn)證碼都是有時(shí)間限制的,按照傳統(tǒng)的數(shù)據(jù)庫(kù)處理方式温圆,一般都是自己判斷過(guò)期膝蜈,這樣無(wú)疑會(huì)嚴(yán)重影響項(xiàng)目性能。

我們 set key 時(shí)备绽,可以給一個(gè) expire time深浮,就是過(guò)期時(shí)間蜗顽,通過(guò)過(guò)期時(shí)間我們可以指定這個(gè) key 可以存活的時(shí)間栖忠。

redis 刪除過(guò)期key的方式:定期刪除 + 惰性刪除。

  1. 定期刪除:redis默認(rèn)是每隔 100ms 就隨機(jī)抽取一些設(shè)置了過(guò)期時(shí)間的key,檢查其是否過(guò)期古沥,如果過(guò)期就刪除。注意這里是隨機(jī)抽取的栋齿。為什么要隨機(jī)呢歌亲?想一想假如 redis 存了幾十萬(wàn)個(gè) key 陷揪,每隔100ms就遍歷所有的設(shè)置過(guò)期時(shí)間的 key 的話,就會(huì)給 CPU 帶來(lái)很大的負(fù)載。
  2. 惰性刪除:定期刪除可能會(huì)導(dǎo)致很多過(guò)期 key 到了時(shí)間并沒(méi)有被刪除掉溅漾,所以就有了惰性刪除。惰性刪除不再是Redis去主動(dòng)刪除,而是在客戶端要獲取某個(gè)key時(shí),Redis會(huì)先去檢測(cè)一下這個(gè)key是否已經(jīng)過(guò)期,如果沒(méi)有過(guò)期則返回給客戶端,如果已經(jīng)過(guò)期了,那么Redis會(huì)刪除這個(gè)key启具,不會(huì)返回給客戶端。

過(guò)期鍵有3種刪除策略:定期刪除、惰性刪除、定時(shí)刪除帝嗡。Redis不使用定時(shí)刪除策略。

為什么不使用定時(shí)刪除隘竭?所謂定時(shí)刪除爪幻,指的是用一個(gè)定時(shí)器來(lái)負(fù)責(zé)監(jiān)視key,當(dāng)這個(gè)key過(guò)期就自動(dòng)刪除篷店,雖然內(nèi)存及時(shí)釋放,但是十分消耗CPU資源。

所以惰性刪除可以解決一些過(guò)期了,但沒(méi)被定期刪除隨機(jī)抽取到的key。但有些過(guò)期的key既沒(méi)有被隨機(jī)抽取逮京,也沒(méi)有被客戶端訪問(wèn),就會(huì)一直保留在數(shù)據(jù)庫(kù),占用內(nèi)存,長(zhǎng)期下去可能會(huì)導(dǎo)致內(nèi)存耗盡。所以Redis提供了內(nèi)存淘汰機(jī)制來(lái)解決這個(gè)問(wèn)題。

7.2 內(nèi)存淘汰機(jī)制

redis 提供 8 種數(shù)據(jù)淘汰策略:(7硕并,8是4.0版本后新增加的)

  1. volatile-lru:從已設(shè)置過(guò)期時(shí)間的數(shù)據(jù)集(server.db[i].expires)中挑選最近最少使用的數(shù)據(jù)淘汰
  2. volatile-ttl:從已設(shè)置過(guò)期時(shí)間的數(shù)據(jù)集(server.db[i].expires)中挑選將要過(guò)期的數(shù)據(jù)淘汰
  3. volatile-random:從已設(shè)置過(guò)期時(shí)間的數(shù)據(jù)集(server.db[i].expires)中任意選擇數(shù)據(jù)淘汰
  4. allkeys-lru:當(dāng)內(nèi)存不足以容納新寫(xiě)入數(shù)據(jù)時(shí)乙濒,在鍵空間中,移除最近最少使用的key(這個(gè)是最常用的)
  5. allkeys-random:從數(shù)據(jù)集(server.db[i].dict)中任意選擇數(shù)據(jù)淘汰
  6. no-eviction:禁止驅(qū)逐數(shù)據(jù)桑滩,也就是說(shuō)當(dāng)內(nèi)存不足以容納新寫(xiě)入數(shù)據(jù)時(shí),新寫(xiě)入操作會(huì)報(bào)錯(cuò)。這個(gè)應(yīng)該沒(méi)人使用吧!
  7. volatile-lfu:從已設(shè)置過(guò)期時(shí)間的數(shù)據(jù)集(server.db[i].expires)中挑選最不經(jīng)常使用的數(shù)據(jù)淘汰
  8. allkeys-lfu:當(dāng)內(nèi)存不足以容納新寫(xiě)入數(shù)據(jù)時(shí),在鍵空間中机蔗,移除最不經(jīng)常使用的key

8. redis 持久化機(jī)制

redis 持久化機(jī)制保證 redis 掛掉之后再重啟數(shù)據(jù)庫(kù)可以進(jìn)行恢復(fù)。

很多時(shí)候,我們需要持久化數(shù)據(jù)也就是將內(nèi)存中的數(shù)據(jù)寫(xiě)入到硬盤(pán)里面,大部分原因是為了之后重用數(shù)據(jù)(比如重啟機(jī)器拔恰、機(jī)器故障之后恢復(fù)數(shù)據(jù))风皿,或者是為了防止系統(tǒng)故障而將數(shù)據(jù)備份到一個(gè)遠(yuǎn)程位置。

Redis不同于Memcached的很重一點(diǎn)就是媳维,Redis支持持久化朋凉,而且支持兩種不同的持久化操作盖灸。

  1. Redis的一種持久化方式叫快照(snapshotting赁炎,RDB)
  2. 另一種方式是只追加文件(append-only file讥裤,AOF)

8.1 RDB

快照(snapshotting)持久化

Redis可以通過(guò)創(chuàng)建快照來(lái)獲得存儲(chǔ)在內(nèi)存里面的數(shù)據(jù)在某個(gè)時(shí)間點(diǎn)上的副本。Redis創(chuàng)建快照之后厢破,可以對(duì)快照進(jìn)行備份摩泪,可以將快照復(fù)制到其他服務(wù)器從而創(chuàng)建具有相同數(shù)據(jù)的服務(wù)器副本(Redis主從結(jié)構(gòu)见坑,主要用來(lái)提高Redis性能)荞驴,還可以將快照留在原地以便重啟服務(wù)器的時(shí)候使用熊楼。

快照持久化是Redis默認(rèn)采用的持久化方式孙蒙,在redis.conf配置文件中默認(rèn)有此下配置:

save 900 1           # 在900秒(15分鐘)之后,如果至少有1個(gè)key發(fā)生變化合瓢,Redis就會(huì)自動(dòng)觸發(fā)BGSAVE命令創(chuàng)建快照晴楔。

save 300 10          # 在300秒(5分鐘)之后税弃,如果至少有10個(gè)key發(fā)生變化,Redis就會(huì)自動(dòng)觸發(fā)BGSAVE命令創(chuàng)建快照做修。

save 60 10000        # 在60秒(1分鐘)之后,如果至少有10000個(gè)key發(fā)生變化铣除,Redis就會(huì)自動(dòng)觸發(fā)BGSAVE命令創(chuàng)建快照。

8.2 AOF(append-only file)持久化

與快照持久化相比辑鲤,AOF持久化的實(shí)時(shí)性更好决左,因此已成為主流的持久化方案擦秽。

默認(rèn)情況下Redis沒(méi)有開(kāi)啟AOF(append only file)方式的持久化置谦,可以通過(guò)appendonly參數(shù)開(kāi)啟:

appendonly yes

開(kāi)啟AOF持久化后每執(zhí)行一條會(huì)更改Redis中數(shù)據(jù)的命令签餐,Redis就會(huì)將該命令寫(xiě)入硬盤(pán)中的AOF文件。

AOF文件的保存位置和RDB文件的位置相同,都是通過(guò)dir參數(shù)設(shè)置的搪缨,默認(rèn)的文件名是appendonly.aof。

在Redis的配置文件中存在三種不同的 AOF 持久化方式迫淹,它們分別是:

appendfsync always    # 每次有數(shù)據(jù)修改發(fā)生時(shí)都會(huì)寫(xiě)入AOF文件瑞妇,這樣會(huì)嚴(yán)重降低Redis的速度
appendfsync everysec  # 每秒鐘同步一次,顯示地將多個(gè)寫(xiě)命令同步到硬盤(pán)
appendfsync no        # 讓操作系統(tǒng)決定何時(shí)進(jìn)行同步

為了兼顧數(shù)據(jù)和寫(xiě)入性能形导,用戶可以考慮 appendfsync everysec選項(xiàng) 伪阶,讓Redis每秒同步一次AOF文件,Redis性能幾乎沒(méi)受到任何影響。而且這樣即使出現(xiàn)系統(tǒng)崩潰,用戶最多只會(huì)丟失一秒之內(nèi)產(chǎn)生的數(shù)據(jù)。當(dāng)硬盤(pán)忙于執(zhí)行寫(xiě)入操作的時(shí)候,Redis還會(huì)優(yōu)雅的放慢自己的速度以便適應(yīng)硬盤(pán)的最大寫(xiě)入速度。

8.3 AOF 重寫(xiě)

AOF重寫(xiě)可以產(chǎn)生一個(gè)新的AOF文件泄私,這個(gè)新的AOF文件和原有的AOF文件所保存的數(shù)據(jù)庫(kù)狀態(tài)一樣瞪讼,但體積更小端姚。

AOF重寫(xiě)是一個(gè)有歧義的名字盆顾,該功能是通過(guò)讀取數(shù)據(jù)庫(kù)中的鍵值對(duì)來(lái)實(shí)現(xiàn)的,程序無(wú)須對(duì)現(xiàn)有AOF文件進(jìn)行任何讀入、分析或者寫(xiě)入操作捏卓。

在執(zhí)行 BGREWRITEAOF 命令時(shí),Redis 服務(wù)器會(huì)維護(hù)一個(gè) AOF 重寫(xiě)緩沖區(qū)锣笨,該緩沖區(qū)會(huì)在子進(jìn)程創(chuàng)建新AOF文件期間挺身,記錄服務(wù)器執(zhí)行的所有寫(xiě)命令祷蝌。當(dāng)子進(jìn)程完成創(chuàng)建新AOF文件的工作之后剑令,服務(wù)器會(huì)將重寫(xiě)緩沖區(qū)中的所有內(nèi)容追加到新AOF文件的末尾碍脏,使得新舊兩個(gè)AOF文件所保存的數(shù)據(jù)庫(kù)狀態(tài)一致糊探。最后瞪慧,服務(wù)器用新的AOF文件替換舊的AOF文件矢腻,以此來(lái)完成AOF文件重寫(xiě)操作。

8.4 Redis 4.0 對(duì)于持久化機(jī)制的優(yōu)化

Redis 4.0 開(kāi)始支持 RDB 和 AOF 的混合持久化(默認(rèn)關(guān)閉,可以通過(guò)配置項(xiàng) aof-use-rdb-preamble 開(kāi)啟)沮趣。

如果把混合持久化打開(kāi)温眉,AOF 重寫(xiě)時(shí)就直接把 RDB 的內(nèi)容寫(xiě)到 AOF 文件開(kāi)頭闯冷。這樣做的好處是可以結(jié)合 RDB 和 AOF 的優(yōu)點(diǎn)纺涤, 快速加載同時(shí)避免丟失過(guò)多的數(shù)據(jù)象迎。當(dāng)然缺點(diǎn)也是有的汪厨, AOF 里面的 RDB 部分是壓縮格式不再是 AOF 格式,可讀性較差殖妇。

9. redis 事務(wù)

Redis 通過(guò) MULTI前鹅、EXEC除盏、WATCH 等命令來(lái)實(shí)現(xiàn)事務(wù)(transaction)功能大磺。

事務(wù)提供了一種將多個(gè)命令請(qǐng)求打包锐锣,然后一次性斤彼、按順序地執(zhí)行多個(gè)命令的機(jī)制趁冈,并且在事務(wù)執(zhí)行期間扮超,服務(wù)器不會(huì)中斷事務(wù)而改去執(zhí)行其他客戶端的命令請(qǐng)求崩侠,它會(huì)將事務(wù)中的所有命令都執(zhí)行完畢八拱,然后才去處理其他客戶端的命令請(qǐng)求榛搔。

在傳統(tǒng)的關(guān)系式數(shù)據(jù)庫(kù)中芥吟,常常用 ACID 性質(zhì)來(lái)檢驗(yàn)事務(wù)功能的可靠性和安全性种樱。在 Redis 中佩抹,事務(wù)總是具有原子性(Atomicity)栏豺、一致性(Consistency)和隔離性(Isolation),并且當(dāng) Redis 運(yùn)行在某種特定的持久化模式下時(shí)攀细,事務(wù)也具有持久性(Durability)鱼的。

10. 緩存雪崩和緩存穿透問(wèn)題解決方案

10.1 緩存雪崩

緩存雪崩:緩存同一時(shí)間大面積的失效,所以地梨,后面的請(qǐng)求都會(huì)落到數(shù)據(jù)庫(kù)上雅镊,造成數(shù)據(jù)庫(kù)短時(shí)間內(nèi)承受大量請(qǐng)求而崩掉捌显。

解決辦法:

  1. 事前:盡量保證整個(gè) redis 集群的高可用性,發(fā)現(xiàn)機(jī)器宕機(jī)盡快補(bǔ)上桥状。選擇合適的內(nèi)存淘汰策略。
  2. 事中:本地ehcache緩存 + hystrix限流&降級(jí)他嫡,避免MySQL崩掉(因?yàn)檫@時(shí)是從數(shù)據(jù)庫(kù)請(qǐng)求數(shù)據(jù))。
  3. 事后:利用 redis 持久化機(jī)制保存的數(shù)據(jù)盡快恢復(fù)緩存蓉媳。
解決方法

10.2 緩存穿透

緩存穿透說(shuō)簡(jiǎn)單點(diǎn)就是大量請(qǐng)求的 key 根本不存在于緩存中,導(dǎo)致請(qǐng)求直接到了數(shù)據(jù)庫(kù)上,根本沒(méi)有經(jīng)過(guò)緩存這一層桨武。

舉個(gè)例子:某個(gè)黑客故意制造我們緩存中不存在的 key 發(fā)起大量請(qǐng)求,導(dǎo)致大量請(qǐng)求落到數(shù)據(jù)庫(kù)。

一般 MySQL 默認(rèn)的最大連接數(shù)在 150 左右曾棕,這個(gè)可以通過(guò) show variables like '%max_connections%';命令來(lái)查看翘地。

最大連接數(shù)一個(gè)還只是一個(gè)指標(biāo)时鸵,cpu某筐,內(nèi)存,磁盤(pán),網(wǎng)絡(luò)等物理?xiàng)l件都是其運(yùn)行指標(biāo)谦屑,這些指標(biāo)都會(huì)限制其并發(fā)能力驳糯。所以,一般 3000 個(gè)并發(fā)請(qǐng)求就能打死大部分?jǐn)?shù)據(jù)庫(kù)了氢橙。

解決辦法:

  1. 最基本的就是首先做好參數(shù)校驗(yàn)酝枢。

    一些不合法的參數(shù)請(qǐng)求直接拋出異常信息返回給客戶端。比如查詢的數(shù)據(jù)庫(kù) id 不能小于 0悍手,傳入的郵箱格式不對(duì)的時(shí)候直接返回錯(cuò)誤消息給客戶端等等隧枫。

  2. 緩存無(wú)效 key。

    如果緩存和數(shù)據(jù)庫(kù)都查不到某個(gè) key 的數(shù)據(jù)就寫(xiě)一個(gè)到 redis 中去并設(shè)置過(guò)期時(shí)間谓苟,具體命令如下:SET key value EX 10086官脓。這種方式可以解決請(qǐng)求的 key 變化不頻繁的情況,如果黑客惡意攻擊涝焙,每次構(gòu)建的不同的請(qǐng)求key卑笨,會(huì)導(dǎo)致 redis 中緩存大量無(wú)效的 key 。很明顯仑撞,這種方案并不能從根本上解決此問(wèn)題赤兴。如果非要用這種方式來(lái)解決穿透問(wèn)題的話,盡量將無(wú)效的 key 的過(guò)期時(shí)間設(shè)置短一點(diǎn)隧哮,比如 1 分鐘桶良。

    用代碼表示如下:

    public Object getObjectInclNullById(Integer id) {
        // 從緩存中獲取數(shù)據(jù)
        Object cacheValue = cache.get(id);
        // 緩存為空
        if (cacheValue == null) {
            // 從數(shù)據(jù)庫(kù)中獲取
            Object storageValue = storage.get(key);
            // 緩存空對(duì)象
            cache.set(key, storageValue);
            // 如果存儲(chǔ)數(shù)據(jù)為空,需要設(shè)置一個(gè)過(guò)期時(shí)間(300秒)
            if (storageValue == null) {
                // 必須設(shè)置過(guò)期時(shí)間沮翔,否則有被攻擊的風(fēng)險(xiǎn)
                cache.expire(key, 60 * 5);
            }
            return storageValue;
        }
        return cacheValue;
    }
    
  3. 布隆過(guò)濾器陨帆。

    布隆過(guò)濾器是一個(gè)非常神奇的數(shù)據(jù)結(jié)構(gòu),通過(guò)它我們可以非常方便地判斷一個(gè)給定數(shù)據(jù)是否存在于海量數(shù)據(jù)中。

    把所有可能存在的請(qǐng)求的值都存放在布隆過(guò)濾器中疲牵,當(dāng)用戶請(qǐng)求過(guò)來(lái)承二,先判斷用戶發(fā)來(lái)的請(qǐng)求的值是否存在于布隆過(guò)濾器中。不存在的話纲爸,直接返回請(qǐng)求參數(shù)錯(cuò)誤信息給客戶端亥鸠,存在的話才會(huì)走下面的流程。如下圖所示识啦。

    布隆過(guò)濾器過(guò)濾請(qǐng)求

11. 如何解決 Redis 的并發(fā)競(jìng)爭(zhēng) Key 問(wèn)題

Redis 的并發(fā)競(jìng)爭(zhēng) Key 的問(wèn)題负蚊,就是多個(gè)系統(tǒng)同時(shí)對(duì)一個(gè) key 進(jìn)行操作,但是最后執(zhí)行的順序和我們期望的順序不同颓哮,這樣也就導(dǎo)致了結(jié)果的不同盖桥。

推薦一種方案:分布式鎖(zookeeper 和 redis 都可以實(shí)現(xiàn)分布式鎖)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末题翻,一起剝皮案震驚了整個(gè)濱河市揩徊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌嵌赠,老刑警劉巖塑荒,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異姜挺,居然都是意外死亡齿税,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門(mén)炊豪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)凌箕,“玉大人,你說(shuō)我怎么就攤上這事词渤∏2眨” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵缺虐,是天一觀的道長(zhǎng)芜壁。 經(jīng)常有香客問(wèn)我,道長(zhǎng)高氮,這世上最難降的妖魔是什么慧妄? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮剪芍,結(jié)果婚禮上塞淹,老公的妹妹穿的比我還像新娘。我一直安慰自己罪裹,他們只是感情好饱普,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布运挫。 她就那樣靜靜地躺著,像睡著了一般费彼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上口芍,一...
    開(kāi)封第一講書(shū)人閱讀 49,166評(píng)論 1 284
  • 那天箍铲,我揣著相機(jī)與錄音,去河邊找鬼鬓椭。 笑死颠猴,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的小染。 我是一名探鬼主播翘瓮,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼裤翩!你這毒婦竟也來(lái)了资盅?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤踊赠,失蹤者是張志新(化名)和其女友劉穎呵扛,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體筐带,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡今穿,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了伦籍。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蓝晒。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖帖鸦,靈堂內(nèi)的尸體忽然破棺而出芝薇,到底是詐尸還是另有隱情,我是刑警寧澤作儿,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布剩燥,位于F島的核電站,受9級(jí)特大地震影響立倍,放射性物質(zhì)發(fā)生泄漏灭红。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一口注、第九天 我趴在偏房一處隱蔽的房頂上張望变擒。 院中可真熱鬧,春花似錦寝志、人聲如沸娇斑。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)毫缆。三九已至唯竹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間苦丁,已是汗流浹背浸颓。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留旺拉,地道東北人产上。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像蛾狗,于是被迫代替她去往敵國(guó)和親晋涣。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容

  • 一沉桌、Redis高可用概述 在介紹Redis高可用之前谢鹊,先說(shuō)明一下在Redis的語(yǔ)境中高可用的含義。 我們知道留凭,在w...
    空語(yǔ)閱讀 1,593評(píng)論 0 2
  • 一冰抢、Redis 簡(jiǎn)介 Redis 是完全開(kāi)源免費(fèi)的松嘶,遵守BSD協(xié)議,是一個(gè)高性能(NOSQL)的key-value...
    神奇作手閱讀 488評(píng)論 0 1
  • 企業(yè)級(jí)redis集群架構(gòu)的特點(diǎn) 海量數(shù)據(jù) 高并發(fā) 高可用 要達(dá)到高可用挎扰,持久化是不可減少的翠订,持久化主要是做災(zāi)難恢復(fù)...
    lucode閱讀 2,193評(píng)論 0 7
  • 主要內(nèi)容 redis 簡(jiǎn)介 為什么要用 redis /為什么要用緩存 為什么要用 redis 而不用 map/gu...
    java成功之路閱讀 588評(píng)論 0 4
  • NOSQL類(lèi)型簡(jiǎn)介鍵值對(duì):會(huì)使用到一個(gè)哈希表,表中有一個(gè)特定的鍵和一個(gè)指針指向特定的數(shù)據(jù)遵倦,如redis尽超,volde...
    MicoCube閱讀 3,958評(píng)論 2 27