Redis分布式鎖是可重入的嗎?
不可重入,可重入鎖可以使用Redisson
redis與memcache差別
- 存儲(chǔ)方式
memcache 將數(shù)據(jù)全部放在內(nèi)存中,斷電后會(huì)掛掉,無法做到數(shù)據(jù)的持久化。
Redis采用RDB 與 AOF結(jié)合的形式可以做到數(shù)據(jù)的持久化。 - 數(shù)據(jù)支持類型
Memcache對數(shù)據(jù)類型支持相對簡單,只支持String類型的數(shù)據(jù)結(jié)構(gòu)
Redis支持多種數(shù)據(jù)結(jié)構(gòu),包括String,List,Hash,Set,Zset - Redis的存儲(chǔ)大小遠(yuǎn)大于Memcache
緩存數(shù)據(jù)一致性
先更新數(shù)據(jù)庫再刪除緩存
在此基礎(chǔ)上可以設(shè)置緩存過期時(shí)間,過期了再從數(shù)據(jù)庫中讀取
還可以建立刪除緩存重試機(jī)制。
- Redis緩存過期策略
- 惰性刪除:當(dāng)訪問redis中鍵值對時(shí)會(huì)判斷這個(gè)鍵值對是否過期,如果過期的話就刪除這個(gè)鍵值對
- 定期刪除:采用expire方法設(shè)置過期刪除時(shí)間,會(huì)對鍵值對進(jìn)行輪詢對過期鍵值對進(jìn)行釋放。
內(nèi)存淘汰機(jī)制

Redis基本數(shù)據(jù)類型底層
https://juejin.im/post/6844903863145742350
基本數(shù)據(jù)類型:String/ hash/ list/ set/ sorted set
每一種數(shù)據(jù)類型都有不同的內(nèi)部編碼
String
內(nèi)部編碼
- int:8個(gè)字節(jié)的長整形
- embstr:小于等于44個(gè)字節(jié)的字符串(redis3.2版本之前是39字節(jié),之后是44字節(jié))
- raw:大于44個(gè)字節(jié)的字符串
底層結(jié)構(gòu) SDS簡單動(dòng)態(tài)字符串
有三個(gè)變量記錄著已經(jīng)使用字節(jié)的數(shù)量,剩余未使用的數(shù)量,字符數(shù)組
struct sdshdr{
//記錄buf數(shù)組中已使用字節(jié)的數(shù)量
//等于 SDS 保存字符串的長度
int len;
//記錄 buf 數(shù)組中未使用字節(jié)的數(shù)量
int free;
//字節(jié)數(shù)組,用于保存字符串
char buf[];
}

raw類型和embstr類型對比
embstr編碼的結(jié)構(gòu):

raw編碼的結(jié)構(gòu):

embstr和raw都是由redisObject和sds組成的。不同的是:embstr的redisObject和sds是連續(xù)的,只需要使用malloc分配一次內(nèi)存;而raw需要為redisObject和sds分別分配內(nèi)存,即需要分配兩次內(nèi)存。
所有相比較而言,embstr少分配一次內(nèi)存,更方便。但embstr也有明顯的缺點(diǎn):如要增加長度,redisObject和sds都需要重新分配內(nèi)存。
list
內(nèi)部編碼
- ziplist:當(dāng)元素個(gè)數(shù)小于默認(rèn)設(shè)置的512個(gè)并且每一個(gè)元素的大小都小于64字節(jié) 會(huì)使用一塊連續(xù)的內(nèi)存存儲(chǔ)
- linkedlist
- quicklist(redis 3.2實(shí)現(xiàn)) 當(dāng)數(shù)據(jù)量多的時(shí)候會(huì)改成quicklist存儲(chǔ),結(jié)合了ziplist和linkedlist功能,將多個(gè)ziplist使用雙向指針串起來使用。
ziplist結(jié)構(gòu)
在較少的情況下,會(huì)使用一塊連續(xù)的內(nèi)存存儲(chǔ),所有元素彼此緊挨著一塊存儲(chǔ),這個(gè)結(jié)構(gòu)是 ziplist(壓縮列表)。在ziplist中有對應(yīng)的字段表示ziplist的信息。比如ziplist的個(gè)數(shù),最后一個(gè)值的位置。

