Redis介紹

介紹

Redis是一個開源的高性能的key-value存儲系統(tǒng)。具有以下特點:
1、Redis支持?jǐn)?shù)據(jù)的持久化,可以將內(nèi)存中的數(shù)據(jù)保持在磁盤中,重啟的時候可以再次加載進(jìn)行使用。
2、Redis不僅僅支持簡單的key-value類型的數(shù)據(jù),同時還提供list,set,sorted set,hash等數(shù)據(jù)結(jié)構(gòu)的存儲。
3、Redis支持?jǐn)?shù)據(jù)的備份,即master-slave模式的數(shù)據(jù)備份。
Redis優(yōu)勢:
1、性能極高 – Redis能讀的速度是110000次/s,寫的速度是81000次/s 。
2、豐富的數(shù)據(jù)類型 – Redis支持二進(jìn)制案例的 String, List, Hash, Set 及 Sorted Set 數(shù)據(jù)類型操作。
3、原子 – Redis的所有操作都是原子性的,同時Redis還支持對幾個操作全并后的原子性執(zhí)行。
4、豐富的特性 – Redis還支持 publish/subscribe, 通知, key 過期等等特性

數(shù)據(jù)類型

string、hash、list、set、sorted set

內(nèi)存管理

image.png

首先 Redis 內(nèi)部使用一個 redisObject 對象來表示所有的 key 和 value,redisObject 最主要的信息如上圖所示:type 代表一個 value 對象具體是何種數(shù)據(jù)類型,encoding 是不同數(shù)據(jù)類型在 redis 內(nèi)部的存儲方式,比如:type=string 代表 value 存儲的是一個普通字符串,那么對應(yīng)的 encoding 可以是 raw 或者是 int,如果是 int 則代表實際 redis 內(nèi)部是按數(shù)值型類存儲和表示這個字符串的,當(dāng)然前提是這個字符串本身可以用數(shù)值表示,比如:”123″ “456”這樣的字符串。

內(nèi)存調(diào)優(yōu)

1、首先最重要的一點是不要開啟 Redis 的 VM 選項,即虛擬內(nèi)存功能,這個本來是作為 Redis 存儲超出物理內(nèi)存數(shù)據(jù)的一種數(shù)據(jù)在內(nèi)存與磁盤換入換出的一個持久化策略,但是其內(nèi)存管理成本也非常的高,并且我們后續(xù)會分析此種持久化策略并不成熟,所以要關(guān)閉 VM 功能,請檢查你的 redis.conf 文件中 vm-enabled 為 no。
2、其次最好設(shè)置下 redis.conf 中的 maxmemory 選項,該選項是告訴 Redis 當(dāng)使用了多少物理內(nèi)存后就開始拒絕后續(xù)的寫入請求,該參數(shù)能很好的保護(hù)好你的 Redis 不會因為使用了過多的物理內(nèi)存而導(dǎo)致 swap,最終嚴(yán)重影響性能甚至崩潰。
3、Redis 為不同數(shù)據(jù)類型分別提供了一組參數(shù)來控制內(nèi)存使用,我們在前面詳細(xì)分析過 Redis Hash 是 value 內(nèi)部為一個 HashMap,如果該 Map 的成員數(shù)比較少,則會采用類似一維線性的緊湊格式來存儲該 Map,即省去了大量指針的內(nèi)存開銷,這個參數(shù)控制對應(yīng)在 redis.conf 配置文件中下面2項:

hash-max-zipmap-entries  64

hash-max-zipmap-value  512

hash-max-zipmap-entries

