国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Redis從基礎(chǔ)到進(jìn)階篇(四)----性能調(diào)優(yōu)、分布式鎖與緩存問題

這篇具有很好參考價值的文章主要介紹了Redis從基礎(chǔ)到進(jìn)階篇(四)----性能調(diào)優(yōu)、分布式鎖與緩存問題。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

目錄

一、Redis 集群演變

1.1?Replication+Sentinel*高可用

1.2?Proxy+Replication+Sentinel(僅僅了解)

1.3?Redis Cluster 集群 (重點)

1.3.1?Redis-cluster架構(gòu)圖

1.3.2?工作原理

1.3.3?主從切換

1.3.4 副本漂移

1.3.5 分片漂移

二、Redis版本歷史(增加了解)

三、Redis 5.0 源碼清單 (對源碼感興趣的,看一下)

四、Redis和lua整合

4.1?什么是lua

4.2?Redis中使?lua的好處

4.3?lua的安裝和語法

4.3.1?Redis整合lua腳本

4.4?lua 腳本調(diào)?Redis 命令

4.4.1?redis.call();

4.4.2?redis.pcall();

4.4.3?redis-cli --eval

4.5?Redis+lua 秒殺

五、Redis Stream

5.1?Redis Stream介紹

5.2?Redis Stream使?場景

六、Redis分布式

6.1?業(yè)務(wù)場景

6.2?鎖的處理

6.3?分布式鎖

6.3.1?分布式鎖特點

6.3.2?分布式鎖的實現(xiàn)?式

6.4?Redis?式實現(xiàn)分布式鎖

6.4.1 獲取鎖

6.4.2 釋放鎖

6.4.3?Redis分布式鎖--優(yōu)缺點

6.4.4?本質(zhì)分析

6.5??產(chǎn)環(huán)境中的分布式鎖

6.5.1?加鎖機制

6.5.2?Redisson分布式鎖的使?

七、緩存常見問題

7.1?緩存預(yù)熱

7.2?緩存雪崩

7.3?緩存擊穿

7.4?緩存穿透

7.5?緩存降級

7.6?緩存更新

7.7?緩存數(shù)據(jù)庫雙寫一致性 (重點)

7.7.1?先更新redis再更新db

7.7.2?先更新db再更新redis

7.7.3?先更新DB再刪除redis

7.7.4?先刪除redis再更新DB

7.7.5?延遲雙刪

7.7.6?思考變種

7.7.7?總結(jié)

7.8?多個系統(tǒng)同時操作(并發(fā))Redis帶來的數(shù)據(jù)問題

八、Redis 常見面試問題

8.1?Memcache特點

8.2?Reids 特點


首先是對上一篇文章的補充,接下來開始正題

一、Redis 集群演變

1.1?Replication+Sentinel*高可用

?這套架構(gòu)使用的是社區(qū)版本推出的原生高可用解決方案,其架構(gòu)圖如下

Redis從基礎(chǔ)到進(jìn)階篇(四)----性能調(diào)優(yōu)、分布式鎖與緩存問題,Redis,緩存,redis,分布式

這里Sentinel的作用有三個:

監(jiān)控:Sentinel 會不斷的檢查主服務(wù)器和從服務(wù)器是否正常運行。


通知:當(dāng)被監(jiān)控的某個Redis服務(wù)器出現(xiàn)問題,Sentinel通過API腳本向管理員或者其他的應(yīng)用程序發(fā)送通知。


自動故障轉(zhuǎn)移:當(dāng)主節(jié)點不能正常工作時,Sentinel會開始一次自動的故障轉(zhuǎn)移操作,它會將與失效主節(jié)點是主從關(guān)系的其中一個從節(jié)點升級為新的主節(jié)點,并且將其他的從節(jié)點指向新的主節(jié)點。

工作原理:

當(dāng)Master宕機的時候,Sentinel會選舉出新的Master,并根據(jù)Sentinel中client-reconfig-script腳本配置的內(nèi)容,去動態(tài)修改VIP(虛擬IP),將VIP(虛擬IP)指向新的Master。我們的客戶端就連向指定的VIP即可!故障發(fā)生后的轉(zhuǎn)移情況,可以理解為下圖

??Redis從基礎(chǔ)到進(jìn)階篇(四)----性能調(diào)優(yōu)、分布式鎖與緩存問題,Redis,緩存,redis,分布式

缺陷:

(1)主從切換的過程中會丟數(shù)據(jù)

(2)Redis只能單點寫,不能水平擴容

常用方案:

內(nèi)網(wǎng)DNS,VIP和 封裝客戶端直連Redis Sentinel 端口

(1)內(nèi)網(wǎng)DNS:

底層是 Redis Sentinel 集群,代理著 Redis 主,Web 端連接內(nèi)網(wǎng) DNS 提供服務(wù)。內(nèi)網(wǎng) DNS 按照一定的規(guī)則分配,比如 xxxx.redis.cache/queue.portxxx.xxx,第一個段表示業(yè)務(wù)簡寫,第二個段表示這是Redis 內(nèi)網(wǎng)域名,第三個段表示 Redis 類型,cache 表示緩存,queue 表示隊列,第四個段表示 Redis端口,第五、第六個段表示內(nèi)網(wǎng)主域名。當(dāng)主節(jié)點發(fā)生故障,比如機器故障、Redis 節(jié)點故障或者網(wǎng)絡(luò)不可達(dá),Sentinel集群會調(diào)用 client-reconfig- 配置的腳本,修改對應(yīng)端口的內(nèi)網(wǎng)域名。對應(yīng)端口的內(nèi)網(wǎng)域名指向新的 Redis 主節(jié)點。

優(yōu)點:秒級切換,10秒之內(nèi) ,腳本自定義,架構(gòu)可控,對應(yīng)用透明,前端不用擔(dān)心后端發(fā)生什么變化

缺點:維護成本高,依賴DNS,存在解析超時,哨兵存在短時間服務(wù)不可用,服務(wù)時通過外網(wǎng)不可采用

(2)VIP:

和第一種方案略有不同,把內(nèi)網(wǎng) DNS 換成了虛擬 IP。底層是 Redis Sentinel集群,代理著 Redis 主從,Web 端通過 VIP 提供服務(wù)。在部署 Redis 主從的時候,需要將虛擬P 綁定到當(dāng)前的 Redis 主節(jié)點。當(dāng)主節(jié)點發(fā)生故障,比如機器故障、Redis 節(jié)點故障或者網(wǎng)絡(luò)不可達(dá),Sentinel集群會調(diào)用 client-reconfig-配置的腳本,將VIP 漂移到新的主節(jié)點上。

優(yōu)點:秒級切換,5秒之內(nèi) ;腳本自定義,架構(gòu)可控,對應(yīng)用透明,前端不用擔(dān)心后端發(fā)生什么變化缺點:維護成本更高,使用VIP增加維護成本,并存在IP混亂風(fēng)險

(3)封裝客戶端直連 Redis Sentinel 端口:

這個主要是因為有些業(yè)務(wù)只能通過外網(wǎng)訪問 Redis,于是衍生出了這種方案。Web 使用客戶端連接其中臺 Redis Sentinel 集群中的一臺機器的某個端口,然后通過這個端口獲取到當(dāng)前的主節(jié)點,然后再連接到真實的 Redis 主節(jié)點進(jìn)行相應(yīng)的業(yè)務(wù)員操作。需要注意的是,Redis Sentinel 端口和 Redis 主節(jié)點均需要開放訪問權(quán)限。前端業(yè)務(wù)使用 lava,有JedisSentinelPool 可以復(fù)用。

優(yōu)點: 服務(wù)探測故障及時,DBA維護成本低
缺點: 依賴客戶端支持Sentinel:Sentinel 服務(wù)器和 Redis 節(jié)點需要開放訪問權(quán)限;再有 對應(yīng)用有侵入性

1.2?Proxy+Replication+Sentinel(僅僅了解)

這里的Proxy有兩種選擇:Codis (豌豆英) 和Twemproxy (推特)????????

