Redis總體

介紹

Redis是一個開源的高性能的key-value存儲系統。具備如下特色:java

一、Redis支持數據的持久化,能夠將內存中的數據保持在磁盤中,重啓的時候能夠再次加載進行使用。mysql

二、Redis不只僅支持簡單的key-value類型的數據,同時還提供list,set,sorted set,hash等數據結構的存儲。redis

三、Redis支持數據的備份,即master-slave模式的數據備份。sql

Redis優點:數據庫

一、性能極高 – Redis能讀的速度是110000次/s,寫的速度是81000次/s 。緩存

二、豐富的數據類型 – Redis支持二進制案例的 String, List, Hash, Set 及 Sorted Set 數據類型操做。安全

三、原子 – Redis的全部操做都是原子性的,同時Redis還支持對幾個操做全並後的原子性執行。服務器

四、豐富的特性 – Redis還支持 publish/subscribe, 通知, key 過時等等特性網絡

數據類型

string、hash、list、set、sorted set數據結構

內存管理

 

 

 
 

 

首先 Redis 內部使用一個 redisObject 對象來表示全部的 key 和 value,redisObject 最主要的信息如上圖所示:type  表明一個 value 對象具體是何種數據類型,encoding 是不一樣數據類型在 redis 內部的存儲方式,好比:type=string 表明 value 存儲的是一個普通字符串,那麼對應的 encoding 能夠是 raw 或者是 int,若是是 int 則表明實際 redis 內部是按數值型類存儲和表示這個字符串的,固然前提是這個字符串自己能夠用數值表示,好比:」123″ 「456」這樣的字符串。

內存調優

一、首先最重要的一點是不要開啓 Redis 的 VM 選項,即虛擬內存功能,這個原本是做爲 Redis 存儲超出物理內存數據的一種數據在內存與磁盤換入換出的一個持久化策略,可是其內存管理成本也很是的高,而且咱們後續會分析此種持久化策略並不成熟,因此要關閉 VM 功能,請檢查你的 redis.conf 文件中 vm-enabled 爲 no。

二、其次最好設置下 redis.conf 中的 maxmemory 選項,該選項是告訴 Redis 當使用了多少物理內存後就開始拒絕後續的寫入請求,該參數能很好的保護好你的 Redis 不會由於使用了過多的物理內存而致使 swap,最終嚴重影響性能甚至崩潰。

三、Redis 爲不一樣數據類型分別提供了一組參數來控制內存使用,咱們在前面詳細分析過 Redis Hash 是 value 內部爲一個 HashMap,若是該 Map 的成員數比較少,則會採用相似一維線性的緊湊格式來存儲該 Map,即省去了大量指針的內存開銷,這個參數控制對應在 redis.conf 配置文件中下面2項:

hash-max-zipmap-entries  64
hash-max-zipmap-value  512
hash-max-zipmap-entries

 含義是當 value 這個 Map 內部不超過多少個成員時會採用線性緊湊格式存儲,默認是64,即 value 內部有64個如下的成員就是使用線性緊湊存儲,超過該值自動轉成真正的 HashMap。

hash-max-zipmap-value 含義是當 value 這個 Map 內部的每一個成員值長度不超過多少字節就會採用線性緊湊存儲來節省空間。

 以上2個條件任意一個條件超過設置值都會轉換成真正的 HashMap,也就不會再節省內存了,那麼這個值是否是設置的越大越好呢,答案固然是否認的,HashMap 的優點就是查找和操做的時間複雜度都是 O(1) 的,而放棄 Hash 採用一維存儲則是 O(n) 的時間複雜度,若是成員數量不多,則影響不大,不然會嚴重影響性能,因此要權衡好這個值的設置,整體上仍是最根本的時間成本和空間成本上的權衡。

內存淘汰策略

Redis提供了下面幾種淘汰策略供用戶選擇,其中默認的策略爲noeviction策略:

  • noeviction:當內存使用達到閾值的時候,全部引發申請內存的命令會報錯。
  • allkeys-lru:在主鍵空間中,優先移除最近未使用的key。
  • volatile-lru:在設置了過時時間的鍵空間中,優先移除最近未使用的key。
  • allkeys-random:在主鍵空間中,隨機移除某個key。
  • volatile-random:在設置了過時時間的鍵空間中,隨機移除某個key。
  • volatile-ttl:在設置了過時時間的鍵空間中,具備更早過時時間的key優先移除

持久化

redis是一個支持持久化的內存數據庫,也就是說redis須要常常將內存中的數據同步到磁盤來保證持久化。

redis支持兩種持久化方式,一種是 Snapshotting(快照)也是默認方式,另外一種是Append-only file(aof)的方式。

snapshotting

快照是默認的持久化方式。這種方式是就是將內存中數據以快照的方式寫入到二進制文件中,默認的文件名爲dump.rdb。能夠經過配置設置自動作快照持久化的方式。咱們能夠配置redis在n秒內若是超過m個key被修改就自動作快照。也能夠命令行的方式讓redis進行snapshotting。

