摘要:從代碼上看字典也是在哈希表基礎(chǔ)上再抽象了一層而已。在中,哈希表實際上就是數(shù)組鏈表的形式來構(gòu)建的。后,在哈希沖突時是將新的節(jié)點(diǎn)添加到鏈表的表尾。在對哈希表進(jìn)行擴(kuò)展或者收縮操作時,過程并不是一次性地完成的,而是漸進(jìn)式地完成的。
前言
只有光頭才能變強(qiáng)
最近在學(xué)Redis,我相信只要是接觸過Java開發(fā)的都會聽過Redis這么一個技術(shù)。面試也是非常高頻的一個知識點(diǎn),之前一直都是處于了解階段。秋招過后這段時間是沒有什么壓力的,所以打算系統(tǒng)學(xué)學(xué)Redis,這也算是我從零學(xué)習(xí)Redis的筆記吧。
本文力求講清每個知識點(diǎn),希望大家看完能有所收獲。
一、介紹一下Redis首先,肯定是去官網(wǎng)看看官方是怎么介紹Redis的啦。https://redis.io/topics/introduction
如果像我一樣,英語可能不太好的,可能看不太懂。沒事,咱們Chrome瀏覽器可以切換成中文的,中文是我們的母語,肯定沒啥壓力了。Eumm...
讀完之后,發(fā)現(xiàn)中文也就那樣了。
一大堆沒見過的技術(shù):lua(Lua腳本)、replication(復(fù)制)、Redis Sentinel(哨兵)、Redis Cluster(Redis 集群),當(dāng)然我們也會有看得懂的技術(shù):transactions(事務(wù))、different levels of on-disk persistence(數(shù)據(jù)持久化)、LRU eviction(LRU淘汰機(jī)制)..
至少官方介紹Redis的第一句應(yīng)該是可以很容易看懂:"Redis is an open source (BSD licensed),in-memory data structure store, used as a database,cache and message broker."
Redis是一個開源的,基于內(nèi)存的數(shù)據(jù)結(jié)構(gòu)存儲,可用作于數(shù)據(jù)庫、緩存、消息中間件。
從官方的解釋上,我們可以知道:Redis是基于內(nèi)存,支持多種數(shù)據(jù)結(jié)構(gòu)。
從經(jīng)驗的角度上,我們可以知道:Redis常用作于緩存。
就我個人認(rèn)為:學(xué)習(xí)一種新技術(shù),先把握該技術(shù)整體的知識(思想),再扣細(xì)節(jié),這樣學(xué)習(xí)起來會比較輕松一些。所以我們先以“內(nèi)存”、“數(shù)據(jù)結(jié)構(gòu)”、“緩存”來對Redis入門。
1.1為什么要用Redis?從上面可知:Redis是基于內(nèi)存,常用作于緩存的一種技術(shù),并且Redis存儲的方式是以key-value的形式。
我們可以發(fā)現(xiàn)這不就是Java的Map容器所擁有的特性嗎,那為什么還需要Redis呢?
Java實現(xiàn)的Map是本地緩存,如果有多臺實例(機(jī)器)的話,每個實例都需要各自保存一份緩存,緩存不具有一致性
Redis實現(xiàn)的是分布式緩存,如果有多臺實例(機(jī)器)的話,每個實例都共享一份緩存,緩存具有一致性。
Java實現(xiàn)的Map不是專業(yè)做緩存的,JVM內(nèi)存太大容易掛掉的。一般用做于容器來存儲臨時數(shù)據(jù),緩存的數(shù)據(jù)隨著JVM銷毀而結(jié)束。Map所存儲的數(shù)據(jù)結(jié)構(gòu),緩存過期機(jī)制等等是需要程序員自己手寫的。
Redis是專業(yè)做緩存的,可以用幾十個G內(nèi)存來做緩存。Redis一般用作于緩存,可以將緩存數(shù)據(jù)保存在硬盤中,Redis重啟了后可以將其恢復(fù)。原生提供豐富的數(shù)據(jù)結(jié)構(gòu)、緩存過期機(jī)制等等簡單好用的功能。
參考資料:
為什么要用redis而不用map做緩存?https://segmentfault.com/q/1010000009106416
1.2為什么要用緩存?如果我們的網(wǎng)站出現(xiàn)了性能問題(訪問時間慢),按經(jīng)驗來說,一般是由于數(shù)據(jù)庫撐不住了。因為一般數(shù)據(jù)庫的讀寫都是要經(jīng)過磁盤的,而磁盤的速度可以說是相當(dāng)慢的(相對內(nèi)存來說)
科普文:讓 CPU 告訴你硬盤和網(wǎng)絡(luò)到底有多慢https://zhuanlan.zhihu.com/p/24726196
如果學(xué)過Mybaits、Hibernate的同學(xué)就可以知道,它們有一級緩存、二級緩存這樣的功能(終究來說還是本地緩存)。目的就是為了:不用每次讀取的時候,都要查一次數(shù)據(jù)庫。
有了緩存之后,我們的訪問就變成這樣了:
二、Redis的數(shù)據(jù)結(jié)構(gòu)本文不會講述命令的使用方式,具體的如何使用可查詢API。
Redis 命令參考:http://doc.redisfans.com/
try Redis(不用安裝Redis即可體驗Redis命令):http://try.redis.io/
Redis支持豐富的數(shù)據(jù)結(jié)構(gòu),常用的有string、list、hash、set、sortset這幾種。學(xué)習(xí)這些數(shù)據(jù)結(jié)構(gòu)是使用Redis的基礎(chǔ)!
"Redis is written in ANSI C"-->Redis由C語言編寫
首先還是得聲明一下,Redis的存儲是以key-value的形式的。Redis中的key一定是字符串,value可以是string、list、hash、set、sortset這幾種常用的。
但要值得注意的是:Redis并沒有直接使用這些數(shù)據(jù)結(jié)構(gòu)來實現(xiàn)key-value數(shù)據(jù)庫,而是基于這些數(shù)據(jù)結(jié)構(gòu)創(chuàng)建了一個對象系統(tǒng)。
簡單來說:Redis使用對象來表示數(shù)據(jù)庫中的鍵和值。每次我們在Redis數(shù)據(jù)庫中新創(chuàng)建一個鍵值對時,至少會創(chuàng)建出兩個對象。一個是鍵對象,一個是值對象。
Redis中的每個對象都由一個redisObject結(jié)構(gòu)來表示:
typedef struct redisObject{ // 對象的類型 unsigned type 4:; // 對象的編碼格式 unsigned encoding:4; // 指向底層實現(xiàn)數(shù)據(jù)結(jié)構(gòu)的指針 void * ptr; //..... }robj;
簡單來說就是Redis對key-value封裝成對象,key是一個對象,value也是一個對象。每個對象都有type(類型)、encoding(編碼)、ptr(指向底層數(shù)據(jù)結(jié)構(gòu)的指針)來表示。
下面我就來說一下我們Redis常見的數(shù)據(jù)類型:string、list、hash、set、sortset。它們的底層數(shù)據(jù)結(jié)構(gòu)究竟是怎么樣的!
2.1SDS簡單動態(tài)字符串簡單動態(tài)字符串(Simple dynamic string,SDS)
Redis中的字符串跟C語言中的字符串,是有點(diǎn)差距的。
Redis使用sdshdr結(jié)構(gòu)來表示一個SDS值:
struct sdshdr{ // 字節(jié)數(shù)組,用于保存字符串 char buf[]; // 記錄buf數(shù)組中已使用的字節(jié)數(shù)量,也是字符串的長度 int len; // 記錄buf數(shù)組未使用的字節(jié)數(shù)量 int free; }
例子:
2.1.1使用SDS的好處SDS與C的字符串表示比較
sdshdr數(shù)據(jù)結(jié)構(gòu)中用len屬性記錄了字符串的長度。那么獲取字符串的長度時,時間復(fù)雜度只需要O(1)。
SDS不會發(fā)生溢出的問題,如果修改SDS時,空間不足。先會擴(kuò)展空間,再進(jìn)行修改!(內(nèi)部實現(xiàn)了動態(tài)擴(kuò)展機(jī)制)。
SDS可以減少內(nèi)存分配的次數(shù)(空間預(yù)分配機(jī)制)。在擴(kuò)展空間時,除了分配修改時所必要的空間,還會分配額外的空閑空間(free 屬性)。
SDS是二進(jìn)制安全的,所有SDS API都會以處理二進(jìn)制的方式來處理SDS存放在buf數(shù)組里的數(shù)據(jù)。
2.2鏈表對于鏈表而言,我們不會陌生的了。在大學(xué)期間肯定開過數(shù)據(jù)結(jié)構(gòu)與算法課程,鏈表肯定是講過的了。在Java中Linkedxxx容器底層數(shù)據(jù)結(jié)構(gòu)也是鏈表+[xxx]的。我們來看看Redis中的鏈表是怎么實現(xiàn)的:
使用listNode結(jié)構(gòu)來表示每個節(jié)點(diǎn):
typedef strcut listNode{ //前置節(jié)點(diǎn) strcut listNode *pre; //后置節(jié)點(diǎn) strcut listNode *pre; //節(jié)點(diǎn)的值 void *value; }listNode
使用listNode是可以組成鏈表了,Redis中使用list結(jié)構(gòu)來持有鏈表:
typedef struct list{ //表頭結(jié)點(diǎn) listNode *head; //表尾節(jié)點(diǎn) listNode *tail; //鏈表長度 unsigned long len; //節(jié)點(diǎn)值復(fù)制函數(shù) void *(*dup) (viod *ptr); //節(jié)點(diǎn)值釋放函數(shù) void (*free) (viod *ptr); //節(jié)點(diǎn)值對比函數(shù) int (*match) (void *ptr,void *key); }list
具體的結(jié)構(gòu)如圖:
2.2.1Redis鏈表的特性Redis的鏈表有以下特性:
無環(huán)雙向鏈表
獲取表頭指針,表尾指針,鏈表節(jié)點(diǎn)長度的時間復(fù)雜度均為O(1)
鏈表使用void *指針來保存節(jié)點(diǎn)值,可以保存各種不同類型的值
2.3哈希表聲明:《Redis設(shè)計與實現(xiàn)》里邊有“字典”這么一個概念,我個人認(rèn)為還是直接叫哈希表比較通俗易懂。從代碼上看:“字典”也是在哈希表基礎(chǔ)上再抽象了一層而已。
在Redis中,key-value的數(shù)據(jù)結(jié)構(gòu)底層就是哈希表來實現(xiàn)的。對于哈希表來說,我們也并不陌生。在Java中,哈希表實際上就是數(shù)組+鏈表的形式來構(gòu)建的。下面我們來看看Redis的哈希表是怎么構(gòu)建的吧。
在Redis里邊,哈希表使用dictht結(jié)構(gòu)來定義:
typedef struct dictht{ //哈希表數(shù)組 dictEntry **table; //哈希表大小 unsigned long size; //哈希表大小掩碼,用于計算索引值 //總是等于size-1 unsigned long sizemark; //哈希表已有節(jié)點(diǎn)數(shù)量 unsigned long used; }dictht
我們下面繼續(xù)寫看看哈希表的節(jié)點(diǎn)是怎么實現(xiàn)的吧:
typedef struct dictEntry { //鍵 void *key; //值 union { void *value; uint64_tu64; int64_ts64; }v; //指向下個哈希節(jié)點(diǎn),組成鏈表 struct dictEntry *next; }dictEntry;
從結(jié)構(gòu)上看,我們可以發(fā)現(xiàn):Redis實現(xiàn)的哈希表和Java中實現(xiàn)的是類似的。只不過Redis多了幾個屬性來記錄常用的值:sizemark(掩碼)、used(已有的節(jié)點(diǎn)數(shù)量)、size(大小)。
同樣地,Redis為了更好的操作,對哈希表往上再封裝了一層(參考上面的Redis實現(xiàn)鏈表),使用dict結(jié)構(gòu)來表示:
typedef struct dict { //類型特定函數(shù) dictType *type; //私有數(shù)據(jù) void *privdata; //哈希表 dictht ht[2]; //rehash索引 //當(dāng)rehash不進(jìn)行時,值為-1 int rehashidx; }dict; //----------------------------------- typedef struct dictType{ //計算哈希值的函數(shù) unsigned int (*hashFunction)(const void * key); //復(fù)制鍵的函數(shù) void *(*keyDup)(void *private, const void *key); //復(fù)制值得函數(shù) void *(*valDup)(void *private, const void *obj); //對比鍵的函數(shù) int (*keyCompare)(void *privdata , const void *key1, const void *key2) //銷毀鍵的函數(shù) void (*keyDestructor)(void *private, void *key); //銷毀值的函數(shù) void (*valDestructor)(void *private, void *obj); }dictType
所以,最后我們可以發(fā)現(xiàn),Redis所實現(xiàn)的哈希表最后的數(shù)據(jù)結(jié)構(gòu)是這樣子的:
從代碼實現(xiàn)和示例圖上我們可以發(fā)現(xiàn),Redis中有兩個哈希表:
ht[0]:用于存放真實的key-vlaue數(shù)據(jù)
ht[1]:用于擴(kuò)容(rehash)
Redis中哈希算法和哈希沖突跟Java實現(xiàn)的差不多,它倆差異就是:
Redis哈希沖突時:是將新節(jié)點(diǎn)添加在鏈表的表頭。
JDK1.8后,Java在哈希沖突時:是將新的節(jié)點(diǎn)添加到鏈表的表尾。
2.3.1rehash的過程下面來具體講講Redis是怎么rehash的,因為我們從上面可以明顯地看到,Redis是專門使用一個哈希表來做rehash的。這跟Java一次性直接rehash是有區(qū)別的。
在對哈希表進(jìn)行擴(kuò)展或者收縮操作時,reash過程并不是一次性地完成的,而是漸進(jìn)式地完成的。
Redis在rehash時采取漸進(jìn)式的原因:數(shù)據(jù)量如果過大的話,一次性rehash會有龐大的計算量,這很可能導(dǎo)致服務(wù)器一段時間內(nèi)停止服務(wù)。
Redis具體是rehash時這么干的:
(1:在字典中維持一個索引計數(shù)器變量rehashidx,并將設(shè)置為0,表示rehash開始。
(2:在rehash期間每次對字典進(jìn)行增加、查詢、刪除和更新操作時,除了執(zhí)行指定命令外;還會將ht[0]中rehashidx索引上的值rehash到ht[1],操作完成后rehashidx+1。
(3:字典操作不斷執(zhí)行,最終在某個時間點(diǎn),所有的鍵值對完成rehash,這時將rehashidx設(shè)置為-1,表示rehash完成
(4:在漸進(jìn)式rehash過程中,字典會同時使用兩個哈希表ht[0]和ht[1],所有的更新、刪除、查找操作也會在兩個哈希表進(jìn)行。例如要查找一個鍵的話,服務(wù)器會優(yōu)先查找ht[0],如果不存在,再查找ht[1],諸如此類。此外當(dāng)執(zhí)行新增操作時,新的鍵值對一律保存到ht[1],不再對ht[0]進(jìn)行任何操作,以保證ht[0]的鍵值對數(shù)量只減不增,直至變?yōu)榭毡怼?/p> 2.4跳躍表(shiplist)
跳躍表(shiplist)是實現(xiàn)sortset(有序集合)的底層數(shù)據(jù)結(jié)構(gòu)之一!
跳躍表可能對于大部分人來說不太常見,之前我在學(xué)習(xí)的時候發(fā)現(xiàn)了一篇不錯的文章講跳躍表的,建議大家先去看完下文再繼續(xù)回來閱讀:
漫畫算法:什么是跳躍表?http://blog.jobbole.com/111731/
Redis的跳躍表實現(xiàn)由zskiplist和zskiplistNode兩個結(jié)構(gòu)組成。其中zskiplist保存跳躍表的信息(表頭,表尾節(jié)點(diǎn),長度),zskiplistNode則表示跳躍表的節(jié)點(diǎn)。
按照慣例,我們來看看zskiplistNode跳躍表節(jié)點(diǎn)的結(jié)構(gòu)是怎么樣的:
typeof struct zskiplistNode { // 后退指針 struct zskiplistNode *backward; // 分值 double score; // 成員對象 robj *obj; // 層 struct zskiplistLevel { // 前進(jìn)指針 struct zskiplistNode *forward; // 跨度 unsigned int span; } level[]; } zskiplistNode;
zskiplistNode的對象示例圖(帶有不同層高的節(jié)點(diǎn)):
示例圖如下:
zskiplist的結(jié)構(gòu)如下:
typeof struct zskiplist { // 表頭節(jié)點(diǎn),表尾節(jié)點(diǎn) struct skiplistNode *header,*tail; // 表中節(jié)點(diǎn)數(shù)量 unsigned long length; // 表中最大層數(shù) int level; } zskiplist;
最后我們整個跳躍表的示例圖如下:
2.5整數(shù)集合(intset)整數(shù)集合是set(集合)的底層數(shù)據(jù)結(jié)構(gòu)之一。當(dāng)一個set(集合)只包含整數(shù)值元素,并且元素的數(shù)量不多時,Redis就會采用整數(shù)集合(intset)作為set(集合)的底層實現(xiàn)。
整數(shù)集合(intset)保證了元素是不會出現(xiàn)重復(fù)的,并且是有序的(從小到大排序),intset的結(jié)構(gòu)是這樣子的:
typeof struct intset { // 編碼方式 unit32_t encoding; // 集合包含的元素數(shù)量 unit32_t lenght; // 保存元素的數(shù)組 int8_t contents[]; } intset;
intset示例圖:
說明:雖然intset結(jié)構(gòu)將contents屬性聲明為int8_t類型的數(shù)組,但實際上contents數(shù)組并不保存任何int8_t類型的值,contents數(shù)組的真正類型取決于encoding屬性的值:
INTSET_ENC_INT16
INTSET_ENC_INT32
INTSET_ENC_INT64
從編碼格式的名字我們就可以知道,16,32,64編碼對應(yīng)能存放的數(shù)字范圍是不一樣的。16明顯最少,64明顯最大。
如果本來是INTSET_ENC_INT16的編碼,想要存放大于INTSET_ENC_INT16編碼能存放的整數(shù)值,此時就得編碼升級(從16升級成32或者64)。步驟如下:
1)根據(jù)新元素類型拓展整數(shù)集合底層數(shù)組的空間并為新元素分配空間。
2)將底層數(shù)組現(xiàn)有的所以元素都轉(zhuǎn)換成與新元素相同的類型,并將類型轉(zhuǎn)換后的元素放到正確的位上,需要維持底層數(shù)組的有序性質(zhì)不變。
3)將新元素添加到底層數(shù)組。
另外一提:只支持升級操作,并不支持降級操作。
2.6壓縮列表(ziplist)壓縮列表(ziplist)是list和hash的底層實現(xiàn)之一。如果list的每個都是小整數(shù)值,或者是比較短的字符串,壓縮列表(ziplist)作為list的底層實現(xiàn)。
壓縮列表(ziplist)是Redis為了節(jié)約內(nèi)存而開發(fā)的,是由一系列的特殊編碼的連續(xù)內(nèi)存塊組成的順序性數(shù)據(jù)結(jié)構(gòu)。
壓縮列表結(jié)構(gòu)圖例如下:
下面我們看看節(jié)點(diǎn)的結(jié)構(gòu)圖:
壓縮列表從表尾節(jié)點(diǎn)倒序遍歷,首先指針通過zltail偏移量指向表尾節(jié)點(diǎn),然后通過指向節(jié)點(diǎn)記錄的前一個節(jié)點(diǎn)的長度依次向前遍歷訪問整個壓縮列表。三、Redis中數(shù)據(jù)結(jié)構(gòu)的對象
再次看回這張圖,覺不覺得就很好理解了?
3.1字符串(stirng)對象在上面的圖我們知道string類型有三種編碼格式:
int:整數(shù)值,這個整數(shù)值可以使用long類型來表示
如果是浮點(diǎn)數(shù),那就用embstr或者raw編碼。具體用哪個就看這個數(shù)的長度了
embstr:字符串值,這個字符串值的長度小于39字節(jié)
raw:字符串值,這個字符串值的長度大于39字節(jié)
embstr和raw的區(qū)別:
raw分配內(nèi)存和釋放內(nèi)存的次數(shù)是兩次,embstr是一次
embstr編碼的數(shù)據(jù)保存在一塊連續(xù)的內(nèi)存里面
編碼之間的轉(zhuǎn)換:
int類型如果存的不再是一個整數(shù)值,則會從int轉(zhuǎn)成raw
embstr是只讀的,在修改的時候回從embstr轉(zhuǎn)成raw
3.2列表(list)對象在上面的圖我們知道list類型有兩種編碼格式:
ziplist:字符串元素的長度都小于64個字節(jié)&&總數(shù)量少于512個
linkedlist:字符串元素的長度大于64個字節(jié)||總數(shù)量大于512個
ziplist編碼的列表結(jié)構(gòu):
redis > RPUSH numbers 1 "three" 5 (integer) 3
linkedlist編碼的列表結(jié)構(gòu):
編碼之間的轉(zhuǎn)換:
原本是ziplist編碼的,如果保存的數(shù)據(jù)長度太大或者元素數(shù)量過多,會轉(zhuǎn)換成linkedlist編碼的。
3.3哈希(hash)對象在上面的圖我們知道hash類型有兩種編碼格式:
ziplist:key和value的字符串長度都小于64字節(jié)&&鍵值對總數(shù)量小于512
hashtable:key和value的字符串長度大于64字節(jié)||鍵值對總數(shù)量大于512
ziplist編碼的哈希結(jié)構(gòu):
hashtable編碼的哈希結(jié)構(gòu):
編碼之間的轉(zhuǎn)換:
原本是ziplist編碼的,如果保存的數(shù)據(jù)長度太大或者元素數(shù)量過多,會轉(zhuǎn)換成hashtable編碼的。
3.4集合(set)對象在上面的圖我們知道set類型有兩種編碼格式:
intset:保存的元素全都是整數(shù)&&總數(shù)量小于512
hashtable:保存的元素不是整數(shù)||總數(shù)量大于512
intset編碼的集合結(jié)構(gòu):
hashtable編碼的集合結(jié)構(gòu):
編碼之間的轉(zhuǎn)換:
原本是intset編碼的,如果保存的數(shù)據(jù)不是整數(shù)值或者元素數(shù)量大于512,會轉(zhuǎn)換成hashtable編碼的。
3.5有序集合(sortset)對象在上面的圖我們知道set類型有兩種編碼格式:
ziplist:元素長度小于64&&總數(shù)量小于128
skiplist:元素長度大于64||總數(shù)量大于128
ziplist編碼的有序集合結(jié)構(gòu):
skiplist編碼的有序集合結(jié)構(gòu):
有序集合(sortset)對象同時采用skiplist和哈希表來實現(xiàn):
skiplist能夠達(dá)到插入的時間復(fù)雜度為O(logn),根據(jù)成員查分值的時間復(fù)雜度為O(1)
編碼之間的轉(zhuǎn)換:
原本是ziplist編碼的,如果保存的數(shù)據(jù)長度大于64或者元素數(shù)量大于128,會轉(zhuǎn)換成skiplist編碼的。
3.6Redis對象一些細(xì)節(jié)
(1:服務(wù)器在執(zhí)行某些命令的時候,會先檢查給定的鍵的類型能否執(zhí)行指定的命令。
比如我們的數(shù)據(jù)結(jié)構(gòu)是sortset,但你使用了list的命令。這是不對的,服務(wù)器會檢查一下我們的數(shù)據(jù)結(jié)構(gòu)是什么才會進(jìn)一步執(zhí)行命令
(2:Redis的對象系統(tǒng)帶有引用計數(shù)實現(xiàn)的內(nèi)存回收機(jī)制。
對象不再被使用的時候,對象所占用的內(nèi)存會釋放掉
(3:Redis會共享值為0到9999的字符串對象
(4:對象會記錄自己的最后一次被訪問時間,這個時間可以用于計算對象的空轉(zhuǎn)時間。
最后本文主要講了一下Redis常用的數(shù)據(jù)結(jié)構(gòu),以及這些數(shù)據(jù)結(jié)構(gòu)的底層設(shè)計是怎么樣的。整體來說不會太難,因為這些數(shù)據(jù)結(jié)構(gòu)我們在學(xué)習(xí)的過程中多多少少都接觸過了,《Redis設(shè)計與實現(xiàn)》這本書寫得也足夠通俗易懂。
至于我們在使用的時候挑選哪些數(shù)據(jù)結(jié)構(gòu)作為存儲,可以簡單看看:
string-->簡單的key-value
list-->有序列表(底層是雙向鏈表)-->可做簡單隊列
set-->無序列表(去重)-->提供一系列的交集、并集、差集的命令
hash-->哈希表-->存儲結(jié)構(gòu)化數(shù)據(jù)
sortset-->有序集合映射(member-score)-->排行榜
如果大家有更好的理解方式或者文章有錯誤的地方還請大家不吝在評論區(qū)留言,大家互相學(xué)習(xí)交流~~~
參考博客:
Redis簡明教程http://bridgeforyou.cn/2018/05/19/Redis-Tutorial/
五旬大爺教你一窺redis之謎https://zhuanlan.zhihu.com/p/34762100
參考資料:
《Redis設(shè)計與實現(xiàn)》
《Redis實戰(zhàn)》
一個堅持原創(chuàng)的Java技術(shù)公眾號:Java3y,歡迎大家關(guān)注
原創(chuàng)技術(shù)文章導(dǎo)航:
文章的目錄導(dǎo)航(腦圖+海量視頻資源):https://github.com/ZhongFuCheng3y/3y
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/71936.html
摘要:前言只有光頭才能變強(qiáng)好的,今天我們要上鉑金段位了,如果還沒經(jīng)歷過青銅和白銀和黃金階段的,可以先去蹭蹭經(jīng)驗再回來從零單排學(xué)青銅從零單排學(xué)白銀從零單排學(xué)黃金這篇文章主要講的是主從復(fù)制。 前言 只有光頭才能變強(qiáng) 好的,今天我們要上鉑金段位了,如果還沒經(jīng)歷過青銅和白銀和黃金階段的,可以先去蹭蹭經(jīng)驗再回來: 從零單排學(xué)Redis【青銅】 從零單排學(xué)Redis【白銀】 從零單排學(xué)Redis【黃金...
摘要:當(dāng)被監(jiān)聽的準(zhǔn)備好執(zhí)行連接應(yīng)答讀取等等操作時,與操作相對應(yīng)的文件事件就會產(chǎn)生,根據(jù)文件事件來為關(guān)聯(lián)對應(yīng)的事件處理器,從而實現(xiàn)功能。服務(wù)器使用單線程單進(jìn)程的方式處理命令請求。 前言 只有光頭才能變強(qiáng) 好的,今天我們要上黃金段位了,如果還沒經(jīng)歷過青銅和白銀階段的,可以先去蹭蹭經(jīng)驗再回來: 從零單排學(xué)Redis【青銅】 從零單排學(xué)Redis【白銀】 看過相關(guān)Redis基礎(chǔ)的同學(xué)可以知道Re...
摘要:可以通過以下兩個配置盡量減少數(shù)據(jù)丟失的可能從零單排學(xué)鉑金三,敬請期待參考資料設(shè)計與實現(xiàn)實戰(zhàn)如果你覺得我寫得還不錯,了解一下堅持原創(chuàng)的技術(shù)公眾號。 前言 只有光頭才能變強(qiáng) 好的,今天我們要上【鉑金二】了,如果還沒有上鉑金的,趕緊先去蹭蹭經(jīng)驗再回來(不然不帶你上分了): 從零單排學(xué)Redis【青銅】 從零單排學(xué)Redis【白銀】 從零單排學(xué)Redis【黃金】 從零單排學(xué)Redis【鉑金一...
閱讀 2727·2021-11-24 10:44
閱讀 2047·2021-11-22 13:53
閱讀 2050·2021-09-30 09:47
閱讀 3770·2021-09-22 16:00
閱讀 2512·2021-09-08 09:36
閱讀 2368·2019-08-30 15:53
閱讀 2851·2019-08-30 15:48
閱讀 1078·2019-08-30 15:44