這套架構(gòu)的時間為2015年,原因有二:

因為Codis開源的比較晚,考慮到更換組件的成本問題。畢竟本來運行好好的東西,你再去換組件,風(fēng)險是很大的。

Redis Cluster在2015年還是試用版,不保證會遇到什么問題,因此不敢嘗試

所以我沒接觸過Codis,之前一直用的是Twemproxy作為Proxy。這里以Twemproxy為例說明,如下圖所示

Redis從基礎(chǔ)到進(jìn)階篇(四)----性能調(diào)優(yōu)、分布式鎖與緩存問題,Redis,緩存,redis,分布式

工作原理:

1.前端使用Twemproxy+KeepAlived做代理,將其后端的多臺Redis實例分片進(jìn)行統(tǒng)一管理與分配。

2.每一個分片節(jié)點的Slave都是Master的副本且只讀

3.Sentinel持續(xù)不斷的監(jiān)控每個分片節(jié)點的Master,當(dāng)Master出現(xiàn)故障且不可用狀態(tài)時,Sentinel會通知/啟動自動故障轉(zhuǎn)移等動作


4.Sentinel 可以在發(fā)生故障轉(zhuǎn)移動作后觸發(fā)相應(yīng)腳本 (通過 client-reconfig-script 參數(shù)配置),腳本獲取到最新的Master來修改Twemproxy配置

缺陷:
(1)部署結(jié)構(gòu)超級復(fù)雜
(2)可擴展性差,進(jìn)行擴縮容需要手動干預(yù)
(3)運維不方便

1.3?Redis Cluster 集群 (重點)

這一章的其他內(nèi)容請在Redis從基礎(chǔ)到進(jìn)階篇(三)----架構(gòu)原理與集群演變?中閱讀,這里是對第三篇進(jìn)行的補充

1.3.1?Redis-cluster架構(gòu)圖

? ??Redis從基礎(chǔ)到進(jìn)階篇(四)----性能調(diào)優(yōu)、分布式鎖與緩存問題,Redis,緩存,redis,分布式

1.3.2?工作原理

1.客戶端與Redis節(jié)點直連,不需要中間Proxy層,直接連接任意一個Master節(jié)點根據(jù)公式

2.HASH_SLOT=CRC16(key) mod 16384,計算出映射到哪個分片上,然后Redis會去相應(yīng)的節(jié)點進(jìn)行操作

優(yōu)點:

(1)無需Sentinel哨兵監(jiān)控,如果Master掛了,Redis Cluster內(nèi)部自動將Slave切換Master

(2)可以進(jìn)行水亞擴容

(3)支持自動化遷移,當(dāng)出現(xiàn)某個slave宕機了,那么就只有Master了,這時候的高可用性就無法很好的保證了,萬一Master也宕機了,咋辦呢? 針對這種情況,如果說其他Master有多余的Slave,集群自動把多余的slave遷移到?jīng)]有slave的Master 中

缺點:

(1)批量操作是個坑
(2)資源隔離性較差,容易出現(xiàn)相互影響的情況.

1.3.3?主從切換

????????當(dāng)集群中節(jié)點通過錯誤檢測機制發(fā)現(xiàn)某個節(jié)點處于fail狀態(tài)時,會執(zhí)行主從切換。Redis 還提供了手動切換的方法,即通過執(zhí)行 cluster failover 命令

自動切換:

切換流程如下 (假設(shè)被切換的主節(jié)點為M,執(zhí)行切換的從節(jié)點為S)

1.?s先更新自己的狀態(tài),將聲明自己為主節(jié)點。并且將s從M中移除
2. 由于s需要切換為主節(jié)點,所以將s的同步數(shù)據(jù)相關(guān)信息清除 (即不再從M同步鎖數(shù)據(jù))
3.?將M提供服務(wù)的slot都聲明到s中:.
4. 發(fā)送一個PONG包,通知集群中其他節(jié)點更新狀態(tài)

手動切換:

當(dāng)一個節(jié)點接受到 cluster failove 命令之后,執(zhí)行手動切換,流程如下

1. 該從節(jié)點首先向主節(jié)點發(fā)送一個mfstart包。通知主節(jié)點從節(jié)點開始進(jìn)行手動切換
2. 主節(jié)點會阻塞所有客戶端指令的執(zhí)行。之后主節(jié)點在周期函數(shù)clusterCron中發(fā)送ping 包時會在包頭部分做特殊標(biāo)記
3. 當(dāng)從節(jié)點收到主節(jié)點的ping包并且檢測到特殊標(biāo)記之后,會從包頭中獲取主節(jié)點的復(fù)制偏移量

4. 從節(jié)點在周期函數(shù)clusterCron中檢測當(dāng)前處理的復(fù)制偏移量與主節(jié)點復(fù)制偏移量是否相等,當(dāng)相等時開始執(zhí)行切換流程

5. 切換完成后,主節(jié)點會講阻塞的所有客戶端命令通過發(fā)送+MOVED 指令重定向到新的主節(jié)點

通過流程可以看到,手動執(zhí)行主從切換流程時不會丟失任何數(shù)據(jù),也不會丟失任何執(zhí)行命令,只在切換過程中會有暫時的停頓

1.3.4 副本漂移

Redis從基礎(chǔ)到進(jìn)階篇(四)----性能調(diào)優(yōu)、分布式鎖與緩存問題,Redis,緩存,redis,分布式

假設(shè)A發(fā)生故障,主A的A1會執(zhí)行切換,切換完成后A1變?yōu)锳1,此時主A1會出現(xiàn)單點問題
在周期性調(diào)度函數(shù) clusterCron中會定期檢查如下條件

1是否存在單點的主節(jié)點,即主節(jié)點沒有任何一臺可用的從節(jié)點
2是否存在有兩臺及以上可用從節(jié)點的主節(jié)點


如果以上兩個條件都滿足,從有最多可用從節(jié)點中選擇一臺從節(jié)點執(zhí)行副本漂移。選擇標(biāo)準(zhǔn)為按節(jié)點名稱從小到大,選擇最靠前的一臺從節(jié)點執(zhí)行漂移。具體漂移過程

Redis從基礎(chǔ)到進(jìn)階篇(四)----性能調(diào)優(yōu)、分布式鎖與緩存問題,Redis,緩存,redis,分布式

3.從C的記錄中將C1移除

4.將C1所記錄的主節(jié)點更改為A1

5.在A1中添加C1從節(jié)點

6.將C1的數(shù)據(jù)同步源設(shè)置為A1

漂移過程只是更改一些節(jié)點所記錄的信息,之后會通過心跳包將該信息同步到所有的集群節(jié)點。

1.3.5 分片漂移

這點也請查看上一篇文章的6.7小節(jié)添加節(jié)點

二、Redis版本歷史(增加了解)

1.Redis2.6 Redis2.6在2012年正式發(fā)布
2.Redis2.8
? ?Redis2.8在2013年11月22日正式發(fā)布
? ?Redis Sentinel第二版,相比于Redis2.6的Redis Sentinel,此版本已經(jīng)變成生產(chǎn)可用。
3.Redis3.0
? ?Redis3.0在2015年4月1日正式發(fā)布

4.Redis3.2
? ?Redis3.2在2016年5月6日正式發(fā)布,集群高可用
5.Redis4.0
? ?Redis 4.0在2017年7月發(fā)布為GA,主要是增加了混合持久化和LFU淘汰策略
6.Redis5.0
? ?Redis5.0 2018年10月18日正式發(fā)布,stream 是重要新增特性

三、Redis 5.0 源碼清單 (對源碼感興趣的,看一下)

1.基本數(shù)據(jù)結(jié)構(gòu)
? ?動態(tài)字符串sds.c
? ?整數(shù)集合intset.c
? ?壓縮列表ziplist.c
? ?快速鏈表quicklist.c
? ?字典dict.c