快照生成過程大體以下:

  1. redis調用fork,如今有了子進程和父進程;
  2. 父進程繼續處理client請求,子進程負責將內存內容寫入到臨時文件。因爲os的寫時複製機制(copy on write)父子進程會共享相同的物理頁面,當父進程處理寫請求時os會爲父進程要修改的頁面建立副本,而不是寫共享的頁面。因此子進程的地址空間內的數據是fork時刻整個數據庫的一個快照;
  3. 當子進程將快照寫入臨時文件完畢後,用臨時文件替換原來的快照文件,而後子進程退出。

同時snapshotting也有不足的,由於兩次快照操做之間是有時間間隔的,一旦數據庫出現問題,那麼快照文件中保存的數據並非全新的,從上次快照文件生成到Redis停機這段時間的數據所有丟掉了。若是業務對數據準確性要求極高的話,就得采用aof持久化機制了。

aof

比快照方式有更好的持久化性,是因爲在使用aof持久化方式時,redis會將每個收到的寫命令都經過write函數追加到文件中(默認是 appendonly.aof)。當redis重啓時會經過從新執行文件中保存的寫命令來在內存中重建整個數據庫的內容。固然因爲os會在內核中緩存 write作的修改,因此可能不是當即寫到磁盤上。這樣aof方式的持久化也仍是有可能會丟失部分修改。不過咱們能夠經過配置文件告訴redis咱們想要經過fsync函數強制os寫入到磁盤的時機。有三種方式以下(默認是:每秒fsync一次):

一、appendonly yes //啓用aof持久化方式  

 二、# appendfsync always //每次收到寫命令就當即強制寫入磁盤,最慢的,可是保證徹底的持久化,不推薦使用  

 三、appendfsync everysec //每秒鐘強制寫入磁盤一次,在性能和持久化方面作了很好的折中,推薦  

 四、# appendfsync no //徹底依賴os,性能最好,持久化沒保證  

aof 的方式也同時帶來了另外一個問題。持久化文件會變的愈來愈大。例如咱們調用incr test命令100次,文件中必須保存所有的100條命令,其實有99條都是多餘的。由於要恢復數據庫的狀態其實文件中保存一條set test 100就夠了。爲了壓縮aof的持久化文件。redis提供了bgrewriteaof命令。收到此命令redis將使用與快照相似的方式將內存中的數據 以命令的方式保存到臨時文件中,最後替換原來的文件。

bgrewriteaof命令執行過程以下:

  1. redis調用fork ,如今有父子兩個進程;
  2. 子進程根據內存中的數據庫快照,往臨時文件中寫入重建數據庫狀態的命令;
  3. 父進程繼續處理client請求,除了把寫命令寫入到原來的aof文件中。同時把收到的寫命令緩存起來。這樣就能保證若是子進程重寫失敗的話並不會出問題;
  4. 當子進程把快照內容寫入以命令方式寫到臨時文件中後,子進程發信號通知父進程。而後父進程把緩存的寫命令也寫入到臨時文件;
  5. 如今父進程可使用臨時文件替換老的aof文件,並重命名,後面收到的寫命令也開始往新的aof文件中追加。

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

線程體系

Redis能夠說是基於單線程模型的,由於對於客戶端的全部讀寫請求的處理,都由一個主線程串行地處理,所以多個客戶端同時對一個鍵進行寫操做不會有併發問題,可是除了客戶端讀寫請求以外還有一些比較耗時的操做,如持久化RDB文件,持久化AOF文件等等,這些操做不能放在主線程裏面處理,所以Redis會在適當的時候fork子進程來異步的處理這種任務。除了這些,Redis還有一組異步任務處理線程,用於處理不須要主線程同步處理的工做,整體上Redis的線程體系結構大體以下圖:

 

 

 

 

主從同步

Redis的主從複製功能很是強大,一個master能夠擁有多個slave,而一個slave又能夠擁有多個slave,如此下去,造成了強大的多級服務器集羣架構。下面是關於redis主從複製的一些特色:
1.master能夠有多個slave
2.除了多個slave連到相同的master外,slave也能夠鏈接其餘slave造成圖狀結構
3.主從複製不會阻塞master。也就是說當一個或多個slave與master進行初次同步數據時,master能夠繼續處理client發來的請求。相反slave在初次同步數據時則會阻塞不能處理client的請求。
4.主從複製能夠用來提升系統的可伸縮性,咱們能夠用多個slave 專門用於client的讀請求,好比sort操做可使用slave來處理。也能夠用來作簡單的數據冗餘
5.能夠在master禁用數據持久化,只須要註釋掉master 配置文件中的全部save配置,而後只在slave上配置數據持久化。

