www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當前位置:首頁 > 公眾號精選 > 架構(gòu)師社區(qū)
[導讀]學C語言的一定要看!

1、基本類型及底層實現(xiàn)

Redis:從應(yīng)用到底層,一文幫你搞定

1.1、String

用途:

適用于簡單key-value存儲、setnx key value實現(xiàn)分布式鎖、計數(shù)器(原子性)、分布式全局唯一ID。

底層:C語言中String用char[]數(shù)組表示,源碼中用SDS(simple dynamic string)封裝char[],這是是Redis存儲的最小單元,一個SDS最大可以存儲512M信息。

struct?sdshdr{
??unsigned?int?len;?//?標記char[]的長度
??unsigned?int?free;?//標記char[]中未使用的元素個數(shù)
??char?buf[];?//?存放元素的坑
}

Redis對SDS再次封裝生成了RedisObject,核心有兩個作用:

  1. 說明是5種類型哪一種。
  2. 里面有指針用來指向 SDS。

當你執(zhí)行set name sowhat的時候,其實Redis會創(chuàng)建兩個RedisObject對象,鍵的RedisObject 和 值的RedisOjbect 其中它們type = REDIS_STRING,而SDS分別存儲的就是 name 跟 sowhat 字符串咯。

并且Redis底層對SDS有如下優(yōu)化:

  1. SDS修改后大小 > 1M時 系統(tǒng)會多分配空間來進行 空間預分配。
  2. SDS是 惰性釋放空間的,你free了空間,可是系統(tǒng)把數(shù)據(jù)記錄下來下次想用時候可直接使用。不用新申請空間。

1.2、List

Redis:從應(yīng)用到底層,一文幫你搞定查看源碼底層 adlist.h 會發(fā)現(xiàn)底層就是個 雙端鏈表,該鏈表最大長度為2^32-1。常用就這幾個組合。

lpush + lpop = stack 先進后出的棧?

lpush + rpop = queue 先進先出的隊列?

lpush + ltrim = capped collection 有限集合

lpush + brpop = message queue 消息隊列

一般可以用來做簡單的消息隊列,并且當數(shù)據(jù)量小的時候可能用到獨有的壓縮列表來提升性能。當然專業(yè)點還是要 RabbitMQ、ActiveMQ等

1.3、Hash

散列非常適用于將一些相關(guān)的數(shù)據(jù)存儲在一起,比如用戶的購物車。該類型在日常用途還是挺多的。

這里需要明確一點:Redis中只有一個K,一個V。其中 K 絕對是字符串對象,而 V 可以是String、List、Hash、Set、ZSet任意一種。

hash的底層主要是采用字典dict的結(jié)構(gòu),整體呈現(xiàn)層層封裝。從小到大如下:

1.3.1、dictEntry

真正的數(shù)據(jù)節(jié)點,包括key、value 和 next 節(jié)點。

Redis:從應(yīng)用到底層,一文幫你搞定

1.3.2、dictht

1、數(shù)據(jù) dictEntry 類型的數(shù)組,每個數(shù)組的item可能都指向一個鏈表。

2、數(shù)組長度 size。

3、sizemask 等于 size - 1。

4、當前 dictEntry 數(shù)組中包含總共多少節(jié)點。

Redis:從應(yīng)用到底層,一文幫你搞定

1.3.3、dict

1、dictType 類型,包括一些自定義函數(shù),這些函數(shù)使得key和value能夠存儲

2、rehashidx 其實是一個標志量,如果為-1說明當前沒有擴容,如果不為 -1 則記錄擴容位置。

3、dictht數(shù)組,兩個Hash表。

4、iterators 記錄了當前字典正在進行中的迭代器

Redis:從應(yīng)用到底層,一文幫你搞定

組合后結(jié)構(gòu)就是如下

Redis:從應(yīng)用到底層,一文幫你搞定

1.3.4、漸進式擴容

為什么 dictht ht[2]是兩個呢?目的是在擴容的同時不影響前端的CURD,慢慢的把數(shù)據(jù)從ht[0]轉(zhuǎn)移到ht[1]中,同時rehashindex來記錄轉(zhuǎn)移的情況,當全部轉(zhuǎn)移完成,將ht[1]改成ht[0]使用。

rehashidx = -1說明當前沒有擴容,rehashidx != -1則表示擴容到數(shù)組中的第幾個了。

擴容之后的數(shù)組大小為大于used*2的2的n次方的最小值,跟 HashMap 類似。然后挨個遍歷數(shù)組同時調(diào)整rehashidx的值,對每個dictEntry[i] 再挨個遍歷鏈表將數(shù)據(jù) Hash 后重新映射到 dictht[1]里面。并且 dictht[0].usedictht[1].use 是動態(tài)變化的。

Redis:從應(yīng)用到底層,一文幫你搞定整個過程的重點在于rehashidx,其為第一個數(shù)組正在移動的下標位置,如果當前內(nèi)存不夠,或者操作系統(tǒng)繁忙,擴容的過程可以隨時停止。

停止之后如果對該對象進行操作,那是什么樣子的呢?

1、如果是新增,則直接新增后第二個數(shù)組,因為如果新增到第一個數(shù)組,以后還是要移過來,沒必要浪費時間

2、如果是刪除,更新,查詢,則先查找第一個數(shù)組,如果沒找到,則再查詢第二個數(shù)組。

Redis:從應(yīng)用到底層,一文幫你搞定

1.4、Set

如果你明白Java中HashSet是HashMap的簡化版那么這個Set應(yīng)該也理解了。都是一樣的套路而已。這里你可以認為是沒有Value的Dict。看源碼 t.set.c 就可以了解本質(zhì)了。