2.Redis數(shù)據(jù)類型的底層實現(xiàn)
? ?Redis對象object.c
? ?字符串t_string.c
? ?列表t list.c
? ?字典t_hash.c
? ?集合及有序集合t set.c和t_zset

3.Redis數(shù)據(jù)庫的實現(xiàn)
? ?數(shù)據(jù)庫的底層實現(xiàn)db.c
? ?持久化rdb.c和aof.c

4.Redis服務(wù)端和客戶端實現(xiàn)
? ?事件驅(qū)動ae.c和ae_epoll.c
? ?網(wǎng)絡(luò)連接anet.c和networking.c
? ?服務(wù)端程序server.c
? ?客戶端程序redis-cli.c

5. 集群相關(guān)
? ? 主從復(fù)制replication.c

? ? 哨兵sentinel.c

? ? 集群cluster.c

6.特殊數(shù)據(jù)類型

? ?其他數(shù)據(jù)結(jié)構(gòu),如hyperloglog.c、geo.c

? ?數(shù)據(jù)流t stream.c
? ?streams的底層實現(xiàn)結(jié)構(gòu)listpack.c和rax.c

四、Redis和lua整合

4.1?什么是lua

lua 是?種輕量?巧的 腳本語? ,?標(biāo)準(zhǔn) C 語? 編寫并以源代碼形式開放, 其設(shè)計?的是為了嵌?應(yīng)?程序中,從?為應(yīng)?程序提供靈活的擴展和定制功能。

4.2?Redis中使?lua的好處

1. 減少?絡(luò)開銷 ,在 Lua 腳本中可以把多個命令放在同?個腳本中運?
2. 原?操作 , redis 會將整個腳本作為?個整體執(zhí)?,中間不會被其他命令插?。換句話說,編寫腳本 的過程中?需擔(dān)?會出現(xiàn)競態(tài)條件。 隔離性
3. 復(fù)?性 ,客戶端發(fā)送的腳本會永遠(yuǎn)存儲在 redis 中,這意味著其他客戶端可以復(fù)?這?腳本來完成同樣的邏輯

4.3?lua的安裝和語法

lua 教程 https://www.runoob.com/lua/lua-tutorial.html

4.3.1?Redis整合lua腳本

Redis2.6.0 版本開始,通過 內(nèi)置的 lua 編譯 / 解釋器 ,可以使? EVAL 命令對 lua 腳本進(jìn)?求值。

EVAL命令

(1) 在redis客戶端中,執(zhí)?以下命令:
EVAL script numkeys key [key ...] arg [arg ...]

命令說明:?

script 參數(shù): 是?段 Lua 腳本程序,它會被運?在 Redis 服務(wù)器上下?中,這段腳本不必 ( 也不
應(yīng)該 ) 定義為?個 Lua 函數(shù)。
numkeys 參數(shù): ?于指定鍵名參數(shù)的個數(shù)。
key [key ...] 參數(shù): EVAL 的第三個參數(shù)開始算起,使?了 numkeys 個鍵( key ),表示在腳本中所?到的那些Redis (key) ,這些鍵名參數(shù)可以在 Lua 中通過全局變量 KEYS 數(shù)組,? 1 為基址的形式訪問( KEYS[1] , KEYS[2] ,以此類推 ) 。
arg [arg ...] 參數(shù): 可以在 Lua 中通過全局變量 ARGV 數(shù)組訪問,訪問的形式和 KEYS 變量類似 ( ARGV[1] 、 ARGV[2] ,諸如此類 ) 。
例如
./redis-cli
> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
1) "key1"
2) "key2"
3) "first"
4) "second"

4.4?lua 腳本調(diào)?Redis 命令

4.4.1?redis.call();

返回值就是 redis 命令執(zhí)?的返回值
如果出錯,返回錯誤信息,不繼續(xù)執(zhí)?

4.4.2?redis.pcall();

返回值就是 redis 命令執(zhí)?的返回值
如果出錯了 記錄錯誤信息,繼續(xù)執(zhí)?
注意事項:
在腳本中,使? return 語句將返回值返回給客戶端,如果沒有 return ,則返回 nil
示例:
127.0.0.1:6379> eval "return redis.call('set',KEYS[1],'bar')" 1 foo
OK

4.4.3?redis-cli --eval

可以使? redis-cli --eval 命令指定?個 lua 腳本?件去執(zhí)?。
腳本?件 (redis.lua) ,內(nèi)容如下:
local num = redis.call('GET', KEYS[1]);

if not num then
    return 0;
else
    local res = num * ARGV[1];
    redis.call('SET',KEYS[1], res);
    return res;
end
redis 客戶機,執(zhí)?腳本命令:
[root@localhost bin]# ./redis-cli --eval redis.lua lua:incrbyml , 8
(integer) 0
[root@localhost bin]# ./redis-cli incr lua:incrbyml
(integer) 1
[root@localhost bin]# ./redis-cli --eval redis.lua lua:incrbyml , 8
(integer) 8
[root@localhost bin]# ./redis-cli --eval redis.lua lua:incrbyml , 8
(integer) 64
[root@localhost bin]# ./redis-cli --eval redis.lua lua:incrbyml , 2
(integer) 128
[root@localhost bin]# ./redis-cli
命令格式說明:
--eval :告訴 redis 客戶端去執(zhí)?后?的 lua 腳本
redis.lua :具體的 lua 腳本?件名稱
lua:incrbymul : lua 腳本中需要的 key
8 lua 腳本中需要的 value
注意事項:
上?命令中 keys values 中間需要使?逗號隔開,并且逗號兩邊都要有空格
執(zhí)? .lua 腳本 不需要寫 key 的個數(shù)

4.5?Redis+lua 秒殺

秒殺場景經(jīng)常使?這個東?,主要利?他的原?性
1.?先定義redis數(shù)據(jù)結(jié)構(gòu)
goodId:
 {
    "total":100,
    "released":0;
 }
其中 goodId 為商品 id 號,可根據(jù)此來查詢相關(guān)的數(shù)據(jù)結(jié)構(gòu)信息, total 為總數(shù), released 為發(fā)放出去
的數(shù)量,可使?數(shù)為 total-released
2.編寫lua腳本
local n = tonumber(ARGV[1])
if not n or n == 0 then
    return 0
end
    local vals = redis.call("HMGET", KEYS[1], "total", "released");
    local total = tonumber(vals[1])
    local blocked = tonumber(vals[2])
if not total or not blocked then
    return 0
end
    if blocked + n <= total then
    redis.call("HINCRBY", KEYS[1], "released", n)
    return n;
end
return 0
執(zhí)?腳本命令 EVAL script_string 1 goodId apply_count
若庫存?夠則返回申請的數(shù)量,否則返回0,不返回可滿?的剩余數(shù)
3.spring?boot 調(diào)?
pom dependency
<groupId>org.springframework.boot</groupId> 
<artifactId>spring-boot-starter-data-redis</artifactId> 
<version>2.0.1.RELEASE</version>
long count = redisHelper.getStrCache().execute(new RedisCallback<Long>() {
     @Nullable
     @Override
     public Long doInRedis(RedisConnection redisConnection) throws DataAccessException {
         long ret =redisConnection.eval(script.getScriptAsString().getBytes(),ReturnType.INTEGER, 1, key.getBytes(), String.valueOf(count).getBytes());
         return ret;
     }
 });
4.redis->database
針對redis到databases的更新,思考了很久,沒有找到較好的解決辦法,先采?定時任務(wù)異步更新。?于數(shù)據(jù)是否丟失的問題,如果redis掛了,重啟后redis會恢復(fù)數(shù)據(jù),等下次定時任務(wù)就可以 將數(shù)據(jù)庫中的數(shù)據(jù)保持?致,缺點是redis掛了秒殺活動會失敗。
?于 redis database 更新?案:
redis 存?份相關(guān) hash 鍵名單表,通過讀取名單表來讀取更新
通過流式讀取 databases 中的表來讀取更新。

五、Redis Stream

5.1?Redis Stream介紹