下面介紹下主從複製的過程
       當設置好slave服務器後,slave會創建和master的鏈接,而後發送sync命令。不管是第一次同步創建的鏈接仍是鏈接斷開後的從新連 接,master都會啓動一個後臺進程,將數據庫快照保存到文件中,同時master主進程會開始收集新的寫命令並緩存起來。後臺進程完成寫文件 後,master就發送文件給slave,slave將文件保存到磁盤上,而後加載到內存恢復數據庫快照到slave上。接着master就會把緩存的命 令轉發給slave。並且後續master收到的寫命令都會經過開始創建的鏈接發送給slave。從master到slave的同步數據的命令和從 client發送的命令使用相同的協議格式。當master和slave的鏈接斷開時slave能夠自動從新創建鏈接。若是master同時收到多個 slave發來的同步鏈接命令,只會使用啓動一個進程來寫數據庫鏡像,而後發送給全部slave。

redis的主從複製策略是經過其持久化的rdb文件來實現的,其過程是先dump出rdb文件,將rdb文件全量傳輸給slave,而後再將dump後的操做實時同步到slave中。

  1. Slave端在配置文件中添加了slaveof指令,因而Slave啓動時讀取配置文件,初始狀態爲REDIS_REPL_CONNECT;
  2. Slave端在定時任務serverCron(Redis內部的定時器觸發事件)中鏈接Master,發送sync命令,而後阻塞等待master發送回其內存快照文件(最新版的Redis已經不須要讓Slave阻塞);
  3. Master端收到sync命令簡單判斷是否有正在進行的內存快照子進程,沒有則當即開始內存快照,有則等待其結束,當快照完成後會將該文件發送給Slave端;
  4. Slave端接收Master發來的內存快照文件,保存到本地,待接收完成後,清空內存表,從新讀取Master發來的內存快照文件,重建整個內存表數據結構,並最終狀態置位爲 REDIS_REPL_CONNECTED狀態,Slave狀態機流轉完成;
  5. Master端在發送快照文件過程當中,接收的任何會改變數據集的命令都會暫時先保存在Slave網絡鏈接的發送緩存隊列裏(list數據結構),待快照完成後,依次發給Slave,以後收到的命令相同處理,並將狀態置位爲 REDIS_REPL_ONLINE。

從以上的複製過程當中能夠發現,Slave從庫在鏈接Master主庫時,Master會進行內存快照,而後把整個快照文件發給Slave,也就是沒有象MySQL那樣有複製位置的概念,即無增量複製,若是一個master鏈接多個slave,就會比較影響master性能了。

事務實現原理

Redis事務一般會使用MULTI,EXEC,WATCH等命令來完成,redis實現事務實現的機制與常見的關係型數據庫有很大的卻別,好比redis的事務不支持回滾,事務執行時會阻塞其它客戶端的請求執行。

Redis事務從開始到結束一般會經過三個階段:一、事務開始,二、命令入隊,三、命令執行。

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

與事務相關的狀態flag

#define REDIS_MULTI ( 1 << 3 )  
#define REDIS_DIRTY_EXEC ( 1 << 12 )
#define REDIS_DIRTY_CAS ( 1 << 5 )
  1. 客戶端redisClient中有一個名叫flags的成員,標識當前客戶端的狀態。
  2. 在聲明事務以前,咱們能夠經過watch命令對一個或多個key進行監視。若是在事務執行以前這些被監視的key被其餘命令修改,Redis將redisClient->flags設置爲REDIS_DIRTY_CAS標識。
  3. 使用multi命令能夠標識着一個事務的開始,此時redisClient進入事務狀態,其flags字段被設置爲REDIS_MULTI標識。
  4. 當客戶端進入事務狀態後,Redis服務器等待接收一個或多個命令,並把它們放入命令隊列中等待執行。若是某條命令在入隊過程當中發生錯誤,Redis會將redisClient的flags字段置爲REDIS_DIRTY_EXEC標識。
  5. 最後咱們經過exec命令執行事務,該命令將會檢查redisClient的flags標識,若是該標識爲REDIS_DIRTY_CAS或REDIS_DIRTY_EXEC,則事務執行失敗,不然Redis一次性執行事務中的多個命令,並將全部命令的結果集合到回覆隊列,再做爲 exec 命令的結果返回給客戶端。

緩存失效機制

一、延遲失效機制:也叫消極失效機制,延遲失效機制即當客戶端請求操做某個key的時候,Redis會對客戶端請求操做的key進行有效期檢查,若是key過時才進行相應的處理

二、主動失效機制:也叫積極失效機制,即服務端定時的去檢查失效的緩存,若是失效則進行相應的操做。

與memcached的比較

 
redis
memcached
 
redis
memcached
數據類型 支持多種數據類型 只支持簡單的k-v
數據持久化支持 支持數據持久化 全內存模式
數據一致性 提供了事務的功能,能夠保證一串 命令的原子性,中間不會被任何操做打斷 提供了cas命令,能夠保證多個併發訪問操做同一份數據的一致性問題
集羣 服務端分佈式 客戶端分佈式
內存管理 實時分配內存 預分配內存
相關文章
相關標籤/搜索