int?setTypeAdd(robj?*subject,?robj?*value)?{
????long?long?llval;
????if?(subject->encoding?==?REDIS_ENCODING_HT)?{
?????????//?看到底層調(diào)用的還是dictAdd,只不過第三個參數(shù)=?NULL
?????????if?(dictAdd(subject->ptr,value,NULL)?==?DICT_OK)?{
????????????incrRefCount(value);
????????????return?1;
????????}
????????....

1.5、ZSet

范圍查找 的天敵就是 有序集合,看底層 redis.h 后就會發(fā)現(xiàn) Zset用的就是可以跟二叉樹媲美的跳躍表來實現(xiàn)有序。跳表就是多層鏈表的結(jié)合體,跳表分為許多層(level),每一層都可以看作是數(shù)據(jù)的索引,這些索引的意義就是加快跳表查找數(shù)據(jù)速度。

每一層的數(shù)據(jù)都是有序的,上一層數(shù)據(jù)是下一層數(shù)據(jù)的子集,并且第一層(level 1)包含了全部的數(shù)據(jù);層次越高,跳躍性越大,包含的數(shù)據(jù)越少。并且隨便插入一個數(shù)據(jù)該數(shù)據(jù)是否會是跳表索引完全隨機的跟玩骰子一樣。

跳表包含一個表頭,它查找數(shù)據(jù)時,是從上往下,從左往右進行查找?,F(xiàn)在找出值為37的節(jié)點為例,來對比說明跳表和普遍的鏈表。

  1. 沒有跳表查詢 比如我查詢數(shù)據(jù)37,如果沒有上面的索引時候路線如下圖: Redis:從應(yīng)用到底層,一文幫你搞定
  2. 有跳表查詢 有跳表查詢37的時候路線如下圖: Redis:從應(yīng)用到底層,一文幫你搞定應(yīng)用場景:

積分排行榜、時間排序新聞、延時隊列。

1.6、Redis Geo

以前寫過Redis Geo核心原理解析,想看的直接跳轉(zhuǎn)即可。他的核心思想就是將地球近似為球體來看待,然后 GEO利用 GeoHash 將二維的經(jīng)緯度轉(zhuǎn)換成字符串,來實現(xiàn)位置的劃分跟指定距離的查詢。Redis:從應(yīng)用到底層,一文幫你搞定

1.7、HyperLogLog

HyperLogLog :是一種概率數(shù)據(jù)結(jié)構(gòu),它使用概率算法來統(tǒng)計集合的近似基數(shù)。而它算法的最本源則是伯努利過程 + 分桶 + 調(diào)和平均數(shù)。具體實現(xiàn)可看 ?HyperLogLog 講解。

功能:誤差允許范圍內(nèi)做基數(shù)統(tǒng)計 (基數(shù)就是指一個集合中不同值的個數(shù)) 的時候非常有用,每個HyperLogLog的鍵可以計算接近2^64不同元素的基數(shù),而大小只需要12KB。錯誤率大概在0.81%。所以如果用做 UV 統(tǒng)計很合適。

HyperLogLog底層 一共分了 2^14 個桶,也就是 16384 個桶。每個(registers)桶中是一個 6 bit 的數(shù)組,這里有個騷操作就是一般人可能直接用一個字節(jié)當桶浪費2個bit空間,但是Redis底層只用6個然后通過前后拼接實現(xiàn)對內(nèi)存用到了極致,最終就是 16384*6/8/1024 = 12KB。

1.8、bitmap

BitMap 原本的含義是用一個比特位來映射某個元素的狀態(tài)。由于一個比特位只能表示 0 和 1 兩種狀態(tài),所以 BitMap 能映射的狀態(tài)有限,但是使用比特位的優(yōu)勢是能大量的節(jié)省內(nèi)存空間。

在 Redis 中BitMap 底層是基于字符串類型實現(xiàn)的,可以把 Bitmaps 想象成一個以比特位為單位的數(shù)組,數(shù)組的每個單元只能存儲0和1,數(shù)組的下標在 Bitmaps 中叫做偏移量,BitMap 的 offset 值上限 2^32 - 1。Redis:從應(yīng)用到底層,一文幫你搞定

  1. 用戶簽到

key = 年份:用戶id ?offset = (今天是一年中的第幾天) % (今年的天數(shù))

  1. 統(tǒng)計活躍用戶

使用日期作為 key,然后用戶 id 為 offset 設(shè)置不同offset為0 1 即可。

PS : Redis 它的通訊協(xié)議是基于TCP的應(yīng)用層協(xié)議 RESP(REdis Serialization Protocol)。

1.9、Bloom Filter

使用布隆過濾器得到的判斷結(jié)果:不存在的一定不存在,存在的不一定存在。

布隆過濾器 原理:

當一個元素被加入集合時,通過K個散列函數(shù)將這個元素映射成一個位數(shù)組中的K個點(有效降低沖突概率),把它們置為1。檢索時,我們只要看看這些點是不是都是1就知道集合中有沒有它了:如果這些點有任何一個為0,則被檢元素一定不在;如果都是1,則被檢元素很可能在。這就是布隆過濾器的基本思想。

想玩的話可以用Google的guava包玩耍一番。Redis:從應(yīng)用到底層,一文幫你搞定

1.10 發(fā)布訂閱

redis提供了發(fā)布、訂閱模式的消息機制,其中消息訂閱者與發(fā)布者不直接通信,發(fā)布者向指定的頻道(channel)發(fā)布消息,訂閱該頻道的每個客戶端都可以接收到消息。不過比專業(yè)的MQ(RabbitMQ RocketMQ ActiveMQ Kafka)相比不值一提,這個功能就算球了。Redis:從應(yīng)用到底層,一文幫你搞定

2、持久化

因為Redis數(shù)據(jù)在內(nèi)存,斷電既丟,因此持久化到磁盤是必須得有的,Redis提供了RDB跟AOF兩種模式。

2.1、RDB

RDB 持久化機制,是對 Redis 中的數(shù)據(jù)執(zhí)行周期性的持久化。更適合做冷備。優(yōu)點:

1、壓縮后的二進制文,適用于備份、全量復制,用于災(zāi)難恢復加載RDB恢復數(shù)據(jù)遠快于AOF方式,適合大規(guī)模的數(shù)據(jù)恢復。

2、如果業(yè)務(wù)對數(shù)據(jù)完整性和一致性要求不高,RDB是很好的選擇。數(shù)據(jù)恢復比AOF快。

缺點:

1、RDB是周期間隔性的快照文件,數(shù)據(jù)的完整性和一致性不高,因為RDB可能在最后一次備份時宕機了。

2、備份時占用內(nèi)存,因為Redis 在備份時會獨立fork一個子進程,將數(shù)據(jù)寫入到一個臨時文件(此時內(nèi)存中的數(shù)據(jù)是原來的兩倍哦),最后再將臨時文件替換之前的備份文件。所以要考慮到大概兩倍的數(shù)據(jù)膨脹性。

注意手動觸發(fā)及COW:

1、SAVE 直接調(diào)用 rdbSave ,阻塞 Redis 主進程,導致無法提供服務(wù)。2、BGSAVE 則 fork 出一個子進程,子進程負責調(diào)用 rdbSave ,在保存完成后向主進程發(fā)送信號告知完成。在BGSAVE 執(zhí)行期間仍可以繼續(xù)處理客戶端的請求。

3、Copy On Write 機制,備份的是開始那個時刻內(nèi)存中的數(shù)據(jù),只復制被修改內(nèi)存頁數(shù)據(jù),不是全部內(nèi)存數(shù)據(jù)。

4、Copy On Write 時如果父子進程大量寫操作會導致分頁錯誤。

Redis:從應(yīng)用到底層,一文幫你搞定

2.2、AOF

AOF 機制對每條寫入命令作為日志,以 append-only 的模式寫入一個日志文件中,因為這個模式是只追加的方式,所以沒有任何磁盤尋址的開銷,所以很快,有點像 Mysql 中的binlog。AOF更適合做熱備。

優(yōu)點:

AOF是一秒一次去通過一個后臺的線程fsync操作,數(shù)據(jù)丟失不用怕。

缺點:

1、對于相同數(shù)量的數(shù)據(jù)集而言,AOF文件通常要大于RDB文件。RDB 在恢復大數(shù)據(jù)集時的速度比 AOF 的恢復速度要快。

2、根據(jù)同步策略的不同,AOF在運行效率上往往會慢于RDB??傊?,每秒同步策略的效率是比較高的。

AOF整個流程分兩步:第一步是命令的實時寫入,不同級別可能有1秒數(shù)據(jù)損失。命令先追加到aof_buf然后再同步到AO磁盤,如果實時寫入磁盤會帶來非常高的磁盤IO,影響整體性能。

第二步是對aof文件的重寫,目的是為了減少AOF文件的大小,可以自動觸發(fā)或者手動觸發(fā)(BGREWRITEAOF),是Fork出子進程操作,期間Redis服務(wù)仍可用。Redis:從應(yīng)用到底層,一文幫你搞定

1、在重寫期間,由于主進程依然在響應(yīng)命令,為了保證最終備份的完整性;它依然會寫入舊的AOF中,如果重寫失敗,能夠保證數(shù)據(jù)不丟失。

2、為了把重寫期間響應(yīng)的寫入信息也寫入到新的文件中,因此也會為子進程保留一個buf,防止新寫的file丟失數(shù)據(jù)。

3、重寫是直接把當前內(nèi)存的數(shù)據(jù)生成對應(yīng)命令,并不需要讀取老的AOF文件進行分析、命令合并。

4、無論是 RDB 還是 AOF 都是先寫入一個臨時文件,然后通過rename完成文件的替換工作。

關(guān)于Fork的建議:

1、降低fork的頻率,比如可以手動來觸發(fā)RDB生成快照、與AOF重寫;

2、控制Redis最大使用內(nèi)存,防止fork耗時過長;

3、配置牛逼點,合理配置Linux的內(nèi)存分配策略,避免因為物理內(nèi)存不足導致fork失敗。

4、Redis在執(zhí)行BGSAVEBGREWRITEAOF命令時,哈希表的負載因子>=5,而未執(zhí)行這兩個命令時>=1。目的是盡量減少寫操作,避免不必要的內(nèi)存寫入操作。

5、哈希表的擴展因子:哈希表已保存節(jié)點數(shù)量 / 哈希表大小。因子決定了是否擴展哈希表。

2.3、恢復

啟動時會先檢查AOF(數(shù)據(jù)更完整)文件是否存在,如果不存在就嘗試加載RDB。Redis:從應(yīng)用到底層,一文幫你搞定

2.4、建議

既然單獨用RDB會丟失很多數(shù)據(jù)。單獨用AOF,數(shù)據(jù)恢復沒RDB來的快,所以出現(xiàn)問題了第一時間用RDB恢復,然后AOF做數(shù)據(jù)補全才說王道。

3、Redis為什么那么快

3.1、 基于內(nèi)存實現(xiàn):

數(shù)據(jù)都存儲在內(nèi)存里,相比磁盤IO操作快百倍,操作速率很快。

3.2、高效的數(shù)據(jù)結(jié)構(gòu):

Redis底層多種數(shù)據(jù)結(jié)構(gòu)支持不同的數(shù)據(jù)類型,比如HyperLogLog它連2個字節(jié)都不想浪費。

3.3、豐富而合理的編碼:

Redis底層提供了 豐富而合理的編碼 ?,五種數(shù)據(jù)類型根據(jù)長度及元素的個數(shù)適配不同的編碼格式。

1、String:自動存儲int類型,非int類型用raw編碼。

2、List:字符串長度且元素個數(shù)小于一定范圍使用 ziplist 編碼,否則轉(zhuǎn)化為 linkedlist 編碼。

3、Hash:hash 對象保存的鍵值對內(nèi)的鍵和值字符串長度小于一定值及鍵值對。

4、Set:保存元素為整數(shù)及元素個數(shù)小于一定范圍使用 intset 編碼,任意條件不滿足,則使用 hashtable 編碼。

5、Zset:保存的元素個數(shù)小于定值且成員長度小于定值使用 ziplist 編碼,任意條件不滿足,則使用 skiplist 編碼。

3.4、合適的線程模型:

I/O 多路復用模型同時監(jiān)聽客戶端連接,多線程是需要上下文切換的,對于內(nèi)存數(shù)據(jù)庫來說這點很致命。

Redis:從應(yīng)用到底層,一文幫你搞定


3.5、 Redis6.0后引入多線程提速:

要知道 讀寫網(wǎng)絡(luò)的read/write系統(tǒng)耗時 >> Redis運行執(zhí)行耗時,Redis的瓶頸主要在于網(wǎng)絡(luò)的 IO 消耗, 優(yōu)化主要有兩個方向:

1、提高網(wǎng)絡(luò) IO 性能,典型的實現(xiàn)比如使用 DPDK 來替代內(nèi)核網(wǎng)絡(luò)棧的方式?

2、使用多線程充分利用多核,典型的實現(xiàn)比如 Memcached。

協(xié)議棧優(yōu)化的這種方式跟 Redis 關(guān)系不大,支持多線程是一種最有效最便捷的操作方式。所以Redis支持多線程主要就是兩個原因:

1、可以充分利用服務(wù)器 CPU 資源,目前主線程只能利用一個核

2、多線程任務(wù)可以分攤 Redis 同步 IO 讀寫負荷

關(guān)于多線程須知:

  1. Redis 6.0 版本 默認多線程是關(guān)閉的 io-threads-do-reads no
  2. Redis 6.0 版本 開啟多線程后 線程數(shù)也要 謹慎設(shè)置。
  3. 多線程可以使得性能翻倍,但是多線程只是用來處理網(wǎng)絡(luò)數(shù)據(jù)的讀寫和協(xié)議解析, 執(zhí)行命令仍然是單線程順序執(zhí)行。

4、常見問題

4.1、緩存雪崩

Redis:從應(yīng)用到底層,一文幫你搞定雪崩定義:

Redis中大批量key在同一時間同時失效導致所有請求都打到了MySQL。而MySQL扛不住導致大面積崩塌。

雪崩解決方案:

1、緩存數(shù)據(jù)的過期時間加上個隨機值,防止同一時間大量數(shù)據(jù)過期現(xiàn)象發(fā)生。

2、如果緩存數(shù)據(jù)庫是分布式部署,將熱點數(shù)據(jù)均勻分布在不同搞得緩存數(shù)據(jù)庫中。

3、設(shè)置熱點數(shù)據(jù)永遠不過期。

4.2、緩存穿透

穿透定義:

緩存穿透 是 指緩存和數(shù)據(jù)庫中都沒有的數(shù)據(jù),比如ID默認>0,黑客一直 請求ID= -12的數(shù)據(jù)那么就會導致數(shù)據(jù)庫壓力過大,嚴重會擊垮數(shù)據(jù)庫。

穿透解決方案:

1、后端接口層增加 用戶鑒權(quán)校驗,參數(shù)做校驗等。

2、單個IP每秒訪問次數(shù)超過閾值直接拉黑IP,關(guān)進小黑屋1天,在獲取IP代理池的時候我就被拉黑過。

3、從緩存取不到的數(shù)據(jù),在數(shù)據(jù)庫中也沒有取到,這時也可以將key-value對寫為key-null 失效時間可以為15秒防止惡意攻擊

4、用Redis提供的 ?Bloom Filter 特性也OK。

4.3、緩存擊穿

Redis:從應(yīng)用到底層,一文幫你搞定

擊穿定義:

現(xiàn)象:大并發(fā)集中對這一個熱點key進行訪問,當這個Key在失效的瞬間,持續(xù)的大并發(fā)就穿破緩存,直接請求數(shù)據(jù)庫。

擊穿解決:

設(shè)置熱點數(shù)據(jù)永遠不過期 加上互斥鎖也能搞定了

4.4、雙寫一致性

雙寫:緩存數(shù)據(jù)庫均更新數(shù)據(jù),如何保證數(shù)據(jù)一致性?

1、先更新數(shù)據(jù)庫,再更新緩存

安全問題:線程A更新數(shù)據(jù)庫->線程B更新數(shù)據(jù)庫->線程B更新緩存->線程A更新緩存。導致臟讀。

業(yè)務(wù)場景:讀多寫少場景,頻繁更新數(shù)據(jù)庫而緩存根本沒用。更何況如果緩存是疊加計算后結(jié)果更浪費性能

2、先刪緩存,再更新數(shù)據(jù)庫

A 請求寫來更新緩存。

B 發(fā)現(xiàn)緩存不在去數(shù)據(jù)查詢舊值后寫入緩存。

A 將數(shù)據(jù)寫入數(shù)據(jù)庫,此時緩存跟數(shù)據(jù)庫不一致。

因此 FackBook 提出了 ?Cache Aside Pattern

失效:應(yīng)用程序先從cache取數(shù)據(jù),沒有得到,則從數(shù)據(jù)庫中取數(shù)據(jù),成功后,放到緩存中。

命中:應(yīng)用程序從cache中取數(shù)據(jù),取到后返回。

更新:先把數(shù)據(jù)存到數(shù)據(jù)庫中,成功后,再讓緩存失效。

4.5、腦裂

Redis:從應(yīng)用到底層,一文幫你搞定腦裂是指因為網(wǎng)絡(luò)原因,導致master節(jié)點、slave節(jié)點 和 sentinel集群處于不用的網(wǎng)絡(luò)分區(qū),此時因為sentinel集群無法感知到master的存在,所以將slave節(jié)點提升為master節(jié)點 此時存在兩個不同的master節(jié)點就像一個大腦分裂成了兩個。其實在Hadoop 、Spark集群中都會出現(xiàn)這樣的情況,只是解決方法不同而已(用ZK配合強制殺死)。

集群腦裂問題中,如果客戶端還在基于原來的master節(jié)點繼續(xù)寫入數(shù)據(jù)那么新的master節(jié)點將無法同步這些數(shù)據(jù),當網(wǎng)絡(luò)問題解決后sentinel集群將原先的master節(jié)點降為slave節(jié)點,此時再從新的master中同步數(shù)據(jù)將造成大量的數(shù)據(jù)丟失。

Redis處理方案是redis的配置文件中存在兩個參數(shù)

min-replicas-to-write?3??表示連接到master的最少slave數(shù)量
min-replicas-max-lag?10??表示slave連接到master的最大延遲時間

如果連接到master的slave數(shù)量 < 第一個參數(shù) 且 ping的延遲時間 <= 第二個參數(shù)那么master就會拒絕寫請求,配置了這兩個參數(shù)后如果發(fā)生了集群腦裂則原先的master節(jié)點接收到客戶端的寫入請求會拒絕就可以減少數(shù)據(jù)同步之后的數(shù)據(jù)丟失。

4.6、事務(wù)

MySQL?中的事務(wù)還是挺多道道的還要,而在Redis中的事務(wù)只要有如下三步:Redis:從應(yīng)用到底層,一文幫你搞定關(guān)于事務(wù)具體結(jié)論:

1、redis事務(wù)就是一次性、順序性、排他性的執(zhí)行一個隊列中的一系列命令?!?

2、Redis事務(wù)沒有隔離級別的概念:批量操作在發(fā)送 EXEC 命令前被放入隊列緩存,并不會被實際執(zhí)行,也就不存在事務(wù)內(nèi)的查詢要看到事務(wù)里的更新,事務(wù)外查詢不能看到。

3、Redis不保證原子性:Redis中單條命令是原子性執(zhí)行的,但事務(wù)不保證原子性。

4、Redis編譯型錯誤事務(wù)中所有代碼均不執(zhí)行,指令使用錯誤。運行時異常是錯誤命令導致異常,其他命令可正常執(zhí)行。

5、watch指令類似于樂觀鎖,在事務(wù)提交時,如果watch監(jiān)控的多個KEY中任何KEY的值已經(jīng)被其他客戶端更改,則使用EXEC執(zhí)行事務(wù)時,事務(wù)隊列將不會被執(zhí)行。

4.7、正確開發(fā)步驟

上線前:Redis 高可用,主從+哨兵,Redis cluster,避免全盤崩潰。

上線時:本地 ehcache 緩存 + Hystrix 限流 + 降級,避免MySQL扛不住。上線后:Redis 持久化采用 RDB + AOF 來保證斷點后自動從磁盤上加載數(shù)據(jù),快速恢復緩存數(shù)據(jù)。

5、分布式鎖

日常開發(fā)中我們可以用 synchronized 、Lock ?實現(xiàn)并發(fā)編程。但是Java中的鎖只能保證在同一個JVM進程內(nèi)中執(zhí)行。如果在分布式集群環(huán)境下用鎖呢?日常一般有兩種選擇方案。

5.1、 Zookeeper實現(xiàn)分布式鎖

你需要知道一點基本zookeeper知識:

1、持久節(jié)點:客戶端斷開連接zk不刪除persistent類型節(jié)點 2、臨時節(jié)點:客戶端斷開連接zk刪除ephemeral類型節(jié)點 3、順序節(jié)點:節(jié)點后面會自動生成類似0000001的數(shù)字表示順序 4、節(jié)點變化的通知:客戶端注冊了監(jiān)聽節(jié)點變化的時候,會調(diào)用回調(diào)方法

大致流程如下,其中注意每個節(jié)點監(jiān)控它前面那個節(jié)點狀態(tài),從而避免羊群效應(yīng)。關(guān)于模板代碼百度即可。Redis:從應(yīng)用到底層,一文幫你搞定缺點:

頻繁的創(chuàng)建刪除節(jié)點,加上注冊watch事件,對于zookeeper集群的壓力比較大,性能也比不上Redis實現(xiàn)的分布式鎖。

5.2、 Redis實現(xiàn)分布式鎖

本身原理也比較簡單,Redis 自身就是一個單線程處理器,具備互斥的特性,通過setNX,exist等命令就可以完成簡單的分布式鎖,處理好超時釋放鎖的邏輯即可。

SETNX

SETNX 是SET if Not eXists的簡寫,日常指令是SETNX key value,如果 key 不存在則set成功返回 1,如果這個key已經(jīng)存在了返回0。

SETEX

SETEX key seconds value 表達的意思是 將值 value 關(guān)聯(lián)到 key ,并將 key 的生存時間設(shè)為多少秒。如果 key 已經(jīng)存在,setex命令將覆寫舊值。并且 setex是一個原子性(atomic)操作。

加鎖:

一般就是用一個標識唯一性的字符串比如UUID 配合 SETNX 實現(xiàn)加鎖。

解鎖:

這里用到了LUA腳本,LUA可以保證是原子性的,思路就是判斷一下Key和入?yún)⑹欠裣嗟?,是的話就刪除,返回成功1,0就是失敗。

缺點:

這個鎖是無法重入的,且自己實心的話各種邊邊角角都要考慮到,所以了解個大致思路流程即可,工程化還是用開源工具包就行

5.3、 Redisson實現(xiàn)分布式鎖

Redisson 是在Redis基礎(chǔ)上的一個服務(wù),采用了基于NIO的Netty框架,不僅能作為Redis底層驅(qū)動客戶端,還能將原生的RedisHash,List,Set,String,Geo,HyperLogLog等數(shù)據(jù)結(jié)構(gòu)封裝為Java里大家最熟悉的映射(Map),列表(List),集(Set),通用對象桶(Object Bucket),地理空間對象桶(Geospatial Bucket),基數(shù)估計算法(HyperLogLog)等結(jié)構(gòu)。

這里我們只是用到了關(guān)于分布式鎖的幾個指令,他的大致底層原理:Redis:從應(yīng)用到底層,一文幫你搞定

Redisson加鎖解鎖 大致流程圖如下:Redis:從應(yīng)用到底層,一文幫你搞定

6、Redis 過期策略和內(nèi)存淘汰策略

6.1、Redis的過期策略

Redis中 過期策略 通常有以下三種:

1、定時過期

每個設(shè)置過期時間的key都需要創(chuàng)建一個定時器,到過期時間就會立即對key進行清除。該策略可以立即清除過期的數(shù)據(jù),對內(nèi)存很友好;但是會占用大量的CPU資源去處理過期的數(shù)據(jù),從而影響緩存的響應(yīng)時間和吞吐量。

2、惰性過期

只有當訪問一個key時,才會判斷該key是否已過期,過期則清除。該策略可以最大化地節(jié)省CPU資源,卻對內(nèi)存非常不友好。極端情況可能出現(xiàn)大量的過期key沒有再次被訪問,從而不會被清除,占用大量內(nèi)存。

3、定期過期

每隔一定的時間,會掃描一定數(shù)量的數(shù)據(jù)庫的expires字典中一定數(shù)量的key,并清除其中已過期的key。該策略是前兩者的一個折中方案。通過調(diào)整定時掃描的時間間隔和每次掃描的限定耗時,可以在不同情況下使得CPU和內(nèi)存資源達到最優(yōu)的平衡效果。

expires字典會保存所有設(shè)置了過期時間的key的過期時間數(shù)據(jù),其中 key 是指向鍵空間中的某個鍵的指針,value是該鍵的毫秒精度的UNIX時間戳表示的過期時間。鍵空間是指該Redis集群中保存的所有鍵。

Redis采用的過期策略:惰性刪除 + 定期刪除。memcached采用的過期策略:惰性刪除。

6.2、6種內(nèi)存淘汰策略

Redis的內(nèi)存淘汰策略是指在Redis的用于緩存的內(nèi)存不足時,怎么處理需要新寫入且需要申請額外空間的數(shù)據(jù)。

1、volatile-lru:從已設(shè)置過期時間的數(shù)據(jù)集(server.db[i].expires)中挑選最近最少使用的數(shù)據(jù)淘汰?

2、volatile-ttl:從已設(shè)置過期時間的數(shù)據(jù)集(server.db[i].expires)中挑選將要過期的數(shù)據(jù)淘汰?

3、volatile-random:從已設(shè)置過期時間的數(shù)據(jù)集(server.db[i].expires)中任意選擇數(shù)據(jù)淘汰?

4、allkeys-lru:從數(shù)據(jù)集(server.db[i].dict)中挑選最近最少使用的數(shù)據(jù)淘汰?

5、allkeys-random:從數(shù)據(jù)集(server.db[i].dict)中任意選擇數(shù)據(jù)淘汰 6、no-enviction(驅(qū)逐):禁止驅(qū)逐數(shù)據(jù),不刪除的意思。

面試常問常考的也就是LRU了,大家熟悉的LinkedHashMap中也實現(xiàn)了LRU算法的,實現(xiàn)如下:

class?SelfLRUCache<K,?V>?extends?LinkedHashMap<K,?V>?{
????private?final?int?CACHE_SIZE;
????/**
?????*?傳遞進來最多能緩存多少數(shù)據(jù)
?????*?@param?cacheSize?緩存大小
?????*/

????public?SelfLRUCache(int?cacheSize)?{
??// true 表示讓 linkedHashMap 按照訪問順序來進行排序,最近訪問的放在頭部,最老訪問的放在尾部。
????????super((int)?Math.ceil(cacheSize?/?0.75)?+?1,?0.75f,?true);
????????CACHE_SIZE?=?cacheSize;
????}
????@Override
????protected?boolean?removeEldestEntry(Map.Entry?eldest)?{
????????//?當 map中的數(shù)據(jù)量大于指定的緩存?zhèn)€數(shù)的時候,就自動刪除最老的數(shù)據(jù)。
????????return?size()?>?CACHE_SIZE;
????}
}

6.2、總結(jié)

Redis的內(nèi)存淘汰策略的選取并不會影響過期的key的處理。內(nèi)存淘汰策略用于處理內(nèi)存不足時的需要申請額外空間的數(shù)據(jù),過期策略用于處理過期的緩存數(shù)據(jù)。

7、Redis 集群高可用

單機問題有機器故障、容量瓶頸、QPS瓶頸。在實際應(yīng)用中,Redis的多機部署時候會涉及到redis主從復制、Sentinel哨兵模式Redis Cluster。

模式 優(yōu)點 缺點
單機版 架構(gòu)簡單,部署方便 機器故障、容量瓶頸、QPS瓶頸
主從復制 高可靠性,讀寫分離 故障恢復復雜,主庫的寫跟存受單機限制
Sentinel 哨兵 集群部署簡單,HA 原理繁瑣,slave存在資源浪費,不能解決讀寫分離問題
Redis Cluster 數(shù)據(jù)動態(tài)存儲solt,可擴展,高可用 客戶端動態(tài)感知后端變更,批量操作支持查

7.1、redis主從復制

該模式下 具有高可用性且讀寫分離, 會采用 增量同步全量同步 兩種機制。

7.1.1、全量同步

Redis:從應(yīng)用到底層,一文幫你搞定Redis全量復制一般發(fā)生在Slave初始化階段,這時Slave需要將Master上的所有數(shù)據(jù)都復制一份:

1、slave連接master,發(fā)送psync命令。

2、master接收到psync命名后,開始執(zhí)行bgsave命令生成RDB文件并使用緩沖區(qū)記錄此后執(zhí)行的所有寫命令。

3、master發(fā)送快照文件到slave,并在發(fā)送期間繼續(xù)記錄被執(zhí)行的寫命令。4、slave收到快照文件后丟棄所有舊數(shù)據(jù),載入收到的快照。

5、master快照發(fā)送完畢后開始向slave發(fā)送緩沖區(qū)中的寫命令。

6、slave完成對快照的載入,開始接收命令請求,并執(zhí)行來自master緩沖區(qū)的寫命令。

7.1.2、增量同步

也叫指令同步,就是從庫重放在主庫中進行的指令。Redis會把指令存放在一個環(huán)形隊列當中,因為內(nèi)存容量有限,如果備機一直起不來,不可能把所有的內(nèi)存都去存指令,也就是說,如果備機一直未同步,指令可能會被覆蓋掉。

Redis增量復制是指Slave初始化后開始正常工作時master發(fā)生的寫操作同步到slave的過程。增量復制的過程主要是master每執(zhí)行一個寫命令就會向slave發(fā)送相同的寫命令。

Redis:從應(yīng)用到底層,一文幫你搞定

7.1.3、Redis主從同步策略:

1、主從剛剛連接的時候,進行全量同步;全同步結(jié)束后,進行增量同步。當然,如果有需要,slave 在任何時候都可以發(fā)起全量同步。redis 策略是,無論如何,首先會嘗試進行增量同步,如不成功,要求從機進行全量同步。2、slave在同步master數(shù)據(jù)時候如果slave丟失連接不用怕,slave在重新連接之后丟失重補

3、一般通過主從來實現(xiàn)讀寫分離,但是如果master掛掉后如何保證Redis的 HA呢?引入Sentinel進行master的選擇。

7.2、高可用之哨兵模式

Redis:從應(yīng)用到底層,一文幫你搞定


Redis-sentinel ?本身是一個獨立運行的進程,一般sentinel集群 節(jié)點數(shù)至少三個且奇數(shù)個,它能監(jiān)控多個master-slave集群,sentinel節(jié)點發(fā)現(xiàn)master宕機后能進行自動切換。Sentinel可以監(jiān)視任意多個主服務(wù)器以及主服務(wù)器屬下的從服務(wù)器,并在被監(jiān)視的主服務(wù)器下線時,自動執(zhí)行故障轉(zhuǎn)移操作。這里需注意sentinel也有single-point-of-failure問題。大致羅列下哨兵用途:

集群監(jiān)控:循環(huán)監(jiān)控master跟slave節(jié)點。

消息通知:當它發(fā)現(xiàn)有redis實例有故障的話,就會發(fā)送消息給管理員?

故障轉(zhuǎn)移:這里分為主觀下線(單獨一個哨兵發(fā)現(xiàn)master故障了)??陀^下線(多個哨兵進行抉擇發(fā)現(xiàn)達到quorum數(shù)時候開始進行切換)。

配置中心:如果發(fā)生了故障轉(zhuǎn)移,它會通知將master的新地址寫在配置中心告訴客戶端。

7.3、Redis Cluster

RedisCluster是Redis的分布式解決方案,在3.0版本后推出的方案,有效地解決了Redis分布式的需求。Redis:從應(yīng)用到底層,一文幫你搞定

7.3.1、分區(qū)規(guī)則

Redis:從應(yīng)用到底層,一文幫你搞定常見的分區(qū)規(guī)則

  1. 節(jié)點取余:hash(key) % N
  2. 一致性哈希:一致性哈希環(huán)
  3. 虛擬槽哈希:CRC16[key] & 16383

RedisCluster采用了虛擬槽分區(qū)方式,具題的實現(xiàn)細節(jié)如下:

1、采用去中心化的思想,它使用虛擬槽solt分區(qū)覆蓋到所有節(jié)點上,取數(shù)據(jù)一樣的流程,節(jié)點之間使用輕量協(xié)議通信Gossip來減少帶寬占用所以性能很高,?

2、自動實現(xiàn)負載均衡與高可用,自動實現(xiàn)failover并且支持動態(tài)擴展,官方已經(jīng)玩到可以1000個節(jié)點 實現(xiàn)的復雜度低。

3、每個Master也需要配置主從,并且內(nèi)部也是采用哨兵模式,如果有半數(shù)節(jié)點發(fā)現(xiàn)某個異常節(jié)點會共同決定更改異常節(jié)點的狀態(tài)。

4、如果集群中的master沒有slave節(jié)點,則master掛掉后整個集群就會進入fail狀態(tài),因為集群的slot映射不完整。如果集群超過半數(shù)以上的master掛掉,集群都會進入fail狀態(tài)。

5、官方推薦 集群部署至少要3臺以上的master節(jié)點。

8、Redis 限流

經(jīng)常乘坐北京西二旗地鐵或者在北京西站乘坐的時候經(jīng)常會遇到一種情況就是如果人很多,地鐵的工作人員拿個小牌前面一檔讓你等會兒再檢票,這就是實際生活應(yīng)對人流量巨大的措施。

在開發(fā)高并發(fā)系統(tǒng)時,有三把利器用來保護系統(tǒng):緩存、降級限流。那么何為限流呢?顧名思義,限流就是限制流量,就像你寬帶包了1個G的流量,用完了就沒了。通過限流,我們可以很好地控制系統(tǒng)的qps,從而達到保護系統(tǒng)的目的。

1、基于Redis的setnx、zset

1.2、setnx

比如我們需要在10秒內(nèi)限定20個請求,那么我們在setnx的時候可以設(shè)置過期時間10,當請求的setnx數(shù)量達到20時候即達到了限流效果。

缺點:比如當統(tǒng)計1-10秒的時候,無法統(tǒng)計2-11秒之內(nèi),如果需要統(tǒng)計N秒內(nèi)的M個請求,那么我們的Redis中需要保持N個key等等問題。

1.3、zset

其實限流涉及的最主要的就是滑動窗口,上面也提到1-10怎么變成2-11。其實也就是起始值和末端值都各+1即可。我們可以將請求打造成一個zset數(shù)組,當每一次請求進來的時候,value保持唯一,可以用UUID生成,而score可以用當前時間戳表示,因為score我們可以用來計算當前時間戳之內(nèi)有多少的請求數(shù)量。而zset數(shù)據(jù)結(jié)構(gòu)也提供了range方法讓我們可以很輕易的獲取到2個時間戳內(nèi)有多少請求,

缺點:就是zset的數(shù)據(jù)結(jié)構(gòu)會越來越大。

2、漏桶算法

漏桶算法思路:把水比作是請求,漏桶比作是系統(tǒng)處理能力極限,水先進入到漏桶里,漏桶里的水按一定速率流出,當流出的速率小于流入的速率時,由于漏桶容量有限,后續(xù)進入的水直接溢出(拒絕請求),以此實現(xiàn)限流。Redis:從應(yīng)用到底層,一文幫你搞定

3、令牌桶算法

令牌桶算法的原理:可以理解成醫(yī)院的掛號看病,只有拿到號以后才可以進行診病。

Redis:從應(yīng)用到底層,一文幫你搞定細節(jié)流程大致:

1、所有的請求在處理之前都需要拿到一個可用的令牌才會被處理

2、根據(jù)限流大小,設(shè)置按照一定的速率往桶里添加令牌。

3、設(shè)置桶最大可容納值,當桶滿時新添加的令牌就被丟棄或者拒絕。

4、請求達到后首先要獲取令牌桶中的令牌,拿著令牌才可以進行其他的業(yè)務(wù)邏輯,處理完業(yè)務(wù)邏輯之后,將令牌直接刪除。

5、令牌桶有最低限額,當桶中的令牌達到最低限額的時候,請求處理完之后將不會刪除令牌,以此保證足夠的限流。

工程化:

1、自定義注解、aop、Redis + Lua 實現(xiàn)限流。

2、推薦 guavaRateLimiter實現(xiàn)。

9、常見知識點

  1. 字符串模糊查詢時用 Keys可能導致線程阻塞,盡量用 scan指令進行無阻塞的取出數(shù)據(jù)然后去重下即可。
  2. 多個操作的情況下記得用 pipeLine把所有的命令一次發(fā)過去,避免頻繁的發(fā)送、接收帶來的網(wǎng)絡(luò)開銷,提升性能。
  3. bigkeys可以掃描redis中的大key,底層是使用scan命令去遍歷所有的鍵,對每個鍵根據(jù)其類型執(zhí)行STRLEN、LLEN、SCARD、HLEN、ZCARD這些命令獲取其長度或者元素個數(shù)。缺陷是線上試用并且個數(shù)多不一定空間大,
  4. 線上應(yīng)用記得開啟Redis慢查詢?nèi)罩九?,基本思路跟MySQL類似。
  5. Redis中因為內(nèi)存分配策略跟增刪數(shù)據(jù)是會導致 內(nèi)存碎片,你可以重啟服務(wù)也可以執(zhí)行 activedefrag yes進行內(nèi)存重新整理來解決此問題。 Redis:從應(yīng)用到底層,一文幫你搞定

1、Ratio >1 表明有內(nèi)存碎片,越大表明越多嚴重。

2、Ratio?< 1 表明正在使用虛擬內(nèi)存,虛擬內(nèi)存其實就是硬盤,性能比內(nèi)存低得多,這是應(yīng)該增強機器的內(nèi)存以提高性能。

3、一般來說,mem_fragmentation_ratio的數(shù)值在1 ~ 1.5之間是比較健康的。

特別推薦一個分享架構(gòu)+算法的優(yōu)質(zhì)內(nèi)容,還沒關(guān)注的小伙伴,可以長按關(guān)注一下:

Redis:從應(yīng)用到底層,一文幫你搞定

Redis:從應(yīng)用到底層,一文幫你搞定

Redis:從應(yīng)用到底層,一文幫你搞定

長按訂閱更多精彩▼

Redis:從應(yīng)用到底層,一文幫你搞定

如有收獲,點個在看,誠摯感謝

免責聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫毥谦F公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運行,同時企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風險,如企業(yè)系統(tǒng)復雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機 衛(wèi)星通信

要點: 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅持高質(zhì)量發(fā)展策略,塑強核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運營商 數(shù)字經(jīng)濟

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團)股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