Redis 5.0 全新的數(shù)據(jù)類型: streams ,官?把它定義為:以更抽象的?式建模?志的數(shù)據(jù)結(jié)構(gòu)。 Redis 的streams 主要是?個 append only AOF )的數(shù)據(jù)結(jié)構(gòu),?少在概念上它是?種在內(nèi)存中表示的抽象 數(shù)據(jù)類型,只不過它們實現(xiàn)了更強?的操作,以克服?志?件本身的限制。
如果你了解 MQ ,那么可以把 streams 當(dāng)做基于內(nèi)存的 MQ 。如果你還了解 kafka ,那么甚?可以把streams當(dāng)做基于內(nèi)存的 kafka listpack 存儲信息, Rax 組織 listpack 消息鏈表
listpack 是對 ziplist 的改進(jìn),它? ziplist 少了?個定位最后?個元素的屬性
另外,這個功能有點類似于 redis 以前的 Pub/Sub ,但是也有基本的不同:
1.streams ?持多個客戶端(消費者)等待數(shù)據(jù)( Linux 環(huán)境開多個窗?執(zhí)? XREAD 即可模擬),并 且每個客戶端得到的是完全相同的數(shù)據(jù)。
2.Pub/Sub 是發(fā)送忘記的?式,并且不存儲任何數(shù)據(jù); ? streams 模式下,所有消息被?限期追加在 streams 中,除??于顯式執(zhí)?刪除( XDEL 。 XDEL 只做?個標(biāo)記位 其實信息和?度還在。
3.streams Consumer Groups 也是 Pub/Sub ?法實現(xiàn)的控制?式。
它主要有消息、?產(chǎn)者、消費者、消費組 4 組成
streams 數(shù)據(jù)結(jié)構(gòu)本身?常簡單,但是 streams 依然是 Redis 到?前為?最復(fù)雜的類型,其原因是實現(xiàn)的 ?些額外的功能:?系列的阻塞操作允許消費者等待?產(chǎn)者加?到streams 的新數(shù)據(jù)。另外還有?個稱為Consumer Groups 的概念, Consumer Group 概念最先由 kafka 提出, Redis 有?個類似實現(xiàn),和kafka的 Consumer Groups 的?的是?樣的:允許?組客戶端協(xié)調(diào)消費相同的信息流!
發(fā)布消息
127.0.0.1:6379> xadd mystream * message apple
"1589994652300-0"
127.0.0.1:6379> xadd mystream * message orange
"1589994679942-0"
讀取消息
127.0.0.1:6379> xrange mystream - + 
1) 1) "1589994652300-0"
   2) 1) "message"
      2) "apple"
2) 1) "1589994679942-0"
   2) 1) "message"
      2) "orange"
阻塞讀取
xread block 0 streams mystream $
發(fā)布新消息
127.0.0.1:6379> xadd mystream * message strawberry
創(chuàng)建消費組
127.0.0.1:6379> xgroup create mystream mygroup1 0
OK
127.0.0.1:6379> xgroup create mystream mygroup2 0
OK
通過消費組讀取消息
127.0.0.1:6379> xreadgroup group mygroup1 zange count 2 streams mystream >
1) 1) "mystream"
   2) 1) 1) "1589994652300-0"
         2) 1) "message"
             2) "apple"
      2) 1) "1589994679942-0"
         2) 1) "message"
            2) "orange"

127.0.0.1:6379> xreadgroup group mugroup1 tuge count 2 streams mystream >
1) 1) "mystream"
   2) 1) 1) "1589995171242-0"
         2) 1) "message"
            2) "strawberry"
 
127.0.0.1:6379> xreadgroup group mugroup2 tuge count 1 streams mystream >
1) 1) "mystream"
   2) 1) 1) "1589995171242-0"
         2) 1) "message"
            2) "apple"

5.2?Redis Stream使?場景

可?作時通信等,?數(shù)據(jù)分析,異地數(shù)據(jù)備份等

Redis從基礎(chǔ)到進(jìn)階篇(四)----性能調(diào)優(yōu)、分布式鎖與緩存問題,Redis,緩存,redis,分布式

客戶端可以平滑擴展,提?處理能?

Redis從基礎(chǔ)到進(jìn)階篇(四)----性能調(diào)優(yōu)、分布式鎖與緩存問題,Redis,緩存,redis,分布式

六、Redis分布式

6.1?業(yè)務(wù)場景

1 、庫存超賣 ?如 5 個筆記本 A 準(zhǔn)備買 3 B 2 C 4 ?下單 3+2+4 =9
2 、防??戶重復(fù)下單
3 、 MQ 消息去重
4 、訂單操作變更
分析:
業(yè)務(wù)場景共性:
共享資源競爭
? ? ?戶 id 、訂單 id 、商品 id 。。。
解決?案:
? ? 共享資源互斥
? ? 共享資源串?化
問題轉(zhuǎn)化
? ? 鎖的問題 (將需求抽象后得到問題的本質(zhì))

6.2?鎖的處理

單應(yīng)?中使?鎖:(單進(jìn)程多線程)
synchronize ReentrantLock
分布式應(yīng)?中使?鎖:(多進(jìn)程多線程)
分布式鎖是控制分布式系統(tǒng)之間同步訪問共享資源的?種?式。

6.3?分布式鎖

流程圖

Redis從基礎(chǔ)到進(jìn)階篇(四)----性能調(diào)優(yōu)、分布式鎖與緩存問題,Redis,緩存,redis,分布式

分布式鎖的狀態(tài):
1. 客戶端通過競爭獲取鎖才能對共享資源進(jìn)?操作 ( ①獲取鎖 ) ;
2. 當(dāng)持有鎖的客戶端對共享資源進(jìn)?操作時(②占有鎖)
3. 其他客戶端都不可以對這個資源進(jìn)?操作(③阻塞)
4. 直到持有鎖的客戶端完成操作( ④釋放鎖 )

6.3.1?分布式鎖特點

互斥性
????????在任意時刻,只有?個客戶端可以持有鎖(排他性)
?可?,具有容錯性
????????只要鎖服務(wù)集群中的?部分節(jié)點正常運?,客戶端就可以進(jìn)?加鎖解鎖操作
避免死鎖
????????具備鎖失效機制,鎖在?段時間之后?定會釋放。(正常釋放或超時釋放)
加鎖和解鎖為同?個客戶端

?????????個客戶端不能釋放其他客戶端加的鎖了

6.3.2?分布式鎖的實現(xiàn)?式

基于數(shù)據(jù)庫實現(xiàn)分布式鎖
基于 zookeeper 時節(jié)點的分布式鎖
基于 Redis 的分布式鎖
基于 Etcd 的分布式鎖

6.4?Redis?式實現(xiàn)分布式鎖

6.4.1 獲取鎖

Redis2.6.12 版本之前,使? Lua 腳本保證原?性,獲取鎖代碼

Redis從基礎(chǔ)到進(jìn)階篇(四)----性能調(diào)優(yōu)、分布式鎖與緩存問題,Redis,緩存,redis,分布式

Redis2.6.12 版本開始,使? Set ?個命令實現(xiàn)加鎖,獲取鎖代

Redis從基礎(chǔ)到進(jìn)階篇(四)----性能調(diào)優(yōu)、分布式鎖與緩存問題,Redis,緩存,redis,分布式

6.4.2 釋放鎖

redis+lua 腳本
public static boolean releaseLock(String lockKey, String requestId) {
     String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return
    redis.call('del', KEYS[1]) else return 0 end";
     Object result = jedis.eval(script, Collections.singletonList(lockKey),
    Collections.singletonList(requestId));
     if (result.equals(1L)) {
         return true;
     }
     return false;
 }

6.4.3?Redis分布式鎖--優(yōu)缺點