含義是當(dāng) value 這個 Map 內(nèi)部不超過多少個成員時會采用線性緊湊格式存儲,默認(rèn)是64,即 value 內(nèi)部有64個以下的成員就是使用線性緊湊存儲,超過該值自動轉(zhuǎn)成真正的 HashMap。
hash-max-zipmap-value 含義是當(dāng) value 這個 Map 內(nèi)部的每個成員值長度不超過多少字節(jié)就會采用線性緊湊存儲來節(jié)省空間。
以上2個條件任意一個條件超過設(shè)置值都會轉(zhuǎn)換成真正的 HashMap,也就不會再節(jié)省內(nèi)存了,那么這個值是不是設(shè)置的越大越好呢,答案當(dāng)然是否定的,HashMap 的優(yōu)勢就是查找和操作的時間復(fù)雜度都是 O(1) 的,而放棄 Hash 采用一維存儲則是 O(n) 的時間復(fù)雜度,如果成員數(shù)量很少,則影響不大,否則會嚴(yán)重影響性能,所以要權(quán)衡好這個值的設(shè)置,總體上還是最根本的時間成本和空間成本上的權(quán)衡。

內(nèi)存淘汰策略

Redis提供了下面幾種淘汰策略供用戶選擇,其中默認(rèn)的策略為noeviction策略:
noeviction:當(dāng)內(nèi)存使用達(dá)到閾值的時候,所有引起申請內(nèi)存的命令會報錯。
allkeys-lru:在主鍵空間中,優(yōu)先移除最近未使用的key。
volatile-lru:在設(shè)置了過期時間的鍵空間中,優(yōu)先移除最近未使用的key。
allkeys-random:在主鍵空間中,隨機移除某個key。
volatile-random:在設(shè)置了過期時間的鍵空間中,隨機移除某個key。
volatile-ttl:在設(shè)置了過期時間的鍵空間中,具有更早過期時間的key優(yōu)先移除

持久化

redis是一個支持持久化的內(nèi)存數(shù)據(jù)庫,也就是說redis需要經(jīng)常將內(nèi)存中的數(shù)據(jù)同步到磁盤來保證持久化。
redis支持兩種持久化方式,一種是 RDB(快照)也是默認(rèn)方式,另一種是Append-only file(aof)的方式。

RDB

快照是默認(rèn)的持久化方式。這種方式是就是將內(nèi)存中數(shù)據(jù)以快照的方式寫入到二進(jìn)制文件中,默認(rèn)的文件名為dump.rdb。可以通過配置設(shè)置自動做快照持久化的方式。我們可以配置redis在n秒內(nèi)如果超過m個key被修改就自動做快照。也可以命令行的方式讓redis進(jìn)行snapshotting。
快照生成過程大致如下:
redis調(diào)用fork,現(xiàn)在有了子進(jìn)程和父進(jìn)程;
父進(jìn)程繼續(xù)處理client請求,子進(jìn)程負(fù)責(zé)將內(nèi)存內(nèi)容寫入到臨時文件。由于os的寫時復(fù)制機制(copy on write)父子進(jìn)程會共享相同的物理頁面,當(dāng)父進(jìn)程處理寫請求時os會為父進(jìn)程要修改的頁面創(chuàng)建副本,而不是寫共享的頁面。所以子進(jìn)程的地址空間內(nèi)的數(shù)據(jù)是fork時刻整個數(shù)據(jù)庫的一個快照;
當(dāng)子進(jìn)程將快照寫入臨時文件完畢后,用臨時文件替換原來的快照文件,然后子進(jìn)程退出。

同時snapshotting也有不足的,因為兩次快照操作之間是有時間間隔的,一旦數(shù)據(jù)庫出現(xiàn)問題,那么快照文件中保存的數(shù)據(jù)并不是全新的,從上次快照文件生成到Redis停機這段時間的數(shù)據(jù)全部丟掉了。如果業(yè)務(wù)對數(shù)據(jù)準(zhǔn)確性要求極高的話,就得采用aof持久化機制了。

AOF

