- Redis
- Redis的常用命令
- Redis的安裝過程
- Redis主從複製原理
- Redis持久化原理
- Redis的哨兵Sentinel機制
- Redis的API使用
Redis 支持string list hash set sortset 五種數據類型
http://8d9901d5.wiz01.com/share/s/2dCg7l3-1kCv2lEZIt2_gYod0iaUd42pBATf28cVd41LwKTl
- 下載 redis-3.0.7.tar.gz 並解壓
- 進入解壓目錄並make,然後可以看到src目錄下的可執行命令
- redis安裝採用主從模式,使用redis的兩種持久化方法:快照(RDB文件)與追加式文件(AOF文件);同時採用redis自帶哨兵機制以完成主從切換。
- 大部分的配置默認即可,下面爲改動或者添加的部分
- 下面是redis.conf中對應改動的部分
-
#默認採用daemon的方式
-
daemonize yes
-
#改變默認的6379端口,防止人爲攻擊
-
port 16379
-
#指定redis的日誌文件位置
-
logfile /home/stat/redis-3.0.7/logs/redis.log
-
#指定持久化文件的日誌文件
-
dir /home/stat/redis-3.0.7/data
-
#這個只在slave側的redis.conf中進行設置
-
slaveof 10.186.150.201 16379
-
#開啓aof持久化方式
-
appendonly yes
- 下面是sentinel.conf中對應改動的部分
-
#指定sentinel的工作目錄
-
dir "/home/stat/redis-3.0.7/data"
-
#指定sentinel監控的master節點及端口
-
sentinel monitor mymaster 10.186.150.201 16379 2
- redis啓動:./redis-server ../redis.conf
- redis停止:./redis-cli -p 16379 shutdown
- 其他的查詢命令:
- ./redis-cli -h host_ip -p 16379 (redis服務端口);INFO命令查看當前主機是slave還是master及其他狀態
- ./redis-cli -h host_ip -p 26379(redis sentinel服務端口);INFO sentinel 查看sentinel哨兵信息;sentinel masters 查看master 節點信息;sentinel slaves mymaster(sentinel.conf制定的名字) 查看slave信息
- 配置好slave後,slave與master建立連接,然後發送sync命令.
- 無論是第一次連接還是重新連接,master都會啓動一個後臺進程,將數據庫快照保存到文件中,同時master主進程會開始收集新的寫命令並緩存.
- 後臺進程完成寫文件後,master就發送文件給slave,slave將文件保存到硬盤上,再加載到內存中,
- 接着master就會把緩存的命令轉發給slave,後續master將收到的寫命令發送給slave.
- 如果master同時收到多個slave發來的同步連接命令,master只會啓動一個進程來寫數據庫鏡像, 然後發送給所有的slave
Redis持久化原理
Redis有兩種持久化的方式:快照(RDB
文件)和追加式文件(AOF
文件):
- RDB持久化方式會在一個特定的間隔保存那個時間點的一個數據快照。
- AOF持久化方式則會記錄每一個服務器收到的寫操作。在服務啓動時,這些記錄的操作會逐條執行從而重建出原來的數據。寫操作命令記錄的格式跟Redis協議一致,以追加的方式進行保存。
- Redis的持久化是可以禁用的,就是說你可以讓數據的生命週期只存在於服務器的運行時間裏。
- 兩種方式的持久化是可以同時存在的,但是當Redis重啓時,AOF文件會被優先用於重建數據。
- Redis調用fork(),產生一個子進程。
- 子進程把數據寫到一個臨時的RDB文件。
- 當子進程寫完新的RDB文件後,把舊的RDB文件替換掉。
- RDB文件是一個很簡潔的單文件,它保存了某個時間點的Redis數據,很適合用於做備份。你可以設定一個時間點對RDB文件進行歸檔,這樣就能在需要的時候很輕易的把數據恢復到不同的版本。
- 基於上面所描述的特性,RDB很適合用於災備。單文件很方便就能傳輸到遠程的服務器上。
- RDB的性能很好,需要進行持久化時,主進程會fork一個子進程出來,然後把持久化的工作交給子進程,自己不會有相關的I/O操作。
- 比起AOF,在數據量比較大的情況下,RDB的啓動速度更快。
- RDB容易造成數據的丟失。假設每5分鐘保存一次快照,如果Redis因爲某些原因不能正常工作,那麼從上次產生快照到Redis出現問題這段時間的數據就會丟失了。
- RDB使用
fork()
產生子進程進行數據的持久化,如果數據比較大的話可能就會花費點時間,造成Redis停止服務幾毫秒。如果數據量很大且CPU性能不是很好的時候,停止服務的時間甚至會到1秒。
你可以配置保存點,使Redis如果在每N秒後數據發生了M次改變就保存快照文件。例如下面這個保存點配置表示每60秒,如果數據發生了1000次以上的變動,Redis就會自動保存快照文件:
save 60 1000
保存點可以設置多個,Redis的配置文件就默認設置了3個保存點:
# 格式爲:save <seconds> <changes># 可以設置多個。
save 900 1 #900秒後至少1個key有變動
save 300 10 #300秒後至少10個key有變動
save 60 10000 #60秒後至少10000個key有變動
如果想禁用快照保存的功能,可以通過註釋掉所有"save"配置達到,或者在最後一條"save"配置後添加如下的配置:
save ""
錯誤處理
默認情況下,如果Redis在後臺生成快照的時候失敗,那麼就會停止接收數據,目的是讓用戶能知道數據沒有持久化成功。但是如果你有其他的方式可以監控到Redis及其持久化的狀態,那麼可以把這個功能禁止掉。
stop-writes-on-bgsave-error yes
數據壓縮
默認Redis會採用LZF
對數據進行壓縮。如果你想節省點CPU的性能,你可以把壓縮功能禁用掉,但是數據集就會比沒壓縮的時候要打。
rdbcompression yes
數據校驗
從版本5的RDB的開始,一個CRC64
的校驗碼會放在文件的末尾。這樣更能保證文件的完整性,但是在保存或者加載文件時會損失一定的性能(大概10%)。如果想追求更高的性能,可以把它禁用掉,這樣文件在寫入校驗碼時會用0
替代,加載的時候看到0
就會直接跳過校驗。
rdbchecksum yes
手動生成快照
Redis提供了兩個命令用於手動生成快照。
SAVE
SAVE命令會使用同步的方式生成RDB快照文件,這意味着在這個過程中會阻塞所有其他客戶端的請求。因此不建議在生產環境使用這個命令,除非因爲某種原因需要去阻止Redis使用子進程進行後臺生成快照(例如調用fork(2)
出錯)。
BGSAVE
BGSAVE命令使用後臺的方式保存RDB文件,調用此命令後,會立刻返回OK
返回碼。Redis會產生一個子進程進行處理並立刻恢復對客戶端的服務。在客戶端我們可以使用LASTSAVE命令查看操作是否成功。
127.0.0.1:6379> BGSAVEBackground saving started
127.0.0.1:6379> LASTSAVE
(integer) 1433936394
配置文件裏禁用了快照生成功能不影響
SAVE
和BGSAVE
命令的效果。
AOF
快照並不是很可靠。如果你的電腦突然宕機了,或者電源斷了,又或者不小心殺掉了進程,那麼最新的數據就會丟失。而AOF文件則提供了一種更爲可靠的持久化方式。每當Redis接受到會修改數據集的命令時,就會把命令追加到AOF文件裏,當你重啓Redis時,AOF裏的命令會被重新執行一次,重建數據。
優點
- 比RDB可靠。你可以制定不同的fsync策略:不進行fsync、每秒fsync一次和每次查詢進行fsync。默認是每秒fsync一次。這意味着你最多丟失一秒鐘的數據。
- AOF日誌文件是一個純追加的文件。就算是遇到突然停電的情況,也不會出現日誌的定位或者損壞問題。甚至如果因爲某些原因(例如磁盤滿了)命令只寫了一半到日誌文件裏,我們也可以用
redis-check-aof
這個工具很簡單的進行修復。 - 當AOF文件太大時,Redis會自動在後臺進行重寫。重寫很安全,因爲重寫是在一個新的文件上進行,同時Redis會繼續往舊的文件追加數據。新文件上會寫入能重建當前數據集的最小操作命令的集合。當新文件重寫完,Redis會把新舊文件進行切換,然後開始把數據寫到新文件上。
- AOF把操作命令以簡單易懂的格式一條接一條的保存在文件裏,很容易導出來用於恢復數據。例如我們不小心用
FLUSHALL
命令把所有數據刷掉了,只要文件沒有被重寫,我們可以把服務停掉,把最後那條命令刪掉,然後重啓服務,這樣就能把被刷掉的數據恢復回來。
缺點
- 在相同的數據集下,AOF文件的大小一般會比RDB文件大。
- 在某些fsync策略下,AOF的速度會比RDB慢。通常fsync設置爲每秒一次就能獲得比較高的性能,而在禁止fsync的情況下速度可以達到RDB的水平。
- 在過去曾經發現一些很罕見的BUG導致使用AOF重建的數據跟原數據不一致的問題。
啓用AOF
把配置項appendonly
設爲yes
:
appendonly yes
文件路徑和名稱
# 文件存放目錄,與RDB共用。默認爲當前工作目錄。dir ./
# 默認文件名爲appendonly.aof
appendfilename "appendonly.aof"
可靠性
你可以配置Redis調用fsync的頻率,有三個選項:
- 每當有新命令追加到AOF的時候調用fsync。速度最慢,但是最安全。
- 每秒fsync一次。速度快(2.4版本跟快照方式速度差不多),安全性不錯(最多丟失1秒的數據)。
- 從不fsync,交由系統去處理。這個方式速度最快,但是安全性一般。
推薦使用每秒fsync一次的方式(默認的方式),因爲它速度快,安全性也不錯。相關配置如下:
# appendfsync alwaysappendfsync everysec
# appendfsync no
日誌重寫(Rewrite機制)
隨着寫操作的不斷增加,AOF文件會越來越大。例如你遞增一個計數器100次,那麼最終結果就是數據集裏的計數器的值爲最終的遞增結果,但是AOF文件裏卻會把這100次操作完整的記錄下來。而事實上要恢復這個記錄,只需要1個命令就行了,也就是說AOF文件裏那100條命令其實可以精簡爲1條。所以Redis支持這樣一個功能:在不中斷服務的情況下在後臺重建AOF文件。
工作原理如下:
- Redis調用fork(),產生一個子進程。
- 子進程把新的AOF寫到一個臨時文件裏。
- 主進程持續把新的變動寫到內存裏的buffer,同時也會把這些新的變動寫到舊的AOF裏,這樣即使重寫失敗也能保證數據的安全。
- 當子進程完成文件的重寫後,主進程會獲得一個信號,然後把內存裏的buffer追加到子進程生成的那個新AOF裏。
- Redis
我們可以通過配置設置日誌重寫的條件:
# Redis會記住自從上一次重寫後AOF文件的大小(如果自Redis啓動後還沒重寫過,則記住啓動時使用的AOF文件的大小)。# 如果當前的文件大小比起記住的那個大小超過指定的百分比,則會觸發重寫。# 同時需要設置一個文件大小最小值,只有大於這個值文件纔會重寫,以防文件很小,但是已經達到百分比的情況。
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
要禁用自動的日誌重寫功能,我們可以把百分比設置爲0:
auto-aof-rewrite-percentage 0
Redis 2.4以上纔可以自動進行日誌重寫,之前的版本需要手動運行BGREWRITEAOF這個命令。
Redis數據恢復順序
當redis服務器掛掉時,重啓時將按以下優先級恢復數據到內存種:
1. 如果只配置了AOF,重啓時加載AOF文件恢復數據.
2. 如果同時配置了RBD和AOF,啓動時只加載AOF文件恢復數據.
3. 如果只配置了㻾DB,啓動時將加載dump文件恢復數據.
數據損壞修復
如果因爲某些原因(例如服務器崩潰)AOF文件損壞了,導致Redis加載不了,可以通過以下方式進行修復:
- 備份AOF文件。
-
使用
redis-check-aof
命令修復原始的AOF文件:$ redis-check-aof --fix
- 可以使用
diff -u
命令看下兩個文件的差異。 - 使用修復過的文件重啓Redis服務。
從RDB切換到AOF
這裏只說Redis >= 2.2版本的方式:
- 備份一個最新的
dump.rdb
的文件,並把備份文件放在一個安全的地方。 -
運行以下兩條命令:
$ redis-cli config set appendonly yes $ redis-cli config set save ""
-
確保數據跟切換前一致。
- 確保數據正確的寫到AOF文件裏。
第二條命令是用來禁用RDB的持久化方式,但是這不是必須的,因爲你可以同時啓用兩種持久化方式。
記得對配置文件
redis.conf
進行編輯啓用AOF,因爲命令行方式修改配置在重啓Redis後就會失效。
備份
建議的備份方法
- 創建一個定時任務,每小時和每天創建一個快照,保存在不同的文件夾裏。
- 定時任務運行時,把太舊的文件進行刪除。例如只保留48小時的按小時創建的快照和一到兩個月的按天創建的快照。
- 每天確保一次把快照文件傳輸到數據中心外的地方進行保存,至少不能保存在Redis服務所在的服務器
Sentinel(哨兵)是用於監控redis集羣中Master狀態的工具
Sentinel作用
- Master狀態檢測
- 如果Master異常,則會進行Master-Slave切換,將其中一個Slave作爲Master,將之前的Master作爲Slave
- Master-Slave切換後,master_redis.conf、slave_redis.conf和sentinel.conf的內容都會發生改變,即master_redis.conf中會多一行slaveof的配置,sentinel.conf的監控目標會隨之調換
Sentinel工作方式
- 每個Sentinel以每秒鐘一次的頻率向它所知的Master,Slave以及其他 Sentinel 實例發送一個 PING 命令
- 如果一個實例(instance)距離最後一次有效回覆 PING 命令的時間超過 down-after-milliseconds 選項所指定的值, 則這個實例會被 Sentinel 標記爲主觀下線
- 如果一個Master被標記爲主觀下線,則正在監視這個Master的所有 Sentinel 要以每秒一次的頻率確認Master的確進入了主觀下線狀態
- 當有足夠數量的 Sentinel(大於等於配置文件指定的值)在指定的時間範圍內確認Master的確進入了主觀下線狀態, 則Master會被標記爲客觀下線
- 在一般情況下, 每個 Sentinel 會以每 10 秒一次的頻率向它已知的所有Master,Slave發送 INFO 命令
- 當Master被 Sentinel 標記爲客觀下線時,Sentinel 向下線的 Master 的所有 Slave 發送 INFO 命令的頻率會從 10 秒一次改爲每秒一次
- 若沒有足夠數量的 Sentinel 同意 Master 已經下線, Master 的主觀下線狀態就會被移除
- 若 Master 重新向 Sentinel 的 PING 命令返回有效回覆, Master 的主觀下線狀態就會被移除
備註
主觀下線:Subjectively Down,簡稱 SDOWN,指的是當前 Sentinel 實例對某個redis服務器做出的下線判斷
客觀下線:Objectively Down, 簡稱 ODOWN,指的是多個 Sentinel 實例在對Master Server做出 SDOWN 判斷,並且通過 SENTINEL is-master-down-by-addr 命令互相交流之後,得出的Master Server下線判斷,然後開啓failover.
SDOWN適合於Master和Slave,只要一個 Sentinel 發現Master進入了ODOWN, 這個 Sentinel 就可能會被其他 Sentinel 推選出, 並對下線的主服務器執行自動故障遷移操作。
ODOWN只適用於Master,對於Slave的 Redis 實例,Sentinel 在將它們判斷爲下線前不需要進行協商, 所以Slave的 Sentinel 永遠不會達到ODOWN。
注意點
- 首次啓動時,必須先啓動Master
- Sentinel 只在 server 端做主從切換,app端要自己開發(例如Jedis庫的SentinelJedis,能夠監控Sentinel的狀態)
- 若Master已經被判定爲下線,Sentinel已經選擇了新的Master,也已經將old Master改成Slave,但是還沒有將其改成new Master。若此時重啓old Master,則Redis集羣將處於無Master狀態,此時只能手動修改配置文件,然後重新啓動集羣
Redis API使用
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.7.3</version>
</dependency>
- 直接使用Redis的接口Jedis
- 使用Redis資源池的接口JedisPool
- 資源池維護一個資源連接池,jedis實例可以動態的從中獲取連接資源
- 使用Redis 集羣的接口JedisCluster
- JedisCluster是jedis封裝的關於redis cluster操作的class;
- JedisCluster裏主要有兩個成員變量:maxRedirections和connectionHandler
- JedisCluster會從connectionHandler基於key找出對應的SLOTS和NODES,並找出對應的JedisPool對象
- 從JedisPool對象中找出可用的Jedis實例,執行Jedis的對應操作
- JedisCluster是jedis封裝的關於redis cluster操作的class;
JedisPoolConfig config = new JedisPoolConfig();
String host = Config.getM_strRedisIp();
int port = Config.getM_nRedisPort();
config.setMaxTotal(MAX_ACTIVE);
config.setMaxIdle(MAX_IDLE);
config.setMaxWaitMillis(MAX_WAIT);
config.setTestOnBorrow(TEST_ON_BORROW);
jedisPool = new JedisPool(config, host, port, TIMEOUT);
Jedis jedis = jedisPool.getResource();
- #設置集羣的節點
Set<HostAndPort> nodes = new HashSet<HostAndPort>();
String redisCluster = Config.getM_strRedisCluster();
String[] ipPorts = redisCluster.split(",");
for (String ipPort : ipPorts)
{
String ip = ipPort.split(":")[0];
String port = ipPort.split(":")[1];
nodes.add(new HostAndPort(ip, Integer.parseInt(port)));
}
#配置config
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxIdle(5);
poolConfig.setMaxTotal(20);
// 在borrow一個jedis實例時,是否提前進行validate操作;如果爲true,則得到的jedis實例均是可用的;
poolConfig.setTestOnBorrow(true);
- #設置超時時間
int timeout = 5000;
#創建jedisCluster
JedisCluster jedisCluster = new JedisCluster(nodes, timeout, poolConfig);