quicklist結(jié)構(gòu)
將多個(gè)ziplist使用雙向鏈表進(jìn)行連接。

hash
內(nèi)部編碼
- ziplist:當(dāng)元素個(gè)數(shù)小于默認(rèn)設(shè)置的512個(gè)并且每一個(gè)元素的大小都小于64字節(jié)
- 字典dict(也稱為hashtable)
redis中的dict 結(jié)構(gòu)內(nèi)部包含兩個(gè) hashtable,通常情況下只有一個(gè) hashtable 是有值的。但是在 dict 擴(kuò)容縮容時(shí),需要分配新的 hashtable,然后進(jìn)行漸進(jìn)式搬遷,這時(shí)兩個(gè) hashtable 存儲(chǔ)的分別是舊的 hashtable 和新的 hashtable。待搬遷結(jié)束后,舊的 hashtable 被刪除,新的 hashtable 取而代之。
set
- intset:當(dāng)集合中的元素都為整數(shù)且元素個(gè)數(shù)小于512個(gè)時(shí)
- hashtable
inset結(jié)構(gòu)
typedef struct intset {
// 編碼方式
uint32_t encoding;
// 集合包含的元素?cái)?shù)量
uint32_t length;
// 保存元素的數(shù)組
int8_t contents[];
} intset;
Sorted set
- ziplist:元素?cái)?shù)量比較少的時(shí)候使用(元素?cái)?shù)量小于128個(gè),長度小于64個(gè)字節(jié))
- skiplist
skiplist

skiplist 跳躍表 查找、插入、刪除的時(shí)間復(fù)雜度與紅黑樹相似都為O(logN)
Skip List主要思想是將鏈表與二分查找相結(jié)合,它維護(hù)了一個(gè)多層級(jí)的鏈表結(jié)構(gòu)
(1) 由很多層結(jié)構(gòu)組成
(2) 每一層都是一個(gè)有序的鏈表
(3) 最底層(Level 1)的鏈表包含所有元素
(4) 如果一個(gè)元素出現(xiàn)在 Level i 的鏈表中,則它在 Level i 之下的鏈表也都會(huì)出現(xiàn)。
(5) 每個(gè)節(jié)點(diǎn)包含兩個(gè)指針,一個(gè)指向同一鏈表中的下一個(gè)元素,一個(gè)指向下面一層的元素。
查找一個(gè)節(jié)點(diǎn)的過程如下:
- 從頂層鏈表的頭部開始進(jìn)行遍歷,比較每一個(gè)節(jié)點(diǎn)的元素與目標(biāo)元素的大小。
- 如果當(dāng)前元素小于目標(biāo)元素,則繼續(xù)遍歷。
- 如果當(dāng)前元素等于目標(biāo)元素,返回該節(jié)點(diǎn)。
- 如果當(dāng)前元素大于目標(biāo)元素,移動(dòng)到前一個(gè)節(jié)點(diǎn)(必須小于等于目標(biāo)元素),然后跳躍到下一層繼續(xù)遍歷。
- 如果遍歷至鏈表尾部,跳躍到下一層繼續(xù)遍歷。
插入一個(gè)節(jié)點(diǎn):先確定該元素要占據(jù)的層數(shù) K,然后在接下來的每一層都插入該節(jié)點(diǎn)。
就插入一個(gè)節(jié)點(diǎn)是一個(gè)概率性的事件,該元素需要占據(jù)的層數(shù)完全是隨機(jī)性的概率事件。
對于刪除一個(gè)節(jié)點(diǎn),需要先找到節(jié)點(diǎn)所在的位置(位于最底層鏈表中的位置),之后再自底向上地刪除該節(jié)點(diǎn)在每一行中的冗余復(fù)制。
為什么不使用紅黑樹而使用skiplist?
因?yàn)閞edis是單線程的,選用跳躍表的原因僅僅是因?yàn)樘S表的實(shí)現(xiàn)相較于紅黑樹更加簡潔。