比快照方式有更好的持久化性,是由于在使用aof持久化方式時,redis會將每一個收到的寫命令都通過write函數(shù)追加到文件中(默認(rèn)是 appendonly.aof)。當(dāng)redis重啟時會通過重新執(zhí)行文件中保存的寫命令來在內(nèi)存中重建整個數(shù)據(jù)庫的內(nèi)容。當(dāng)然由于os會在內(nèi)核中緩存 write做的修改,所以可能不是立即寫到磁盤上。這樣aof方式的持久化也還是有可能會丟失部分修改。不過我們可以通過配置文件告訴redis我們想要通過fsync函數(shù)強制os寫入到磁盤的時機。有三種方式如下(默認(rèn)是:每秒fsync一次):
1、appendonly yes //啟用aof持久化方式
2、# appendfsync always //每次收到寫命令就立即強制寫入磁盤,最慢的,但是保證完全的持久化,不推薦使用
3、appendfsync everysec //每秒鐘強制寫入磁盤一次,在性能和持久化方面做了很好的折中,推薦
4、# appendfsync no //完全依賴os,性能最好,持久化沒保證
aof 的方式也同時帶來了另一個問題。持久化文件會變的越來越大。例如我們調(diào)用incr test命令100次,文件中必須保存全部的100條命令,其實有99條都是多余的。因為要恢復(fù)數(shù)據(jù)庫的狀態(tài)其實文件中保存一條set test 100就夠了。為了壓縮aof的持久化文件。redis提供了bgrewriteaof命令。收到此命令redis將使用與快照類似的方式將內(nèi)存中的數(shù)據(jù) 以命令的方式保存到臨時文件中,最后替換原來的文件。
bgrewriteaof命令執(zhí)行過程如下:
redis調(diào)用fork ,現(xiàn)在有父子兩個進(jìn)程;
子進(jìn)程根據(jù)內(nèi)存中的數(shù)據(jù)庫快照,往臨時文件中寫入重建數(shù)據(jù)庫狀態(tài)的命令;
父進(jìn)程繼續(xù)處理client請求,除了把寫命令寫入到原來的aof文件中。同時把收到的寫命令緩存起來。這樣就能保證如果子進(jìn)程重寫失敗的話并不會出問題;
當(dāng)子進(jìn)程把快照內(nèi)容寫入以命令方式寫到臨時文件中后,子進(jìn)程發(fā)信號通知父進(jìn)程。然后父進(jìn)程把緩存的寫命令也寫入到臨時文件;
現(xiàn)在父進(jìn)程可以使用臨時文件替換老的aof文件,并重命名,后面收到的寫命令也開始往新的aof文件中追加。

這兩種持久化方式有各自的特點,快照相對性能影響不大,但一旦崩潰,數(shù)據(jù)量丟失較大,而aof數(shù)據(jù)安全性較高,但性能影響較大,這就得根據(jù)業(yè)務(wù)特點自行選擇了。

線程體系

Redis可以說是基于單線程模型的,因為對于客戶端的所有讀寫請求的處理,都由一個主線程串行地處理,因此多個客戶端同時對一個鍵進(jìn)行寫操作不會有并發(fā)問題,但是除了客戶端讀寫請求之外還有一些比較耗時的操作,如持久化RDB文件,持久化AOF文件等等,這些操作不能放在主線程里面處理,因此Redis會在適當(dāng)?shù)臅r候fork子進(jìn)程來異步的處理這種任務(wù)。除了這些,Redis還有一組異步任務(wù)處理線程,用于處理不需要主線程同步處理的工作,總體上Redis的線程體系結(jié)構(gòu)大致如下圖:

image.png

主從同步

Redis的主從復(fù)制功能非常強大,一個master可以擁有多個slave,而一個slave又可以擁有多個slave,如此下去,形成了強大的多級服務(wù)器集群架構(gòu)。下面是關(guān)于redis主從復(fù)制的一些特點:
1.master可以有多個slave
2.除了多個slave連到相同的master外,slave也可以連接其他slave形成圖狀結(jié)構(gòu)
3.主從復(fù)制不會阻塞master。也就是說當(dāng)一個或多個slave與master進(jìn)行初次同步數(shù)據(jù)時,master可以繼續(xù)處理client發(fā)來的請求。相反slave在初次同步數(shù)據(jù)時則會阻塞不能處理client的請求。
4.主從復(fù)制可以用來提高系統(tǒng)的可伸縮性,我們可以用多個slave 專門用于client的讀請求,比如sort操作可以使用slave來處理。也可以用來做簡單的數(shù)據(jù)冗余
5.可以在master禁用數(shù)據(jù)持久化,只需要注釋掉master 配置文件中的所有save配置,然后只在slave上配置數(shù)據(jù)持久化。