優(yōu)點:
????????Redis是基于內(nèi)存存儲,并發(fā)性能好。
缺點:
????????1. 需要考慮原?性、超時、誤刪等情形。
????????2. 獲鎖失敗時,客戶端只能?旋等待,在?并發(fā)情況下,性能消耗?較?。
rediscluster--redis主從復(fù)制的坑
redis ?可?最常?的?案就是 主從復(fù)制 (master-slave ),這種模式也給 redis 分布式鎖 挖了?坑。 redis cluster 集群環(huán)境下,假如現(xiàn)在 A 客戶端 想要加鎖,它會根據(jù)路由規(guī)則選擇?臺 master 節(jié)點寫? key mylock ,在加鎖成功后, master 節(jié)點會把 key 異步復(fù)制給對應(yīng)的 slave 節(jié)點。如果此時 redis master 節(jié)點宕機,為保證集群可?性,會進(jìn)? 主備切換 slave 變?yōu)榱? redis master 。 B 客戶端 在新的 master 節(jié)點上加鎖成功,? A 客戶端 也以為??還是成功加了鎖的。 此時就會導(dǎo)致同?時間內(nèi)多個客戶端對?個分布式鎖完成了加鎖,導(dǎo)致各種臟數(shù)據(jù)的產(chǎn)?。?于解決辦法嘛,?前看還沒有什么根治 的?法, 只能盡量保證機器的穩(wěn)定性 ,減少發(fā)?此事件的概率。

6.4.4?本質(zhì)分析

CAP 模型分析
P:容錯
A:???可?
C:?致性
在分布式環(huán)境下不可能滿?三者共存,只能滿?其中的兩者共存,在分布式下 P 不能舍棄 ( 舍棄 P 就是單機了) 。
所以只能是 CP (強?致性模型)和 AP( ?可?模型 ) 。
分布式鎖是 CP 模型, Redis 集群是 AP 模型。 (base)
為什么還可以?Redis實現(xiàn)分布式鎖?
與業(yè)務(wù)有關(guān)
當(dāng)業(yè)務(wù)不需要數(shù)據(jù)強?致性時,?如:社交場景,就可以使? Redis 實現(xiàn)分布式鎖
當(dāng)業(yè)務(wù)必須要數(shù)據(jù)的強?致性,即不允許重復(fù)獲得鎖,?如?融場景(重復(fù)下單,重復(fù)轉(zhuǎn)賬)就不要使?
可以使? CP 模型實現(xiàn),?如: zookeeper etcd

6.5??產(chǎn)環(huán)境中的分布式鎖

?前落地?產(chǎn)環(huán)境?分布式鎖,?般采?開源框架,?如 Redisson 。下?來講?下 Redisson Redis 分布式鎖的實現(xiàn)。
Redisson 分布式鎖的實現(xiàn)原理

Redis從基礎(chǔ)到進(jìn)階篇(四)----性能調(diào)優(yōu)、分布式鎖與緩存問題,Redis,緩存,redis,分布式

6.5.1?加鎖機制

如果該客戶端?對的是?個 redis cluster 集群,他?先會根據(jù) hash 節(jié)點選擇?臺機器。
發(fā)送 lua 腳本到 redis 服務(wù)器上,腳本如下 :
"if (redis.call('exists',KEYS[1])==0) then "+
 "redis.call('hset',KEYS[1],ARGV[2],1) ; "+
 "redis.call('pexpire',KEYS[1],ARGV[1]) ; "+
 "return nil; end ;" +
"if (redis.call('hexists',KEYS[1],ARGV[2]) ==1 ) then "+
 "redis.call('hincrby',KEYS[1],ARGV[2],1) ; "+
 "redis.call('pexpire',KEYS[1],ARGV[1]) ; "+
 "return nil; end ;" +
"return redis.call('pttl',KEYS[1]) ;"
lua 的作?:保證這段復(fù)雜業(yè)務(wù)邏輯執(zhí)?的原?性。
lua 的解釋:
KEYS[1]) : 加鎖的 key
ARGV[1] key 的?存時間,默認(rèn)為 30
ARGV[2] : 加鎖的客戶端 ID ( UUID.randomUUID() + “:” + threadId )
第?段 if 判斷語句,就是? “exists myLock” 命令判斷?下,如果你要加鎖的那個鎖 key 不存在的話,你就進(jìn)?加鎖。如何加鎖呢?很簡單,?下?的命令:
hset myLock
8743c9c0-0795-4907-87fd-6c719a6b4586:1 1
通過這個命令設(shè)置?個 hash 數(shù)據(jù)結(jié)構(gòu),這?命令執(zhí)?后,會出現(xiàn)?個類似下?的數(shù)據(jù)結(jié)構(gòu):
myLock :{"8743c9c0-0795-4907-87fd-6c719a6b4586:1":1 }
上述就代表 “8743c9c0-0795-4907-87fd-6c719a6b4586:1” 這個客戶端對 “myLock” 這個鎖 key 完成了加鎖。
接著會執(zhí)? “pexpire myLock 30000” 命令,設(shè)置 myLock 這個鎖 key 的?存時間是 30 秒。

(1)?鎖互斥機制

那么在這個時候,如果客戶端 2 來嘗試加鎖,執(zhí)?了同樣的?段 lua 腳本,會咋樣呢?
很簡單,第?個 if 判斷會執(zhí)? “exists myLock” ,發(fā)現(xiàn) myLock 這個鎖 key 已經(jīng)存在了。
接著第?個 if 判斷,判斷?下, myLock key hash 數(shù)據(jù)結(jié)構(gòu)中,是否包含客戶端 2 ID ,但是明顯不是的,因為那?包含的是客戶端1 ID
所以,客戶端 2 會獲取到 pttl myLock 返回的?個數(shù)字,這個數(shù)字代表了 myLock 這個鎖 key 剩余?存時 間。 ?如還剩 15000 毫秒的?存時間。
此時客戶端 2 會進(jìn)??個 while 循環(huán),不停的嘗試加鎖。

(2)??動延時機制

只要客戶端 1 ?旦加鎖成功,就會啟動?個 watch dog 看?狗, 他是?個后臺線程,會每隔 10 秒檢查? ,如果客戶端 1 還持有鎖 key ,那么就會不斷的延?鎖 key 的?存時間。

(3)?可重?鎖機制

第?個 if 判斷肯定不成?, “exists myLock” 會顯示鎖 key 已經(jīng)存在了。
第?個 if 判斷會成?,因為 myLock hash 數(shù)據(jù)結(jié)構(gòu)中包含的那個 ID ,就是客戶端 1 的那個 ID ,也就是 “8743c9c0-0795-4907-87fd-6c719a6b4586:1”
此時就會執(zhí)?可重?加鎖的邏輯,他會?:
incrby myLock
8743c9c0-0795-4907-87fd-6c71a6b4586:1 1
通過這個命令,對客戶端 1 的加鎖次數(shù),累加 1 。數(shù)據(jù)結(jié)構(gòu)會變成:
myLock :{"8743c9c0-0795-4907-87fd-6c719a6b4586:1":2 }

(4)?釋放鎖機制

執(zhí)? lua 腳本如下:
#如果key已經(jīng)不存在,說明已經(jīng)被解鎖,直接發(fā)布(publish)redis消息
"if (redis.call('exists', KEYS[1]) == 0) then " +
     "redis.call('publish', KEYS[2], ARGV[1]); " +
     "return 1; " +
    "end;" +
# key和field不匹配,說明當(dāng)前客戶端線程沒有持有鎖,不能主動解鎖。
     "if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +
         "return nil;" +
     "end; " +
# 將value減1
     "local counter = redis.call('hincrby', KEYS[1], ARGV[3],-1); " +
# 如果counter>0說明鎖在重?,不能刪除key
     "if (counter > 0) then " +
         "redis.call('pexpire', KEYS[1], ARGV[2]); " +
         "return 0; " +
# 刪除key并且publish 解鎖消息
     "else " +
         "redis.call('del', KEYS[1]); " +
         "redis.call('publish', KEYS[2], ARGV[1]); " +
         "return 1; "+
      "end; " +
      "return nil;",
