Redis是一種基於鍵值對(key-value)的NoSQL數據庫。java
與不少鍵值對數據庫不一樣的是,Redis中的值能夠是由string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合)等多種數據結構和算法組成,所以Redis能夠知足不少的應用場景。linux
並且由於Redis會將全部數據都存放在內存中,因此它的讀寫性能很是驚人。redis
不只如此,Redis還能夠將內存的數據利用快照(RDB)和日誌(AOF)的形式保存到硬盤上,這樣在發生相似斷電或者機器故障的時候,內存中的數據不會「丟失」。算法
(1)速度快。速度快的緣由:數據庫
(2)基於鍵值對的數據結構服務器。編程
與不少鍵值對數據庫不一樣的是,Redis中的值不只能夠是字符串,並且還能夠是具體的數據結構,它主要提供了5種數據結構:字符串、哈希、列表、集合、有序集合。這樣不只能便於在許多應用場景的開發,同時也可以提升開發效率。後端
(3)簡單穩定數組
(4)持久化瀏覽器
一般看,將數據放在內存中是不安全的,一旦發生斷電或者機器故障,重要的數據可能就會丟失,所以Redis提供了兩種持久化方式:RDB和AOF,便可以用兩種策略將內存的數據保存到硬盤中(如圖1-1所示),這樣就保證了數據的可持久性。緩存
(5)主從複製
Redis提供了複製功能,實現了多個相同數據的Redis副本。
(6)高可用和分佈式
Redis從2.8版本正式提供了高可用實現Redis Sentinel(哨兵模式),它可以保證Redis節點的故障發現和故障自動轉移。
Redis從3.0版本正式提供了分佈式實現Redis Cluster(集羣模式),它是Redis真正的分佈式實現,提供了高可用、讀寫和容量的擴展性。
(1)緩存。合理地使用緩存不只能夠加快數據的訪問速度,並且可以有效地下降後端數據源的壓力。
(2)排行榜系統。Redis提供了列表和有序集合數據結構,合理地使用這些數據結構能夠很方便地構建各類排行榜系統。
(3)計數器應用。Redis自然支持計數功能並且計數的性能也很是好。
(4)社交網絡。贊/踩、粉絲、共同好友/喜愛、推送、下拉刷新等是社交網站的必備功能,因爲社交網站訪問量一般比較大,並且傳統的關係型數據不太適合保存這種類型的數據,Redis提供的數據結構能夠相對比較容易地實現這些功能。
(5)消息隊列系統。消息隊列系統能夠說是一個大型網站的必備基礎組件,由於其具備業務解耦、非實時業務削峯等特性。Redis提供了發佈訂閱功能和阻塞隊列的功能,雖然和專業的消息隊列比還不夠足夠強大,可是對於通常的消息隊列功能基本能夠知足。
第1步:將redis的源碼包上傳到linux系統。
Alt+p打開sftp窗口:輸入put "F:/java/ziyuan/redis-3.0.0.tar.gz"
第2步:解壓:tar -zxvf redis-3.0.0.tar.gz
第3步:進行編譯。 cd到解壓後的目錄 輸入命令:make
第4步:進行安裝。 輸入命令:make install PREFIX=/usr/local/redis
啓動:redis-server (加上配置文件) [root@localhost bin]# ./redis-server redis.conf
鏈接Redis服務:redis-cli [root@localhost bin]# ./redis-cli
中止Redes服務:redis-cli shutdown [root@localhost bin]# ./redis-cli shutdown
keys * :將全部的鍵都輸出
dbsize :輸出鍵總數
exits key :檢查某個鍵是否存在,若是存在返回1,不存在返回0
del key :刪除某個鍵
expire key 時間 :爲某個鍵設置過時時間
ttl key :觀察某鍵的剩餘過時時間
type key :返回某鍵的數據結構類型,若是鍵不存在返回none
type命令實際返回的就是當前鍵的數據結構類型,它們分別是:string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合),但這些只是Redis對外的數據結構。
實際上每種數據結構都有本身底層的內部編碼實現,並且是多種實現,這樣Redis會在合適的場景選擇合適的內部編碼。
多種內部編碼實現能夠在不一樣場景下發揮各自的優點,例如ziplist比較節省內存,可是在列表元素比較多的狀況下,性能會有所降低,這時候Redis會根據配置選項將列表類型的內部實現轉換爲linkedlist。
(1)單線程模型:
Redis使用了單線程架構和I/O多路複用模型來實現高性能的內存數據庫服務。
由於Redis是單線程來處理命令的,因此一條命令從客戶端達到服務端不會馬上被執行,全部命令都會進入一個隊列中,而後逐個被執行。因此假若有多個客戶端命令,則這些命令的執行順序是不肯定的,可是能夠肯定不會有兩條命令被同時執行。
可是像發送命令、返回結果、命令排隊確定不像描述的這麼簡單,Redis使用了I/O多路複用技術來解決I/O的問題。
(2)爲何單線程號還能這麼快?
爲何Redis使用單線程模型會達到每秒萬級別的處理能力呢?能夠將其歸結爲三點:
第一,純內存訪問,Redis將全部數據放在內存中,內存的響應時長大約爲100納秒,這是Redis達到每秒萬級別訪問的重要基礎。
第二,非阻塞I/O,Redis使用epoll做爲I/O多路複用技術的實現,再加上Redis自身的事件處理模型將epoll中的鏈接、讀寫、關閉都轉換爲事件,不在網絡I/O上浪費過多的時間。
第三,單線程避免了線程切換和競態產生的消耗。
字符串類型的值實際能夠是字符串(簡單的字符串、複雜的字符串(例如JSON、XML))、數字(整數、浮點數),甚至是二進制(圖片、音頻、視頻),可是值最大不能超過512MB。
Redis會根據當前值的類型和長度決定使用哪一種內部編碼實現。
(1)緩存功能
下圖是比較典型的緩存使用場景,其中Redis做爲緩存層,MySQL做爲存儲層,絕大部分請求的數據都是從Redis中獲取。因爲Redis具備支撐高併發的特性,因此緩存一般能起到加速讀寫和下降後端壓力的做用。
首先從Redis中獲取用戶信息(僞代碼):
若是沒有從Redis獲取到用戶信息,須要從MySQL中進行獲取,並將結果回寫到Redis,添加1小時(3600秒)過時時間:(僞代碼)
(2)計數
例如使用Redis做爲視頻播放數計數的基礎組件,用戶每播放一次視頻,相應的視頻播放數就會自增1:
(3)共享sessio
一個分佈式Web服務將用戶的Session信息(例如用戶登陸信息)保存在各自服務器中,這樣會形成一個問題,出於負載均衡的考慮,分佈式服務會將用戶的訪問均衡到不一樣服務器上,用戶刷新一次訪問可能會發現須要從新登陸,這個問題是用戶沒法容忍的。
爲了解決這個問題,可使用Redis將用戶的Session進行集中管理,以下圖所示,在這種模式下只要保證Redis是高可用和擴展性的,每次用戶更新或者查詢登陸信息都直接從Redis中集中獲取。
(4)限速
不少應用出於安全的考慮,會在每次進行登陸時,讓用戶輸入手機驗證碼,從而肯定是不是用戶本人。可是爲了短信接口不被頻繁訪問,會限制用戶每分鐘獲取驗證碼的頻率,例如一分鐘不能超過5次。此功能可使用Redis來實現,下面的僞代碼給出了基本實現思路:
一、命令:
二、哈希類型的內部編碼:
哈希類型的內部編碼有兩種:
ziplist(壓縮列表):當哈希類型元素個數小於hash-max-ziplist-entries配置(默認512個)、同時全部值都小於hash-max-ziplist-value配置(默認64字節)時,Redis會使用ziplist做爲哈希的內部實現,ziplist使用更加緊湊的結構實現多個元素的連續存儲,因此在節省內存方面比hashtable更加優秀。
hashtable(哈希表):當哈希類型沒法知足ziplist的條件時,Redis會使用hashtable做爲哈希的內部實現,由於此時ziplist的讀寫效率會降低,而hashtable的讀寫時間複雜度爲O(1)。
列表(list)類型是用來存儲多個有序的字符串.
一、命令:
二、內部編碼
列表類型的內部編碼有兩種:
三、使用場景
(1)消息隊列
Redis的lpush+brpop命令組合便可實現阻塞隊列,生產者客戶端使用lrpush從列表左側插入元素,多個消費者客戶端使用brpop命令阻塞式的「搶」列表尾部的元素,多個客戶端保證了消費的負載均衡和高可用性。
(2)文章列表
每一個用戶有屬於本身的文章列表,現須要分頁展現文章列表。此時能夠考慮使用列表,由於列表不可是有序的,同時支持按照索引範圍獲取元素。
實際上列表的使用場景不少,在選擇時能夠參考如下口訣:
集合(set)類型也是用來保存多個的字符串元素,但和列表類型不同的是,集合中不容許有重複元素,而且集合中的元素是無序的,不能經過索引下標獲取元素。
一個集合最多能夠存儲2^32-1個元素。Redis除了支持集合內的增刪改查,同時還支持多個集合取交集、並集、差集,合理地使用好集合類型,能在實際開發中解決不少實際問題。
一、命令:
二、內部編碼:
三、使用場景:
集合類型比較典型的使用場景是標籤(tag)。例如一個用戶可能對娛樂、體育比較感興趣,另外一個用戶可能對歷史、新聞比較感興趣,這些興趣點就是標籤。
給用戶添加標籤:
它保留了集合不能有重複成員的特性,但不一樣的是,有序集合中的元素能夠排序。可是它和列表使用索引下標做爲排序依據不一樣的是,它給每一個元素設置一個分數(score)做爲排序的依據。
一、命令:
二、內部編碼:
三、使用場景:
有序集合比較典型的使用場景就是排行榜系統。例如視頻網站須要對用戶上傳的視頻作排行榜,榜單的維度多是多個方面的:按照時間、按照播放數量、按照得到的贊數。本節使用贊數這個維度,記錄天天用戶上傳視頻的排行榜。主要須要實現如下4個功能:
添加用戶贊數:zadd和zincrby
取消用戶贊數:zrem
展現獲取贊數最多的十個用戶:zrevrange
展現用戶信息以及用戶分數:zscore和zrank
Redis提供了幾個面向Redis數據庫的操做,它們分別是dbsize、select、flushdb/flushall命令。
(1) 切換數據庫:select dbIndex
許多關係型數據庫,例如MySQL支持在一個實例下有多個數據庫存在的,可是與關係型數據庫用字符來區分不一樣數據庫名不一樣,Redis只是用數字做爲多個數據庫的實現。Redis默認配置中是有16個數據庫。
例:selet 15 切換到15號數據庫
能不能像使用測試數據庫和正式數據庫同樣,把正式的數據放在0號數據庫,測試的數據庫放在1號數據庫,那麼二者在數據上就不會彼此受影響了。事實真有那麼好嗎?
Redis3.0中已經逐漸弱化這個功能,緣由:
- Redis是單線程的。若是使用多個數據庫,那麼這些數據庫仍然是使用一個CPU,彼此之間仍是會受到影響的。
- 多數據庫的使用方式,會讓調試和運維不一樣業務的數據庫變的困難,假若有一個慢查詢存在,依然會影響其餘數據庫,這樣會使得別的業務方定位問題很是的困難。
- 部分Redis的客戶端根本就不支持這種方式。即便支持,在開發的時候來回切換數字形式的數據庫,很容易弄亂。
若是要使用多個數據庫功能,徹底能夠在一臺機器上部署多個Redis實例,彼此用端口來作區分,由於現代計算機或者服務器一般是有多個CPU的。這樣既保證了業務之間不會受到影響,又合理地使用了CPU資源。
(2)flushdb/flushall
flushdb/flushall命令用於清除數據庫,二者的區別的是flushdb只清除當前數據庫,flushall會清除全部數據庫。
注意若是當前數據庫鍵值數量比較多,flushdb/flushall存在阻塞Redis的可能性。
許多存儲系統(例如MySQL)提供慢查詢日誌幫助開發和運維人員定位系統存在的慢操做。所謂慢查詢日誌就是系統在命令執行先後計算每條命令的執行時間,當超過預設閥值,就將這條命令的相關信息(例如:發生時間,耗時,命令的詳細信息)記錄下來,Redis也提供了相似的功能。如圖3-1所示,Redis客戶端執行一條命令分爲以下4個部分:
1)發送命令 2)命令排隊 3)命令執行 4)返回結果
慢查詢的兩個配置參數:slowlog-log-slower-than和slowlog-max-len
獲取慢查詢日誌:slow get
獲取慢查詢日誌列表當前的長度:slowlog len
慢查詢日誌重置:slowlog reset
Redis提供了redis-cli、redis-server、redis-benchmark等Shell工具。
啓動:redis-server (加上配置文件) [root@localhost bin]# ./redis-server redis.conf
鏈接Redis服務:redis-cli [root@localhost bin]# ./redis-cli
中止Redes服務:redis-cli shutdown [root@localhost bin]# ./redis-cli shutdown
redis-benchmark能夠爲Redis作基準性能測試:
-c(clients)選項表明客戶端的併發數量(默認是50)
-n(num)選項表明客戶端請求總量(默認是100000)
Redis客戶端執行一條命令分爲以下四個過程:1)發送命令 2)命令排隊 3)命令執行 4)返回結果。 其中1)+4)稱爲RTT(往返時間)
Redis提供了批量操做命令(例如mget、mset等),有效地節約RTT。但大部分命令是不支持批量操做的,例如要執行n次hgetall命令,並無mhgetall命令存在,須要消耗n次RTT。
Pipeline(流水線)機制能將一組Redis命令進行組裝,經過一次RTT傳輸給Redis,再將這組Redis命令的執行結果按順序返回給客戶端。
爲了保證多條命令組合的原子性,Redis提供了簡單的事務功能以及集成Lua腳原本解決這個問題。
事務表示一組動做,要麼所有執行,要麼所有不執行。例如在社交網站上用戶A關注了用戶B,那麼須要在用戶A的關注表中加入用戶B,而且在用戶B的粉絲表中添加用戶A,這兩個行爲要麼所有執行,要麼所有不執行,不然會出現數據不一致的狀況。
Redis提供了簡單的事務功能,將一組須要一塊兒執行的命令放到multi和exec兩個命令之間。multi命令表明事務開始,exec命令表明事務結束,它們之間的命令是原子順序執行的。
Redis提供了簡單的事務,之因此說它簡單,主要是由於它不支持事務中的回滾特性,同時沒法實現命令之間的邏輯關係計算。Lua腳本一樣能夠實現事務的相關功能,可是功能要強大不少。
Redis將Lua做爲腳本語言可幫助開發者定製本身的Redis命令。Lua語言提供了以下幾種數據類型:booleans(布爾)、numbers(數值)、strings(字符串)、tables(表格)。
在Redis中執行Lua腳本有兩種方法:eval和evalsha。
例:eval 'return "hello" .. KEYS[1] .. ARGV[1]' 1 redis word (此時KEYS[1]="redis",ARGV[1]="world",因此最終的返回結果是"hello redisworld"。)
若是Lua腳本較長,還可使用redis-cli--eval直接執行文件。
eval命令和--eval參數本質是同樣的,客戶端若是想執行Lua腳本,首先在客戶端編寫好Lua腳本代碼,而後把腳本做爲字符串發送給服務端,服務端會將執行結果返回給客戶端。
Lua可使用redis.call函數實現對Redis的訪問,例以下面代碼是Lua使用redis.call調用了Redis的get操做:
除此以外Lua還可使用redis.pcall函數實現對Redis的調用,redis.call和redis.pcall的不一樣在於,若是redis.call執行失敗,那麼腳本執行結束會直接返回錯誤,而redis.pcall會忽略錯誤繼續執行腳本,因此在實際開發中要根據具體的應用場景進行函數的選擇。
Lua腳本功能爲Redis開發和運維人員帶來以下三個好處:
Redis提供了4個命令實現對Lua腳本的管理:
Redis提供了Bitmaps這個「數據結構」能夠實現對位的操做。把數據結構加上引號主要由於:
下面說下Bitmaps的命令。假設將每一個獨立用戶是否訪問過網站存放在Bitmaps中,將訪問的用戶記作1,沒有訪問的用戶記作0,用偏移量做爲用戶的id:
(1)設置值:setbit key offset value (設置鍵的第offset個位的值(從0算起))
假設如今有20個用戶,userid=0,5,11,15,19的用戶對網站進行了訪問,那麼當前Bitmaps初始化結果以下圖所示:
(2)獲取值:getbit key offset (獲取鍵的第offset位的值(從0開始算))
(3)獲取Bitmaps指定範圍值爲1的個數:bitcount [start] [end]
(4)Bitmaps間的運算:bitop and | or | not | xor destkey key [key ...] (作多個Bitmaps的and(交集)、or(並集)、not(非)、xor(異或)操做並將結果保存在destkey中)
假設網站有1億用戶,天天獨立訪問的用戶有5千萬,若是天天用集合類型和Bitmaps分別存儲活躍用戶,這種狀況下使用Bitmaps能節省不少的內存空間,尤爲是隨着時間推移節省的內存仍是很是可觀的。
但假如該網站天天的獨立訪問用戶不多,例如只有10萬(大量的殭屍用戶),那麼二者的對好比下表所示,很顯然,這時候使用Bitmaps就不太合適了,由於基本上大部分位都是0。
Redis提供了基於「發佈/訂閱」模式的消息機制,此種模式下,消息發佈者和訂閱者不進行直接通訊,發佈者客戶端向指定的頻道(channel)發佈消息,訂閱該頻道的每一個客戶端均可以收到該消息。
命令:
使用場景:
聊天室、公告牌、服務之間利用消息解耦均可以使用發佈訂閱模式,下面以簡單的服務解耦進行說明。以下圖示,圖中有兩套業務,上面爲視頻管理系統,負責管理視頻信息;下面爲視頻服務面向客戶,用戶能夠經過各類客戶端(手機、瀏覽器、接口)獲取到視頻信息。
Redis是用單線程來處理多個客戶端的訪問,所以做爲Redis的開發和運維人員須要瞭解Redis服務端和客戶端的通訊協議,以及主流編程語言的Redis客戶端使用方法,同時還須要瞭解客戶端管理的相應API以及開發運維中可能遇到的問題。本章將對這些內容進行詳細分析,本章內容以下:
例如客戶端發送一條set hello world命令給服務端,按照RESP的標準,客戶端須要將其封裝爲以下格式(每行用\r\n分隔):
這樣Redis服務端可以按照RESP將其解析爲set hello world命令,執行後回覆的格式以下:+OK
Redis的返回結果類型分爲如下五種:
Jedis屬於Java的第三方開發包,在Java中獲取第三方開發包一般有兩種方式:
一般在實際項目中使用第二種方式,但若是隻是想測試一下Jedis,第一種方法也是能夠的。以Maven爲例子,在項目中加入下面的依賴便可:
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.8.2</version> </dependency>
//1. 生成一個Jedis對象,這個對象負責和指定Redis實例進行通訊。 初始化Jedis須要兩個參數:Redis實例的IP和端口 Jedis jedis = new Jedis("127.0.0.1", 6379); //2. jedis執行set操做 jedis.set("hello", "world"); //3. jedis執行get操做, value="world" String value = jedis.get("hello");
Jedis對於Redis五種數據結構的操做:
//-----------1.string------------ // 輸出結果:OK jedis.set("hello", "world"); // 輸出結果:world jedis.get("hello"); // 輸出結果:1 jedis.incr("counter"); //-----------2.hash--------------- jedis.hset("myhash", "f1", "v1"); jedis.hset("myhash", "f2", "v2"); // 輸出結果:{f1=v1, f2=v2} jedis.hgetAll("myhash"); //-----------3.list--------------- jedis.rpush("mylist", "1"); jedis.rpush("mylist", "2"); jedis.rpush("mylist", "3"); // 輸出結果:[1, 2, 3] jedis.lrange("mylist", 0, -1); //-----------4.set---------------- jedis.sadd("myset", "a"); jedis.sadd("myset", "b"); jedis.sadd("myset", "a"); // 輸出結果:[b, a] jedis.smembers("myset"); //------------5.zset---------------- jedis.zadd("myzset", 99, "tom"); jedis.zadd("myzset", 66, "peter"); jedis.zadd("myzset", 33, "james"); // 輸出結果:[[["james"],33.0], [["peter"],66.0], [["tom"],99.0]] jedis.zrangeWithScores("myzset", 0, -1);
客戶端鏈接Redis使用的是TCP協議,直連的方式每次須要創建TCP鏈接,而鏈接池的方式是能夠預先初始化好Jedis鏈接,因此每次只須要從Jedis鏈接池借用便可,而借用和歸還操做是在本地進行的,只有少許的併發同步開銷,遠遠小於新建TCP鏈接的開銷。另外直連的方式沒法限制Jedis對象的個數,在極端狀況下可能會形成鏈接泄露,而鏈接池的形式能夠有效的保護和控制資源的使用。下表給出兩種方式各自的優劣勢。
Jedis提供了JedisPool這個類做爲對Jedis的鏈接池。使用JedisPool操做Redis的代碼示例:
(1)Jedis鏈接池(一般JedisPool是單例的):
// common-pool鏈接池配置,這裏使用默認配置 GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); // 初始化Jedis鏈接池 JedisPool jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379);
(2)獲取Jedis對象再也不是直接生成一個Jedis對象進行直連,而是從鏈接池直接獲取,代碼以下:
Jedis jedis = null; try { // 1. 從鏈接池獲取jedis對象 jedis = jedisPool.getResource(); // 2. 執行操做 jedis.get("hello"); } catch (Exception e) { logger.error(e.getMessage(),e); } finally { if (jedis != null) { // 若是使用JedisPool,close操做不是關閉鏈接,表明歸還鏈接池 jedis.close(); } }
回顧:Pipeline(流水線)機制能將一組Redis命令進行組裝,經過一次RTT傳輸給Redis,再將這組Redis命令的執行結果按順序返回給客戶端。
Jedis支持Pipeline特性,咱們知道Redis提供了mget、mset方法,可是並無提供mdel方法,若是想實現這個功能,能夠藉助Pipeline來模擬批量刪除:
public void mdel(List<String> keys) { Jedis jedis = new Jedis("127.0.0.1"); // 1)生成pipeline對象 Pipeline pipeline = jedis.pipelined(); // 2)pipeline執行命令,注意此時命令並未真正執行 for (String key : keys) { pipeline.del(key); } // 3)執行命令 pipeline.sync(); }
Jedis中執行Lua腳本和redis-cli十分相似,Jedis提供了三個重要的函數實現Lua腳本的執行:
Object eval(String script, int keyCount, String... params) Object evalsha(String sha1, int keyCount, String... params) String scriptLoad(String script)
以一個最簡單的Lua腳本爲例子進行說明: return redis.call('get',KEYS[1])
在redis-cli中執行上面的Lua腳本,方法以下:
eval "return redis.call('get',KEYS[1])" 1 hello
在Jedis中執行,方法以下:
String key = "hello"; String script = "return redis.call('get',KEYS[1])"; Object result = jedis.eval(script, 1, key); System.out.println(result);
scriptLoad和evalsha函數要一塊兒使用,首先使用scriptLoad將腳本加載到Redis中,代碼以下:
String scriptSha = jedis.scriptLoad(script);
而後執行結果以下:
Stirng key = "hello"; Object result = jedis.evalsha(scriptSha, 1, key); System.out.println(result);
client list命令能列出與Redis服務端相連的全部客戶端鏈接信息。
Redis爲每一個客戶端分配了輸入緩衝區,它的做用是將客戶端發送的命令臨時保存,同時Redis從會輸入緩衝區拉取命令並執行,輸入緩衝區爲客戶端發送命令到Redis執行命令提供了緩衝功能,以下圖所示。
輸入緩衝使用不當會產生兩個問題:
Redis爲每一個客戶端分配了輸出緩衝區,它的做用是保存命令執行的結果返回給客戶端,爲Redis和客戶端交互返回結果提供緩衝。與輸入緩衝區不一樣的是,輸出緩衝區的容量能夠經過參數client-output-buffer-limit來進行設置,而且輸出緩衝區作得更加細緻,按照客戶端的不一樣分爲三種:普通客戶端、發佈訂閱客戶端、slave客戶端,以下圖所示。
和輸入緩衝區相同的是,輸出緩衝區也不會受到maxmemory的限制,若是使用不當一樣會形成maxmemory用滿產生的數據丟失、鍵值淘汰、OOM等狀況。
Redis支持RDB和AOF兩種持久化機制,持久化功能有效地避免因進程退出形成的數據丟失問題,當下次重啓時利用以前持久化的文件便可實現數據恢復。
RDB持久化是把當前進程數據生成快照保存到硬盤的過程。觸發RDB持久化過程分爲手動觸發和自動觸發:
(1)手動觸發分別對應save和bgsave命令:
顯然bgsave命令是針對save阻塞問題作的優化。所以Redis內部全部的涉及RDB的操做都採用bgsave的方式,而save命令已經廢棄。
bgsave命令的運做過程:
(2)自動觸發:
RDB的優勢:
RDB的缺點:
AOF(append only file)持久化:以獨立日誌的方式記錄每次寫命令,重啓時再從新執行AOF文件中的命令達到恢復數據的目的。AOF的主要做用是解決了數據持久化的實時性,目前已是Redis持久化的主流方式。
AOF默認是默認不開啓的,開啓AOF功能須要設置配置:appendonly yes。
AOF工做流程:
注:
1. AOF爲何把命令追加到aof_buf中?
Redis使用單線程響應命令,若是每次寫AOF文件命令都直接追加到硬盤,那麼性能徹底取決於當前硬盤負載。先寫入緩衝區aof_buf中,還有另外一個好處,Redis能夠提供多種緩衝區同步硬盤的策略,在性能和安全性方面作出平衡。
2. AOF緩衝區同步文件策略,由參數appendfsync控制:
appendfsync always #每次有數據修改發生時都會寫入AOF文件,這樣會嚴重下降Redis的速度
appendfsync everysec #每秒鐘同步一次,顯示地將多個寫命令同步到硬盤
appendfsync no #讓操做系統決定什麼時候進行同步
3. AOF文件重寫是把Redis進程內的數據轉化爲寫命令同步到新AOF文件的過程。重寫後的AOF文件爲何能夠變小?
1)進程內已經超時的數據再也不寫入文件。
2)舊的AOF文件含有無效命令,重寫使用進程內數據直接生成,這樣新的AOF文件只保留最終數據的寫入命令。
3)多條寫命令能夠合併爲一個,如:lpush list a、lpush list b、lpush list c能夠轉化爲:lpush list a b c。爲了防止單條命令過大形成客戶端緩衝區溢出,對於list、set、hash、zset等類型操做,以64個元素爲界拆分爲多條。
AOF重寫下降了文件佔用空間,除此以外,另外一個目的是:更小的AOF文件能夠更快地被Redis加載。
【注】若是同時配了RDB和AOF,優先加載AOF。