下面介紹下主從復(fù)制的過程 當(dāng)設(shè)置好slave服務(wù)器后,slave會建立和master的連接,然后發(fā)送sync命令。無論是第一次同步建立的連接還是連接斷開后的重新連 接,master都會啟動一個后臺進(jìn)程,將數(shù)據(jù)庫快照保存到文件中,同時master主進(jìn)程會開始收集新的寫命令并緩存起來。后臺進(jìn)程完成寫文件 后,master就發(fā)送文件給slave,slave將文件保存到磁盤上,然后加載到內(nèi)存恢復(fù)數(shù)據(jù)庫快照到slave上。接著master就會把緩存的命 令轉(zhuǎn)發(fā)給slave。而且后續(xù)master收到的寫命令都會通過開始建立的連接發(fā)送給slave。從master到slave的同步數(shù)據(jù)的命令和從 client發(fā)送的命令使用相同的協(xié)議格式。當(dāng)master和slave的連接斷開時slave可以自動重新建立連接。如果master同時收到多個 slave發(fā)來的同步連接命令,只會使用啟動一個進(jìn)程來寫數(shù)據(jù)庫鏡像,然后發(fā)送給所有slave。
redis的主從復(fù)制策略是通過其持久化的rdb文件來實現(xiàn)的,其過程是先dump出rdb文件,將rdb文件全量傳輸給slave,然后再將dump后的操作實時同步到slave中。
1、Slave端在配置文件中添加了slaveof指令,于是Slave啟動時讀取配置文件,初始狀態(tài)為REDIS_REPL_CONNECT;
2、Slave端在定時任務(wù)serverCron(Redis內(nèi)部的定時器觸發(fā)事件)中連接Master,發(fā)送sync命令,然后阻塞等待master發(fā)送回其內(nèi)存快照文件(最新版的Redis已經(jīng)不需要讓Slave阻塞);
3、Master端收到sync命令簡單判斷是否有正在進(jìn)行的內(nèi)存快照子進(jìn)程,沒有則立即開始內(nèi)存快照,有則等待其結(jié)束,當(dāng)快照完成后會將該文件發(fā)送給Slave端;
4、Slave端接收Master發(fā)來的內(nèi)存快照文件,保存到本地,待接收完成后,清空內(nèi)存表,重新讀取Master發(fā)來的內(nèi)存快照文件,重建整個內(nèi)存表數(shù)據(jù)結(jié)構(gòu),并最終狀態(tài)置位為 REDIS_REPL_CONNECTED狀態(tài),Slave狀態(tài)機流轉(zhuǎn)完成;
5、Master端在發(fā)送快照文件過程中,接收的任何會改變數(shù)據(jù)集的命令都會暫時先保存在Slave網(wǎng)絡(luò)連接的發(fā)送緩存隊列里(list數(shù)據(jù)結(jié)構(gòu)),待快照完成后,依次發(fā)給Slave,之后收到的命令相同處理,并將狀態(tài)置位為 REDIS_REPL_ONLINE。

從以上的復(fù)制過程中可以發(fā)現(xiàn),Slave從庫在連接Master主庫時,Master會進(jìn)行內(nèi)存快照,然后把整個快照文件發(fā)給Slave,也就是沒有象MySQL那樣有復(fù)制位置的概念,即無增量復(fù)制,如果一個master連接多個slave,就會比較影響master性能了。

事務(wù)實現(xiàn)原理

Redis事務(wù)通常會使用MULTI,EXEC,WATCH等命令來完成,redis實現(xiàn)事務(wù)實現(xiàn)的機制與常見的關(guān)系型數(shù)據(jù)庫有很大的卻別,比如redis的事務(wù)不支持回滾,事務(wù)執(zhí)行時會阻塞其它客戶端的請求執(zhí)行。
Redis事務(wù)從開始到結(jié)束通常會通過三個階段:1、事務(wù)開始,2、命令入隊,3、命令執(zhí)行。