– KEYS[1] :需要加鎖的 key ,這?需要是字符串類型。
– KEYS[2] redis 消息的 ChannelName, ?個分布式鎖對應(yīng)唯?的?個 channelName:
“redisson_lock channel {” + getName() + “}”
– ARGV[1] reids 消息體,這?只需要?個字節(jié)的標(biāo)記就可以,主要標(biāo)記 redis key 已經(jīng)解鎖,再結(jié)合redis的 Subscribe ,能喚醒其他訂閱解鎖消息的客戶端線程申請鎖。
– ARGV[2] :鎖的超時時間,防?死鎖
– ARGV[3] :鎖的唯?標(biāo)識,也就是剛才介紹的 id UUID.randomUUID() + “:” + threadId
如果執(zhí)? lock.unlock() ,就可以釋放分布式鎖,此時的業(yè)務(wù)邏輯也是?常簡單的。
其實說?了,就是每次都對 myLock 數(shù)據(jù)結(jié)構(gòu)中的那個加鎖次數(shù)減 1 。
如果發(fā)現(xiàn)加鎖次數(shù)是 0 了,說明這個客戶端已經(jīng)不再持有鎖了,此時就會?:
“del myLock” 命令,從 redis ?刪除這個 key 。
然后呢,另外的客戶端 2 就可以嘗試完成加鎖了。

6.5.2?Redisson分布式鎖的使?

(1)?加?jar包的依賴

<dependency>
   <groupId>org.redisson</groupId>
   <artifactId>redisson</artifactId>
   <version>3.7.2</version>
</dependency>

(2)?配置Redisson

 private static Config config = new Config();
     //聲明redisso對象
     private static Redisson redisson = null;
     //實例化redisson
    static{
         config.useClusterServers()
        // 集群狀態(tài)掃描間隔時間,單位是毫秒
         .setScanInterval(2000)
         //cluster?式?少6個節(jié)點(3主3從,3主做sharding,3從?來保證主宕機后可以?可?)
         .addNodeAddress("redis://127.0.0.1:6379" )
         .addNodeAddress("redis://127.0.0.1:6380")
         .addNodeAddress("redis://127.0.0.1:6381")
         .addNodeAddress("redis://127.0.0.1:6382")
         .addNodeAddress("redis://127.0.0.1:6383")
         .addNodeAddress("redis://127.0.0.1:6384");
 
         //得到redisson對象
         redisson = (Redisson) Redisson.create(config);
    }
    //獲取redisson對象的?法
     public static Redisson getRedisson(){
         return redisson;
     }
}

(3)?鎖的獲取和釋放?

public class DistributedRedisLock {
     //從配置類中獲取redisson對象
     private static Redisson redisson = RedissonManager.getRedisson();
     private static final String LOCK_TITLE = "redisLock_";
     //加鎖
     public static boolean acquire(String lockName){
         //聲明key對象
         String key = LOCK_TITLE + lockName;
         //獲取鎖對象
         RLock mylock = redisson.getLock(key);
         //加鎖,并且設(shè)置鎖過期時間3秒,防?死鎖的產(chǎn)? uuid+threadId
         mylock.lock(3,TimeUtil.SECOND);
         //加鎖成功
         return true;
     }
     //鎖的釋放
     public static void release(String lockName){
         //必須是和加鎖時的同?個key
         String key = LOCK_TITLE + lockName;
         //獲取鎖對象
         RLock mylock = redisson.getLock(key);
         //釋放鎖(解鎖)
         mylock.unlock();
 
     }
}

(4)?業(yè)務(wù)邏輯中使?分布式鎖

public String discount() throws IOException{
     String key = "test123";
     //加鎖
     DistributedRedisLock.acquire(key);
     //執(zhí)?具體業(yè)務(wù)邏輯
     dosoming
     //釋放鎖
     DistributedRedisLock.release(key);
     //返回結(jié)果
     return soming;
 }

七、緩存常見問題

7.1?緩存預(yù)熱

1. 直接寫個緩存刷新頁面,上線前手工操作一下
2. 數(shù)據(jù)量不大的時候,可以在項目啟動的時候自動加載
3. 定時刷新緩存

7.2?緩存雪崩

什么叫緩存雪崩?

當(dāng)緩存服務(wù)器重啟或者大量緩存集中在某一個時間段失效,這樣在失效的時候,也會給后端系統(tǒng) ( 比如 DB)帶來很大壓力。
如何解決?
1 :在緩存失效后,通過加鎖或者隊列來控制讀數(shù)據(jù)庫寫緩存的線程數(shù)量。比如對某個 key 只允許一個線程查詢數(shù)據(jù)和寫緩存,其他線程等待。
2 :不同的 key ,設(shè)置不同的過期時間,讓緩存失效的時間點盡量均勻。
setRedis Key , value , time + Math.random() * 10000 ) 代碼
3 :做二級緩存, A1 為原始緩存, A2 為拷貝緩存, A1 失效時,可以訪問 A2 , A1 緩存失效時間設(shè)置為短期,A2設(shè)置為長期(此點為補充)
多級緩存 頭條的時候 文章 - 熱點文章
CDN Nginx 啟動 THP 拒敵于國門之外

7.3?緩存擊穿

什么叫緩存擊穿?
對于一些設(shè)置了過期時間的 key ,如果這些 key 可能會在某些時間點被超高并發(fā)地訪問,是一種非常 熱點” 的數(shù)據(jù)。這個時候,需要考慮一個問題:緩存被 擊穿 的問題,這個和緩存雪崩的區(qū)別在于這里針對某一key 緩存,前者則是很多 key
緩存在某個時間點過期的時候,恰好在這個時間點對這個 Key 有大量的并發(fā)請求過來,這些請求發(fā)現(xiàn)緩存過期一般都會從后端DB 加載數(shù)據(jù)并回設(shè)到緩存,這個時候大并發(fā)的請求可能會瞬間把后端 DB 壓垮。
如何解決?
使用 redis setnx 互斥鎖先進(jìn)行判斷,這樣其他線程就處于等待狀態(tài),保證不會有大并發(fā)操作去操作數(shù)據(jù)庫。
if(redis.sexnx()==1){
????????//先查詢緩存
????????//查詢數(shù)據(jù)庫
????????//加入緩存
}
設(shè)置監(jiān)控 當(dāng)某個熱點是超級熱點 一般設(shè)置為持久化

7.4?緩存穿透

什么叫緩存穿透?
一般的緩存系統(tǒng),都是按照 key 去緩存查詢,如果不存在對應(yīng)的 value ,就應(yīng)該去后端系統(tǒng)查找(比如DB)。如果 key 對應(yīng)的 value 是一定不存在的,并且對該 key 并發(fā)請求量很大,就會對后端系統(tǒng)造成很大的壓力。
也就是說,對不存在的 key 進(jìn)行高并發(fā)訪問,導(dǎo)致數(shù)據(jù)庫壓力瞬間增大,這就叫做【緩存穿透】。
如何解決?
1. 在服務(wù)器端,接收參數(shù)時業(yè)務(wù)接口中過濾不合法的值, null ,負(fù)值,和空值進(jìn)行檢測和空值。
2.bloom filter :類似于哈希表的一種算法,用所有可能的查詢條件生成一個 bitmap ,在進(jìn)行數(shù)據(jù)庫查詢之前會使用這個bitmap 進(jìn)行過濾,如果不在其中則直接過濾,從而減輕數(shù)據(jù)庫層面的壓力。采用的是一票否決 只要有一個認(rèn)為你不存在 就認(rèn)為你是不存在的
3. 空值緩存:一種比較簡單的解決辦法,在第一次查詢完不存在的數(shù)據(jù)后,將該 key 與對應(yīng)的空值也放入緩存中,只不過設(shè)定為較短的失效時間,例如幾分鐘,這樣則可以應(yīng)對短時間的大量的該key 攻擊,設(shè)置為較短的失效時間是因為該值可能業(yè)務(wù)無關(guān),存在意義不大,且該次的查詢也未必是攻擊者發(fā)起,無過久存儲的必要,故可以早點失效
緩存降級的使用 我們在緩存中沒有數(shù)據(jù)的時候,會給一個隨機值。 水貼,隨貼。

