什么是Redis?
Redis是非關系型數據庫,是一個高性能的key-value數據庫,它是開源的,更是免費的。
Redis能做什么?
存儲數據
Redis的優點有哪些?
1.它支持存儲豐富的數據類型,比如:Sting,hash,set,List,zset等數據結構的存儲。
2.性能極高 – Redis能讀的速度是110000次/s,寫的速度是81000次/s。
3.原子 – Redis的所有操作都是原子性的,同時Redis還支持對幾個操作全並后的原子性執行。
這里解釋一下什么叫做原子性:操作不可再分,要么全部執行,要么全部不執行。
redis的原子性:
-
單個操作是原子性的
-
多個操作也支持事務,即原子性,通過
MULTI
和EXEC
指令包起來
這里不得不提一下redis的事物,redis的事物原理是這樣子的:
1.批量操作在發送EXEC命令前被放入隊列緩存
2.收到EXEC命令后進入事物執行,事物中任意命令執行失敗,其余的命令都不會被執行
3.在事物執行過程中,其他客戶端提交的命令不會插入到事物執行命令序列中
下面是redis事物的一個例子:redis 127.0.0.1:6379> MULTOK
redis 127.0.0.1:6379> SET book-name "Mastering C++ in 21 days" QUEUED redis 127.0.0.1:6379> GET book-name QUEUED redis 127.0.0.1:6379> SADD tag "C++" "Programming" "Mastering Series" QUEUED redis 127.0.0.1:6379> SMEMBERS tag QUEUED redis 127.0.0.1:6379> EXEC 1) OK 2) "Mastering C++ in 21 days" 3) (integer) 3 4) 1) "Mastering Series" 2) "C++" 3) "Programming"
它是以MULT開始一個事物,然后將多個命令入隊到事物中,最后由EXEC命令觸發事物,一並執行事物中的所有命令。
Redis事物的一些常用命令:
1.DISCARD
取消事務,放棄執行事務塊內的所有命令
2.EXEC
執行所有事務塊內的命令
3.MULTI
標記一個事務塊的開始
4.UNWATCH
取消 WATCH 命令對所有 key 的監視
5.WATCH key [key ...]
監視一個(或多個) key ,如果在事務執行之前這個(或這些) key 被其他命令所改動,那么事務將被打斷
4.豐富的特性 – Redis還支持 publish/subscribe, 通知, key 過期等等特性。
Redis與其他key-value存儲有什么不同?
-
Redis有着更為復雜的數據結構並且提供對他們的原子性操作,這是一個不同於其他數據庫的進化路徑。Redis的數據類型都是基於基本數據結構的同時對程序員透明,無需進行額外的抽象。
-
Redis運行在內存中但是可以持久化到磁盤,所以在對不同數據集進行高速讀寫時需要權衡內存,因為數據量不能大於硬件內存。在內存數據庫方面的另一個優點是,相比在磁盤上相同的復雜的數據結構,在內存中操作起來非常簡單,這樣Redis可以做很多內部復雜性很強的事情。同時,在磁盤格式方面他們是緊湊的以追加的方式產生的,因為他們並不需要進行隨機訪問。
Redis的命令有哪些?
INCR 命令將字符串值解析成整型,將其加一,最后將結果保存為新的字符串值,類似的命令有INCRBY, DECR 和 DECRBY。實際上他們在內部就是同一個命令,只是看上去有點兒不同。
使用EXISTS命令返回1或0標識給定key的值是否存在,使用DEL命令可以刪除key對應的值,DEL命令返回1或0標識值是被刪除(值存在)或者沒被刪除(key對應的值不存在)。
TYPE命令可以返回key對應的值的存儲類型
用EXPIRE來設置超時時間(也可以再次調用這個命令來改變超時時間,使用PERSIST命令去除超時時間 )。我們也可以在創建值的時候設置超時時間
LPUSH 命令可向list的左邊(頭部)添加一個新元素,而RPUSH命令可向list的右邊(尾部)添加一個新元素。最后LRANGE 命令可從list中取出一定范圍的元素
LRANGE 帶有兩個索引,一定范圍的第一個和最后一個元素。這兩個索引都可以為負來告知Redis從尾部開始計數,因此-1表示最后一個元素,-2表示list中的倒數第二個元素,以此類推
還有一個重要的命令是pop,它從list中刪除元素並同時返回刪除的值。可以在左邊或右邊操作
我們增加了三個元素,並彈出了三個元素,因此,在這最后 列表中的命令序列是空的,沒有更多的元素可以被彈出。如果我們嘗試彈出另一個元素,將會得到一個null
可以使用LTRIM把list從左邊截取指定長度。比如 mylist 1 2 3 4 5 ,ltrim mylist 0 2,結果 lrange mylist 0 -1 是 1 2 3
SADD key member1 [member2]
向集合添加一個或多個成員
SCARD key
獲取集合的成員數
SMEMBERS key
返回集合中的所有成員
SREM key member1 [member2]
移除集合中一個或多個成員
SPOP key
移除並返回集合中的一個隨機元素
Redis的復制
redis復制的特性:
1.同一個Master可以同步多個salves
2. Slave同樣可以接受其它Slaves的連接和同步請求,這樣可以有效的分載Master的同步壓力。因此我們可以將Redis的復制架構視為圖結構
3.Master Server是以非阻塞的方式為Slaves提供服務。所以在Master-Slave同步期間,客戶端仍然可以提交查詢或修改請求
4.Slave Server同樣是以非阻塞的方式完成數據同步。在同步期間,如果有客戶端提交查詢請求,Redis則返回同步之前的數據
5.為了分載Master的讀操作壓力,Slave服務器可以為客戶端提供只讀操作的服務,寫服務仍然必須由Master來完成。即便如此,系統的伸縮性還是得到了很大的提高
6.Master可以將數據保存操作交給Slaves完成,從而避免了在Master中要有獨立的進程來完成此操作
redis復制原理:
如果設置了一個slave,不管是在第一次鏈接還是重新鏈接master的時候,slave會發送一個同步命令 然后master開始后台保存,收集所有對修改數據的命令。當后台保存完成,master會將這個數據文件傳送到slave,然后保存在磁盤,加載到內存中;master接着發送收集到的所有的修改數據的命令,這好比一個流命令,是redis協議本身來實現的。
你可以自己通過遠程登錄來進行嘗試,當服務器在做一些工作並發送同步命令的時候鏈接到redis端口,你將會看到大量的數據傳輸,然后收到的每個命令會會顯示在遠程登錄的會話中。當 master和slave因一些故障當機時,slaves會自動的重鏈,如果master收到多個slave的同步請求,master會執行一個后台保存,以確保所有的slaves都是正常的。
當master和slave能夠維持鏈接,就會有一個完整的同步進行。
配置:
1. 同時啟動兩個Redis服務器,可以考慮在同一台機器上啟動兩個Redis服務器,分別監聽不同的端口,如6379和6380
2. 在Slave服務器上執行一下命令:
/> redis-cli -p 6380 //假設salve服務器的端口號是6380
redis 127.0.0.1:6380> slaveof 127.0.0.1 6379 //假設Master和Slave在同一台主機,Master的端口為6379
3.上面的方式只是保證了在執行slaveof命令之后,redis_6380成為了redis_6379的slave,一旦服務(redis_6380)重新啟動之后,他們之間的復制關系將終止。
如果希望長期保證這兩個服務器之間的Replication關系,可以在redis_6380的配置文件中做如下修改:
/> cd /etc/redis //切換Redis服務器配置文件所在的目錄。
/> ls
6379.conf 6380.conf
/> vi 6380.conf
將
# slaveof <masterip> <masterport>
改為
slaveof 127.0.0.1 6379
保存退出。
這樣就可以保證Redis_6380服務程序在每次啟動后都會主動建立與Redis_6379的Replication連接了。
Redis的持久化
什么是持久化?簡單來講就是將數據放到斷電后數據不會丟失的設備中,也就是我們通常理解的硬盤上
首先我們來看一下數據庫在進行寫操作時到底做了哪些事,主要有下面五個過程:
- 客戶端向服務端發送寫操作(數據在客戶端的內存中)。
- 數據庫服務端接收到寫請求的數據(數據在服務端的內存中)。
- 服務端調用write這個系統調用,將數據往磁盤上寫(數據在系統內存的緩沖區中)。
- 操作系統將緩沖區中的數據轉移到磁盤控制器上(數據在磁盤緩存中)。
- 磁盤控制器將數據寫到磁盤的物理介質中(數據真正落到磁盤上)。
1. RDB持久化:該機制是指在指定的時間間隔內將內存中的數據集快照寫入磁盤
也是默認的持久化方式,這種方式是就是將內存中數據以快照的方式寫入到二進制文件中,默認的文件名為dump.rdb。
可以通過配置設置自動做快照持久化的方式。我們可以配置redis在n秒內如果超過m個key被修改就自動做快照,下面是默認的快照保存配置
save 900 1 #900秒內如果超過1個key被修改,則發起快照保存 save 300 10 #300秒內容如超過10個key被修改,則發起快照保存 save 60 10000
#60秒內容如超過10000個key被修改,則發起快照保存
RDB文件保存過程
- redis調用fork,現在有了子進程和父進程。
- 父進程繼續處理client請求,子進程負責將內存內容寫入到臨時文件。由於os的寫時復制機制(copy on write)父子進程會共享相同的物理頁面,當父進程處理寫請求時os會為父進程要修改的頁面創建副本,而不是寫共享的頁面。所以子進程的地址空間內的數 據是fork時刻整個數據庫的一個快照。
- 當子進程將快照寫入臨時文件完畢后,用臨時文件替換原來的快照文件,然后子進程退出。
client 也可以使用save或者bgsave命令通知redis做一次快照持久化。save操作是在主線程中保存快照的,由於redis是用一個主線程來處理所有 client的請求,這種方式會阻塞所有client請求。所以不推薦使用。
另一點需要注意的是,每次快照持久化都是將內存數據完整寫入到磁盤一次,並不 是增量的只同步臟數據。如果數據量大的話,而且寫操作比較多,必然會引起大量的磁盤io操作,可能會嚴重影響性能。
優勢
- 一旦采用該方式,那么你的整個Redis數據庫將只包含一個文件,這樣非常方便進行備份。比如你可能打算每1天歸檔一些數據。
- 方便備份,我們可以很容易的將一個一個RDB文件移動到其他的存儲介質上
- RDB 在恢復大數據集時的速度比 AOF 的恢復速度要快。
- RDB 可以最大化 Redis 的性能:父進程在保存 RDB 文件時唯一要做的就是 fork 出一個子進程,然后這個子進程就會處理接下來的所有保存工作,父進程無須執行任何磁盤 I/O 操作。
劣勢
- 如果你需要盡量避免在服務器故障時丟失數據,那么 RDB 不適合你。 雖然 Redis 允許你設置不同的保存點(save point)來控制保存 RDB 文件的頻率, 但是, 因為RDB 文件需要保存整個數據集的狀態, 所以它並不是一個輕松的操作。 因此你可能會至少 5 分鍾才保存一次 RDB 文件。 在這種情況下, 一旦發生故障停機, 你就可能會丟失好幾分鍾的數據。
- 每次保存 RDB 的時候,Redis 都要 fork() 出一個子進程,並由子進程來進行實際的持久化工作。 在數據集比較龐大時, fork() 可能會非常耗時,造成服務器在某某毫秒內停止處理客戶端; 如果數據集非常巨大,並且 CPU 時間非常緊張的話,那么這種停止時間甚至可能會長達整整一秒。 雖然 AOF 重寫也需要進行 fork() ,但無論 AOF 重寫的執行間隔有多長,數據的耐久性都不會有任何損失。
2.AOF持久化: 該機制將以日志的形式記錄服務器所處理的每一個寫操作,在Redis服務器啟動之初會讀取該文件來重新構建數據庫,以保證啟動后數據庫中的數據是完整的
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將使用與快照類似的方式將內存中的數據 以命令的方式保存到臨時文件中,最后替換原來的文件。具體過程如下
- redis調用fork ,現在有父子兩個進程
- 子進程根據內存中的數據庫快照,往臨時文件中寫入重建數據庫狀態的命令
- 父進程繼續處理client請求,除了把寫命令寫入到原來的aof文件中。同時把收到的寫命令緩存起來。這樣就能保證如果子進程重寫失敗的話並不會出問題。
- 當子進程把快照內容寫入以命令方式寫到臨時文件中后,子進程發信號通知父進程。然后父進程把緩存的寫命令也寫入到臨時文件。
- 現在父進程可以使用臨時文件替換老的aof文件,並重命名,后面收到的寫命令也開始往新的aof文件中追加。
需要注意到是重寫aof文件的操作,並沒有讀取舊的aof文件,而是將整個內存中的數據庫內容用命令的方式重寫了一個新的aof文件,這點和快照有點類似。
優勢
-
使用 AOF 持久化會讓 Redis 變得非常耐久(much more durable):你可以設置不同的 fsync 策略,比如無 fsync ,每秒鍾一次 fsync ,或者每次執行寫入命令時 fsync 。 AOF 的默認策略為每秒鍾 fsync 一次,在這種配置下,Redis 仍然可以保持良好的性能,並且就算發生故障停機,也最多只會丟失一秒鍾的數據( fsync 會在后台線程執行,所以主線程可以繼續努力地處理命令請求)。
-
AOF 文件是一個只進行追加操作的日志文件(append only log), 因此對 AOF 文件的寫入不需要進行 seek , 即使日志因為某些原因而包含了未寫入完整的命令(比如寫入時磁盤已滿,寫入中途停機,等等), redis-check-aof 工具也可以輕易地修復這種問題。
Redis 可以在 AOF 文件體積變得過大時,自動地在后台對 AOF 進行重寫: 重寫后的新 AOF 文件包含了恢復當前數據集所需的最小命令集合。 整個重寫操作是絕對安全的,因為 Redis 在創建新 AOF 文件的過程中,會繼續將命令追加到現有的 AOF 文件里面,即使重寫過程中發生停機,現有的 AOF 文件也不會丟失。 而一旦新 AOF 文件創建完畢,Redis 就會從舊 AOF 文件切換到新 AOF 文件,並開始對新 AOF 文件進行追加操作。 -
AOF 文件有序地保存了對數據庫執行的所有寫入操作, 這些寫入操作以 Redis 協議的格式保存, 因此 AOF 文件的內容非常容易被人讀懂, 對文件進行分析(parse)也很輕松。 導出(export) AOF 文件也非常簡單: 舉個例子, 如果你不小心執行了 FLUSHALL 命令, 但只要 AOF 文件未被重寫, 那么只要停止服務器, 移除 AOF 文件末尾的 FLUSHALL 命令, 並重啟 Redis , 就可以將數據集恢復到 FLUSHALL 執行之前的狀態。
劣勢
-
對於相同的數據集來說,AOF 文件的體積通常要大於 RDB 文件的體積。
-
根據所使用的 fsync 策略,AOF 的速度可能會慢於 RDB 。 在一般情況下, 每秒 fsync 的性能依然非常高, 而關閉 fsync 可以讓 AOF 的速度和 RDB 一樣快, 即使在高負荷之下也是如此。 不過在處理巨大的寫入載入時,RDB 可以提供更有保證的最大延遲時間(latency)。
-
AOF 在過去曾經發生過這樣的 bug : 因為個別命令的原因,導致 AOF 文件在重新載入時,無法將數據集恢復成保存時的原樣。 (舉個例子,阻塞命令 BRPOPLPUSH 就曾經引起過這樣的 bug 。) 測試套件里為這種情況添加了測試: 它們會自動生成隨機的、復雜的數據集, 並通過重新載入這些數據來確保一切正常。 雖然這種 bug 在 AOF 文件中並不常見, 但是對比來說, RDB 幾乎是不可能出現這種 bug 的。
Redis的哨兵機制
哨兵機制簡介:
有了主從復制的實現以后,如果想對主服務器進行監控,那么在redis2.6以后提供了一個"哨兵"的機制。顧名思義,哨兵的含義就是監控Redis系統的運行狀態。可以啟動多個哨兵,去監控Redis數據庫的運行狀態。其主要功能有兩點:
1、監控主數據庫和從數據庫是否正常運行。
2、主數據庫出現故障時,可以自動將從數據庫轉換為主數據庫,實現自動切換。
2、哨兵機制的實現步驟(如果master主服務器設置了密碼,記得在哨兵的配置文件里面配置訪問密碼)
2.0 在 slave1也就是,192.168.2.105 配置 哨兵,步驟如下
2.1 copy 文件/usr/local/redis-3.0.0-rc2/ 下的 sentinel.conf 到 /usr/local/redis/etc中
2.2 修改 sentinel.conf 文件
Redis 的 Sentinel 系統用於管理多個 Redis 服務器(instance), 該系統執行以下三個任務:
- 監控(Monitoring): Sentinel 會不斷地檢查你的主服務器和從服務器是否運作正常。
- 提醒(Notification): 當被監控的某個 Redis 服務器出現問題時, Sentinel 可以通過 API 向管理員或者其他應用程序發送通知。
- 自動故障遷移(Automatic failover): 當一個主服務器不能正常工作時, Sentinel 會開始一次自動故障遷移操作, 它會將失效主服務器的其中一個從服務器升級為新的主服務器, 並讓失效主服務器的其他從服務器改為復制新的主服務器; 當客戶端試圖連接失效的主服務器時, 集群也會向客戶端返回新主服務器的地址, 使得集群可以使用新主服務器代替失效服務器。
配置 Sentinel
Redis 源碼中包含了一個名為 sentinel.conf 的文件, 這個文件是一個帶有詳細注釋的 Sentinel 配置文件示例。
運行一個 Sentinel 所需的最少配置如下所示:
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1
sentinel monitor resque 192.168.1.3 6380 4
sentinel down-after-milliseconds resque 10000
sentinel failover-timeout resque 180000
sentinel parallel-syncs resque 5
第一行配置指示 Sentinel 去監視一個名為 mymaster 的主服務器, 這個主服務器的 IP 地址為 127.0.0.1 , 端口號為 6379 , 而將這個主服務器判斷為失效至少需要 2 個 Sentinel 同意 (只要同意 Sentinel 的數量不達標,自動故障遷移就不會執行)。
不過要注意, 無論你設置要多少個 Sentinel 同意才能判斷一個服務器失效, 一個 Sentinel 都需要獲得系統中多數(majority) Sentinel 的支持, 才能發起一次自動故障遷移, 並預留一個給定的配置紀元 (configuration Epoch ,一個配置紀元就是一個新主服務器配置的版本號)。
換句話說, 在只有少數(minority) Sentinel 進程正常運作的情況下, Sentinel 是不能執行自動故障遷移的。
其他選項的基本格式如下:
sentinel <選項的名字> <主服務器的名字> <選項的值>
各個選項的功能如下:
- down-after-milliseconds 選項指定了 Sentinel 認為服務器已經斷線所需的毫秒數。
如果服務器在給定的毫秒數之內, 沒有返回 Sentinel 發送的 PING 命令的回復, 或者返回一個錯誤, 那么 Sentinel 將這個服務器標記為主觀下線(subjectively down,簡稱 SDOWN )。
不過只有一個 Sentinel 將服務器標記為主觀下線並不一定會引起服務器的自動故障遷移: 只有在足夠數量的 Sentinel 都將一個服務器標記為主觀下線之后, 服務器才會被標記為客觀下線(objectively down, 簡稱 ODOWN ), 這時自動故障遷移才會執行。
將服務器標記為客觀下線所需的 Sentinel 數量由對主服務器的配置決定。
- parallel-syncs 選項指定了在執行故障轉移時, 最多可以有多少個從服務器同時對新的主服務器進行同步, 這個數字越小, 完成故障轉移所需的時間就越長。
如果從服務器被設置為允許使用過期數據集(參見對 redis.conf 文件中對 slave-serve-stale-data 選項的說明), 那么你可能不希望所有從服務器都在同一時間向新的主服務器發送同步請求, 因為盡管復制過程的絕大部分步驟都不會阻塞從服務器, 但從服務器在載入主服務器發來的 RDB 文件時, 仍然會造成從服務器在一段時間內不能處理命令請求: 如果全部從服務器一起對新的主服務器進行同步, 那么就可能會造成所有從服務器在短時間內全部不可用的情況出現。
你可以通過將這個值設為 1 來保證每次只有一個從服務器處於不能處理命令請求的狀態。
本文檔剩余的內容將對 Sentinel 系統的其他選項進行介紹, 示例配置文件 sentinel.conf 也對相關的選項進行了完整的注釋。