redis > MULTI
OK
redis > SET username huating
QUEUED
redis > SET password 161616
QUEUED
redis > GET username
redis > EXEC
1
) ok
2
) 
huating
3
) 
huating

與事務(wù)相關(guān)的狀態(tài)flag

#define REDIS_MULTI (1<<3)  

#define REDIS_DIRTY_EXEC (1<<12)

#define REDIS_DIRTY_CAS (1<<5)

客戶端redisClient中有一個名叫flags的成員,標(biāo)識當(dāng)前客戶端的狀態(tài)。
在聲明事務(wù)之前,我們可以通過watch命令對一個或多個key進(jìn)行監(jiān)視。如果在事務(wù)執(zhí)行之前這些被監(jiān)視的key被其他命令修改,Redis將redisClient->flags設(shè)置為REDIS_DIRTY_CAS標(biāo)識。
使用multi命令可以標(biāo)識著一個事務(wù)的開始,此時redisClient進(jìn)入事務(wù)狀態(tài),其flags字段被設(shè)置為REDIS_MULTI標(biāo)識。
當(dāng)客戶端進(jìn)入事務(wù)狀態(tài)后,Redis服務(wù)器等待接收一個或多個命令,并把它們放入命令隊列中等待執(zhí)行。如果某條命令在入隊過程中發(fā)生錯誤,Redis會將redisClient的flags字段置為REDIS_DIRTY_EXEC標(biāo)識。
最后我們通過exec命令執(zhí)行事務(wù),該命令將會檢查redisClient的flags標(biāo)識,如果該標(biāo)識為REDIS_DIRTY_CAS或REDIS_DIRTY_EXEC,則事務(wù)執(zhí)行失敗,否則Redis一次性執(zhí)行事務(wù)中的多個命令,并將所有命令的結(jié)果集合到回復(fù)隊列,再作為 exec 命令的結(jié)果返回給客戶端。

緩存時效機制

1、延遲時效機制:也叫消極失效機制,延遲失效機制即當(dāng)客戶端請求操作某個key的時候,Redis會對客戶端請求操作的key進(jìn)行有效期檢查,如果key過期才進(jìn)行相應(yīng)的處理
2、主動時效機制:也叫積極失效機制,即服務(wù)端定時的去檢查失效的緩存,如果失效則進(jìn)行相應(yīng)的操作。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,748評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,165評論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,595評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,633評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,435評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,943評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,035評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,175評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,713評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,599評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,788評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,303評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,034評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,412評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,664評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,408評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,747評論 2 370

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

  • (1)什么是redis? Redis 是一個基于內(nèi)存的高性能key-value數(shù)據(jù)庫。 (有空再補充,有理解錯誤或...
    走過路過_97c2閱讀 708評論 0 1
  • 1.1 資料 ,最好的入門小冊子,可以先于一切文檔之前看,免費。 作者Antirez的博客,Antirez維護(hù)的R...
    JefferyLcm閱讀 17,083評論 1 51
  • 本文將從Redis的基本特性入手,通過講述Redis的數(shù)據(jù)結(jié)構(gòu)和主要命令對Redis的基本能力進(jìn)行直觀介紹。之后概...
    kelgon閱讀 61,208評論 23 625
  • 陽光明媚,微風(fēng)習(xí)習(xí) 我就在這如詩的湖畔遇見你 你坐在湖邊似乎在等待又似乎沉浸在這湖畔的美麗之中 風(fēng)吹起你的發(fā)絲,我...
    三月白雪閱讀 128評論 0 0
  • 今天福大爺醉了。也是第一次見他喝醉,之前聽他說過大學(xué)的時候經(jīng)常喝酒,醉的最嚴(yán)重的一次直接被送到醫(yī)院。今天也被送進(jìn)了...
    茄富論閱讀 181評論 0 1