7.5?緩存降級

當(dāng)訪問量出現(xiàn)劇增、服務(wù)出現(xiàn)問題(相應(yīng)時間慢或者不響應(yīng)) 或非核心業(yè)務(wù)影響到核心流程的性能,還需要保證服務(wù)的可用性,即便有損服務(wù)。
11 的時候 一般會對 退款 降級。
方式: 系統(tǒng)根據(jù)一些關(guān)鍵數(shù)據(jù)進(jìn)行降級
????????????配置開關(guān)實現(xiàn)人工降級
有些服務(wù)時無法降級(加入購物車,結(jié)算)
參考日志級別:
一般: ex 有些服務(wù)偶爾網(wǎng)絡(luò)抖動或者服務(wù)正在上線超時,可以自定降級.
警告:有些服務(wù)在一端時間內(nèi)有波動( 95%-100% ),可以自定降級或人工降級,還有發(fā)送
告警.
錯誤:可利用率低于 90% , redis 連接池被打爆了,數(shù)據(jù)庫連接池被打爆,或者訪問量突然猛
增到系統(tǒng)能承受的最大閾值,這時候根據(jù)情況自動降級或人工降級.
嚴(yán)重錯誤:比如因為特殊原因數(shù)據(jù)錯誤了,需要緊急人工降級。 redis服務(wù)出問題了, 不去查數(shù)據(jù)庫,而是直接返回一個默認(rèn)值(自定義一些隨機值).

7.6?緩存更新

自定義的緩存淘汰策略:
1. 定期去清理過期的緩存
2. 當(dāng)有用戶請求過來時,先判斷這個請求用到的緩存是否過期,過期的話就去底層系統(tǒng)得到新數(shù)據(jù)進(jìn)行緩存更新

7.7?緩存數(shù)據(jù)庫雙寫一致性 (重點)

DB KV

雙寫 就一定會出現(xiàn)數(shù)據(jù)一致性問題

一般來說,在讀取緩存方面,我們都是先讀取緩存,再讀取數(shù)據(jù)庫的。
但是,在更新緩存方面,我們是需要先更新緩存,再更新數(shù)據(jù)庫?還是先更新數(shù)據(jù)庫,再更新緩存?還 是說有其他的方案?

7.7.1?先更新redis再更新db

按下面步驟會有問題 ,AB 是兩個線程
A_update_redis 
B_update_redis 
B_update_db 
A_update_db
最終 db a 值但是 redis b 值,不一致

7.7.2?先更新db再更新redis

A_update_db 
B_update_db 
B_update_redis 
A_update_redis
最終 db b 值但是 redis a

7.7.3?先更新DB再刪除redis

A_update_db 
B_update_db 
B_rm_redis 
A_rm_redis
是不是不明白。想不出來怎么不一致了?
不是這樣的,沒這么簡單,第二次 rm_redis 就會保證后面的 redis db 是一致的
實際是下面這種形式
A_get_data 
redis_cache_miss 
A_get_db 
B_update_db 
B_rm_redis 
(此時如果拿db是b值,但是redis沒有值) 
A_update_redis
依賴于 A_update_redis B_update_db 之后,極端情況
此時 redis old , db new

7.7.4?先刪除redis再更新DB

A_rm_redis 
B_get_data 
B_redis_miss
B_get_db 
B_update_redis 
A_update_db
此時 redis old 值, db new

7.7.5?延遲雙刪

rm_redis 
update_db 
sleep xxx ms 
rm_redis
這樣叫做雙刪,最后一次 sleep 一段時間再 rm_redis 保證再次讀請求回溯打到 db ,用最新值寫 redis

7.7.6?思考變種

上面的 3 5 情況可以直接變種,即
update_db 
sleep xxx ms 
rm_redis
解決了 3 中的極端情況(靠 sleep 解決),
并且減少 5 中第一次不必要的 rm redis 請求
當(dāng)然,這個 rm_redis 還可以考慮異步化(提高吞吐)以及重試(避免異步處理失敗),這里不展示

7.7.7?總結(jié)

db 回源到 redis ,需要考慮上面這些極端情況的 case
適用場景
當(dāng)然這些極端情況本身要求同一個 key 是多寫的,這個根據(jù)業(yè)務(wù)需求來看是否需要,比如某些場景本身就是寫少讀多的

7.8?多個系統(tǒng)同時操作(并發(fā))Redis帶來的數(shù)據(jù)問題

系統(tǒng) A 、 B 、 C 三個系統(tǒng),分別去操作 Redis 的同一個 Key ,本來順序是 1 2 3 是正常的,但是因為系統(tǒng)A網(wǎng)絡(luò)突然抖動了一下, B C 在他前面操作了 Redis ,這樣數(shù)據(jù)不就錯了么。
就好比下單,支付,退款三個順序你變了,你先退款,再下單,再支付,那流程就會失敗,那數(shù)據(jù)不就亂了?你訂單還沒生成你卻支付,退款了?明顯走不通了,這在線上是很恐怖的事情。
這種情況怎么解決呢?
可以找個管家?guī)臀覀児芾砗脭?shù)據(jù)的嘛!

Redis從基礎(chǔ)到進(jìn)階篇(四)----性能調(diào)優(yōu)、分布式鎖與緩存問題,Redis,緩存,redis,分布式文章來源地址http://www.zghlxwxcb.cn/news/detail-697470.html

某個時刻,多個系統(tǒng)實例都去更新某個 key ??梢曰? Zookeeper 實現(xiàn)分布式鎖。每個系統(tǒng)通過 Zookeeper 獲取分布式鎖,確保同一時間,只能有一個系統(tǒng)實例在操作某個 Key ,別人都不允許讀和寫。
要寫入緩存的數(shù)據(jù),都是從 MySQL 里查出來的,都得寫入 MySQL 中,寫入 MySQL 中的時候必須保 存一個時間戳,從 MySQL 查出來的時候,時間戳也查出來
每次要 寫之前,先判斷 一下當(dāng)前這個 Value 的時間戳是否比緩存里的 Value 的時間戳要新。如果是的話,那么可以寫,否則,就不能用舊的數(shù)據(jù)覆蓋新的數(shù)據(jù)

八、Redis 常見面試問題

8.1?Memcache特點

MC 處理請求時使用多線程異步 IO 的方式,可以合理利用 CPU 多核的優(yōu)勢,性能非常優(yōu)秀;
MC 功能簡單,使用內(nèi)存存儲數(shù)據(jù)
MC 對緩存的數(shù)據(jù)可以設(shè)置失效期,過期后的數(shù)據(jù)會被清除;
失效的策略采用延遲失效,就是當(dāng)再次使用數(shù)據(jù)時檢查是否失效;
當(dāng)容量存滿時,會對緩存中的數(shù)據(jù)進(jìn)行剔除,剔除時,除了會對過期 key 進(jìn)行清理,還會按 LRU 策略對數(shù)據(jù)進(jìn)行剔除。
MC 有一些限制,這些限制在現(xiàn)在的互聯(lián)網(wǎng)場景下很致命,成為大家選擇 Redis 、 MongoDB 的重要原因:
key 不能超過 250 個字節(jié) ;
value 不能超過 1M 字節(jié) ;
key 的最大失效時間是 30 天;
只支持 K-V 結(jié)構(gòu),不提供持久化和主從同步功能 。
MC 沒有原生的集群,可以依靠客戶端實現(xiàn)往集群中做分片寫入數(shù)據(jù)。

8.2?Reids 特點

MC 不同的是, Redis 采用單線程模式處理請求。這樣做的原因有 2 個:一個是因為采用了非阻塞的異步事件處理機制;另一個是緩存數(shù)據(jù)都是內(nèi)存操作 IO 時間不會太長,單線程可以避免線程上下文切換產(chǎn)生的代價。
Redis 支持持久化,所以 Redis 不僅僅可以用作緩存,也可以用作 NoSQL 數(shù)據(jù)庫。
相比 MC , Redis 還有一個非常大的優(yōu)勢,就是除了 K-V 之外,還支持多種數(shù)據(jù)格式,例如 list 、set、 sorted set 、 hash 等。
Redis 提供主從同步機制,以及 Cluster 集群部署能力,能夠提供高可用服務(wù)。
MC redis 的性能對比
存儲小數(shù)據(jù)時候 Redis 性能是比 MC 性能高
100K 以上 ,MC 的性能是高于 Redis 。
MC 本身沒有集群功能,可以使用客戶端做分片

到了這里,關(guān)于Redis從基礎(chǔ)到進(jìn)階篇(四)----性能調(diào)優(yōu)、分布式鎖與緩存問題的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • “深入解析Redis:高性能緩存與分布式數(shù)據(jù)存儲“

    標(biāo)題:深入解析Redis:高性能緩存與分布式數(shù)據(jù)存儲 摘要:本文將深入解析Redis,介紹其作為高性能緩存和分布式數(shù)據(jù)存儲的特點和功能,并提供示例代碼展示其使用方法。 正文: 一、引言 Redis是一個開源的內(nèi)存數(shù)據(jù)結(jié)構(gòu)存儲系統(tǒng),它以其高性能、靈活的數(shù)據(jù)結(jié)構(gòu)以及豐富的

    2024年02月17日
    瀏覽(25)
  • 分布式系統(tǒng)架構(gòu)設(shè)計之分布式消息隊列的水平擴展性、安全可用性以及監(jiān)控與調(diào)優(yōu)

    分布式系統(tǒng)架構(gòu)設(shè)計之分布式消息隊列的水平擴展性、安全可用性以及監(jiān)控與調(diào)優(yōu)

    隨著業(yè)務(wù)的快速發(fā)展和數(shù)據(jù)的不斷增長,單一的消息隊列服務(wù)器往往難以滿足高并發(fā)、高可用和高吞吐量的需求,因此,如何實現(xiàn)消息隊列的水平擴展成為了一個重要的問題。這部分我將從分區(qū)、副本、負(fù)載均衡等關(guān)鍵概念出發(fā),一起探討如何實現(xiàn)分布式消息隊列的水平擴展

    2024年02月01日
    瀏覽(106)
  • Redis緩存設(shè)計與性能優(yōu)化【緩存和數(shù)據(jù)庫不一致問題,解決方案:1.加過期時間這樣可以一段時間后自動刷新 2.分布式的讀寫鎖】

    Redis緩存設(shè)計與性能優(yōu)化【緩存和數(shù)據(jù)庫不一致問題,解決方案:1.加過期時間這樣可以一段時間后自動刷新 2.分布式的讀寫鎖】

    在大并發(fā)下,同時操作數(shù)據(jù)庫與緩存會存在數(shù)據(jù)不一致性問題 1、雙寫不一致情況 2、讀寫并發(fā)不一致 解決方案: 1、對于并發(fā)幾率很小的數(shù)據(jù)(如個人維度的訂單數(shù)據(jù)、用戶數(shù)據(jù)等),這種幾乎不用考慮這個問題,很少會發(fā)生緩存不一致, 可以給緩存數(shù)據(jù)加上過期時間,每隔一

    2024年04月13日
    瀏覽(29)
  • Java單體到分布式進(jìn)階,分布式到高可用進(jìn)階,單體到微服務(wù)進(jìn)

    Java單體到分布式進(jìn)階,分布式到高可用進(jìn)階,單體到微服務(wù)進(jìn)

    鵝廠實習(xí)第十周 研二下了論文沒有實習(xí)沒有怎么辦 數(shù)據(jù)分析求職Happy Ending 獻(xiàn)上我的面經(jīng)和回答思路 求求大家投下我們鵝廠吧 五年職場人,今做面試官,我來揭秘大學(xué)生校招內(nèi)幕! 五年職場人,今做面試官,我來揭秘大學(xué)生校招內(nèi)幕! 京東Java實習(xí)一面 機械轉(zhuǎn)碼前端上岸,

    2024年03月08日
    瀏覽(27)
  • Redis與分布式-分布式鎖

    Redis與分布式-分布式鎖

    接上文 Redis與分布式-集群搭建 為了解決上述問題,可以利用分布式鎖來實現(xiàn)。 重新復(fù)制一份redis,配置文件都是剛下載時候的不用更改,然后啟動redis服務(wù)和redis客戶。 redis存在這樣的命令:和set命令差不多,但是它有一個機制,當(dāng)指定的key不存在的時候,才能進(jìn)行插入,實

    2024年02月07日
    瀏覽(27)
  • Redis分布式鎖和分布式事務(wù)

    Redis分布式鎖和分布式事務(wù) 一、Redis分布式鎖 1.1 watch和事務(wù)實現(xiàn)分布式鎖 原理是通過watch來觀察一個變量,一個線程在操作的時候,其他線程會操作失敗,相當(dāng)于樂觀鎖。 1.2 setnx實現(xiàn)分布式鎖 原理是通過setnx設(shè)置一個變量,設(shè)置成功的線程搶到鎖,執(zhí)行相關(guān)的業(yè)務(wù),執(zhí)行完畢

    2024年02月09日
    瀏覽(25)
  • 進(jìn)階分布式鏈路追蹤

    進(jìn)階分布式鏈路追蹤

    ? ? ? ? ? ? ? ? ? ? ? ? https://item.jd.com/14337086.html?編輯https://item.jd.com/14337086.html “ RocketMQ 消息中間件實戰(zhàn)派上下冊”是我既“ Spring Cloud Alibaba 微服務(wù)架構(gòu)實戰(zhàn)派上下冊”之后,又一本歷時超過 1 年半的巨無霸技術(shù)實戰(zhàn)類型的書籍。 為了提高讀者閱讀本書的體驗性,本書

    2024年02月02日
    瀏覽(21)
  • 【redis】redis分布式鎖

    一、為什么需要分布式鎖 1.在java單機服務(wù)中,jvm內(nèi)部有一個全局的鎖監(jiān)視器,只有一個線程能獲取到鎖,可以實現(xiàn)線程之間的互斥 2.當(dāng)有多個java服務(wù)時,會有多個jvm,也會有多個鎖監(jiān)視器,這樣沒辦法使得多個jvm之間的線程互斥,所以無法使用jvm內(nèi)部的鎖監(jiān)視器,也就是s

    2023年04月25日
    瀏覽(28)
  • 分布式系統(tǒng)架構(gòu)設(shè)計之分布式數(shù)據(jù)存儲的安全隱私和性能優(yōu)化

    分布式系統(tǒng)架構(gòu)設(shè)計之分布式數(shù)據(jù)存儲的安全隱私和性能優(yōu)化

    在前面分布式系統(tǒng)部分,有對安全性做過介紹,如前面所述,在分布式系統(tǒng)中,確保系統(tǒng)的安全性和隱私是至關(guān)重要的。安全性關(guān)注系統(tǒng)的防護措施,而隱私是關(guān)注用戶的個人信息保護。 身份認(rèn)證:確保用戶和系統(tǒng)組件的身份是合法的,通過通過密碼、令牌或證書實現(xiàn) 授權(quán)

    2024年02月02日
    瀏覽(98)
  • 分布式鎖實現(xiàn)(mysql,以及redis)以及分布式的概念

    分布式鎖實現(xiàn)(mysql,以及redis)以及分布式的概念

    我旁邊的一位老哥跟我說,你知道分布式是是用來干什么的嘛?一句話給我干懵了,我能隱含知道,大概是用來做分壓處理的,并增加系統(tǒng)穩(wěn)定性的。但是具體如何,我卻道不出個1,2,3?,F(xiàn)在就將這些做一個詳細(xì)的總結(jié)。至少以后碰到面試官可以說上個123。 那么就正式進(jìn)入

    2024年01月21日
    瀏覽(37)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包