一、簡單的介紹
Redis是一個開源的使用C語言編寫、開源、支援網路、可基於記憶體亦可持久化的日誌型、高效能的Key-Value資料庫,並提供多種語言的API。其支援的資料結構型別有字串(string)、雜湊(hash)、列表(list)、集合(set)和有序集合(zset)。
Redis 與其他 key - value 快取產品有以下三個特點:
Redis支援資料的持久化,可以將記憶體中的資料保持在磁碟中,重啟的時候可以再次載入進行使用。
Redis不僅僅支援簡單的key-value型別的資料,同時還提供list,set,zset,hash等資料結構的儲存。
Redis支援資料的備份,即master-slave模式的資料備份。
Redis優勢:
效能極高 – Redis能讀的速度是110000次/s,寫的速度是81000次/s 。
豐富的資料型別 – Redis支援二進位制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 資料型別操作。
原子 – Redis的所有操作都是原子性的,同時Redis還支援對幾個操作全並後的原子性執行。
豐富的特性 – Redis還支援 publish/subscribe, 通知, key 過期等等特性。
二、部署安裝
下載
$ wget http://download.redis.io/releases/redis-2.8.19.tar.gz
解壓
$ tar xzf redis-2.8.19.tar.gz
$ cd redis-2.8.19
編譯
$ make
安裝
$makefile
Redis 由四個可執行檔案:redis-benchmark、redis-cli、redis-server、redis-stat 這四個檔案,加上一個redis.conf就構成了整個redis的最終可用包。它們的作用如下:
redis-server:Redis伺服器的daemon啟動程式
redis-cli:Redis命令列操作工具。當然,你也可以用telnet根據其純文字協議來操作
redis-benchmark:Redis效能測試工具,測試Redis在你的系統及你的配置下的讀寫效能
redis-stat:Redis狀態檢測工具,可以檢測Redis當前狀態引數及延遲狀況
現在就可以啟動redis了,redis只有一個啟動引數,就是他的配置檔案路徑。
redis-server /etc/redis.conf
注意,預設複製過去的redis.conf檔案的daemonize引數為no,所以redis不會在後臺執行,這時要測試,我們需要重新開一個終端。修改為yes則為後臺執行redis。另外配置檔案中規定了pid檔案,log檔案和資料檔案的地址,如果有需要先修改,預設log資訊定向到stdout.
下面是redis.conf的主要配置引數的意義:
daemonize:是否以後臺daemon方式執行
pidfile:pid檔案位置
port:監聽的埠號
timeout:請求超時時間
loglevel:log資訊級別
logfile:log檔案位置
databases:開啟資料庫的數量
save * *:儲存快照的頻率,第一個*表示多長時間,第三個*表示執行多少次寫操作。在一定時間內執行一定數量的寫操作時,自動儲存快照。可設定多個條件。
rdbcompression:是否使用壓縮
dbfilename:資料快照檔名(只是檔名,不包括目錄)
dir:資料快照的儲存目錄(這個是目錄)
appendonly:是否開啟appendonlylog,開啟的話每次寫操作會記一條log,這會提高資料抗風險能力,但影響效率。
appendfsync:appendonlylog如何同步到磁碟(三個選項,分別是每次寫都強制呼叫fsync、每秒啟用一次fsync、不呼叫fsync等待系統自己同步)
這時你可以開啟一個終端進行測試了,配置檔案中預設的監聽埠是6379。
備註:
Redis支援兩種資料持久化方式:RDB方式和AOF方式。前者會根據配置的規則定時將記憶體中的資料持久化到硬碟上,後者則是在每次執行寫命令之後將命令記錄下來。兩種持久化方式可以單獨使用,但是通常會將兩者結合使用。
save seconds updates,save配置,指出在多長時間內,有多少次更新操作,就將資料同步到資料檔案。這個可以多個條件配合,比如預設配置檔案中的設定,就設定了三個條件。
appendonly yes/no ,appendonly配置,指出是否在每次更新操作後進行日誌記錄,如果不開啟,可能會在斷電時導致一段時間內的資料丟失。因為redis本身同步資料檔案是按上面的save條件來同步的,所以有的資料會在一段時間內只存在於記憶體中。
appendfsync no/always/everysec ,appendfsync配置,no表示等作業系統進行資料快取同步到磁碟,always表示每次更新操作後手動呼叫fsync()將資料寫到磁碟,everysec表示每秒同步一次。
三、五種資料型別
1、字串型別
字串是Redis中最基本的資料型別,它能夠儲存任何型別的字串,包含二進位制資料。可以用於儲存郵箱,JSON化的物件,甚至是一張圖片,一個字串允許儲存的最大容量為512MB(未來可能沒有該限制)。字串是其他四種型別的基礎,與其他幾種型別的區別從本質上來說只是組織字串的方式不同而已。
SET 賦值,用法: SET key value
GET 取值,用法: GET key
INCR 遞增數字,僅僅對數字型別的鍵有用,相當於Java的i++運算,用法: INCR key
INCRBY 增加指定的數字,僅僅對數字型別的鍵有用,相當於Java的i+=3,用法:INCRBY key increment,意思是key自增increment,increment可以為負數,表示減少。
DECR 遞減數字,僅僅對數字型別的鍵有用,相當於Java的i–-,用法:DECR key
DECRBY 減少指定的數字,僅僅對數字型別的鍵有用,相當於Java的i-=3,用法:DECRBY key decrement,意思是key自減decrement,decrement可以為正數,表示增加。
INCRBYFLOAT 增加指定浮點數,僅僅對數字型別的鍵有用,用法:INCRBYFLOAT key increment
APPEND 向尾部追加值,相當於Java中的”hello”.append(“ world”),用法:APPEND key value
STRLEN 獲取字串長度,用法:STRLEN key
MSET 同時設定多個key的值,用法:MSET key1 value1 [key2 value2 ...]
MGET 同時獲取多個key的值,用法:MGET key1 [key2 ...]
雜湊型別相當於Java中的HashMap,他的值是一個字典,儲存很多key,value對,每對key,value的值個鍵都是字串型別,換句話說,雜湊型別不能巢狀其他資料型別。一個雜湊型別鍵最多可以包含2的32次方-1個欄位。
HSET 賦值,用法:HSET key field value
HMSET 一次賦值多個欄位,用法:HMSET key field1 value1 [field2 values]
HGET 取值,用法:HSET key field
HMGET 一次取多個欄位的值,用法:HMSET key field1 [field2]
HGETALL 一次取所有欄位的值,用法:HGETALL key
HEXISTS 判斷欄位是否存在,用法:HEXISTS key field
HSETNX 當欄位不存在時賦值,用法:HSETNX key field value
HINCRBY 增加數字,僅對數字型別的值有用,用法:HINCRBY key field increment
HDEL 刪除欄位,用法:HDEL key field
HKEYS 獲取所有欄位名,用法:HKEYS key
HVALS 獲取所有欄位值,用法:HVALS key
HLEN 獲取欄位數量,用法:HLEN key
3、列表型別
列表型別(list)用於儲存一個有序的字串列表,常用的操作是向佇列兩端新增元素或者獲得列表的某一片段。列表內部使用的是雙向連結串列(double linked list)實現的,所以向列表兩端新增元素的時間複雜度是O(1),獲取越接近列表兩端的元素的速度越快。但是缺點是使用列表通過索引訪問元素的效率太低(需要從端點開始遍歷元素)。所以列表的使用場景一般如:朋友圈新鮮事,只關心最新的一些內容。藉助列表型別,Redis還可以作為訊息佇列使用。
LPUSH 向列表左端新增元素,用法:LPUSH key value
RPUSH 向列表右端新增元素,用法:RPUSH key value
LPOP 從列表左端彈出元素,用法:LPOP key
RPOP 從列表右端彈出元素,用法:RPOP key
LLEN 獲取列表中元素個數,用法:LLEN key
LRANGE 獲取列表中某一片段的元素,用法:LRANGE key start stop,index從0開始,-1表示最後一個元素
LREM 刪除列表中指定的值,用法:LREM key count value,刪除列表中前count個值為value的元素,當count>0時從左邊開始數,count<0時從右邊開始數,count=0時會刪除所有值為value的元素
LINDEX 獲取指定索引的元素值,用法:LINDEX key index
LSET 設定指定索引的元素值,用法:LSET key index value
LTRIM 只保留列表指定片段,用法:LTRIM key start stop,包含start和stop
LINSERT 像列表中插入元素,用法:LINSERT key BEFORE|AFTER privot value,從左邊開始尋找值為privot的第一個元素,然後根據第二個引數是BEFORE還是AFTER決定在該元素的前面還是後面插入value
RPOPLPUSH 將元素從一個列表轉義到另一個列表,用法:RPOPLPUSH source destination
4、集合型別
集合在概念在高中課本就學過,集合中每個元素都是不同的,集合中的元素個數最多為2的32次方-1個,集合中的元素師沒有順序的。
SADD 新增元素,用法:SADD key value1 [value2 value3 ...]
SREM 刪除元素,用法:SREM key value2 [value2 value3 ...]
SMEMBERS 獲得集合中所有元素,用法:SMEMBERS key
SISMEMBER 判斷元素是否在集合中,用法:SISMEMBER key value
SDIFF 對集合做差集運算,用法:SDIFF key1 key2 [key3 ...],先計算key1和key2的差集,然後再用結果與key3做差集
SINTER 對集合做交集運算,用法:SINTER key1 key2 [key3 ...]
SUNION 對集合做並集運算,用法:SUNION key1 key2 [key3 ...]
SCARD 獲得集合中元素的個數,用法:SCARD key
SDIFFSTORE 對集合做差集並將結果儲存,用法:SDIFFSTORE destination key1 key2 [key3 ...]
SINTERSTORE 對集合做交集運算並將結果儲存,用法:SINTERSTORE destination key1 key2 [key3 ...]
SUNIONSTORE 對集合做並集運算並將結果儲存,用法:SUNIONSTORE destination key1 key2 [key3 ...]
SRANDMEMBER 隨機獲取集合中的元素,用法:SRANDMEMBER key [count],當count>0時,會隨機中集合中獲取count個不重複的元素,當count<0時,隨機中集合中獲取|count|和可能重複的元素。
SPOP 從集合中隨機彈出一個元素,用法:SPOP key
5、有序集合型別
有序集合型別與集合型別的區別就是他是有序的。有序集合是在集合的基礎上為每一個元素關聯一個分數,這就讓有序集合不僅支援插入,刪除,判斷元素是否存在等操作外,還支援獲取分數最高/最低的前N個元素。有序集合中的每個元素是不同的,但是分數卻可以相同。有序集合使用雜湊表和跳躍表實現,即使讀取位於中間部分的資料也很快,時間複雜度為O(log(N)),有序集合比列表更費記憶體。
ZADD 新增元素,用法:ZADD key score1 value1 [score2 value2 score3 value3 ...]
ZSCORE 獲取元素的分數,用法:ZSCORE key value
ZRANGE 獲取排名在某個範圍的元素,用法:ZRANGE key start stop [WITHSCORES],按照元素從小到大的順序排序,從0開始編號,包含start和stop對應的元素,WITHSCORE選項表示是否返回元素分數
ZREVRANGE 獲取排名在某個範圍的元素,用法:ZREVRANGE key start stop [WITHSCORES],和上一個命令用法一樣,只是這個倒序排序的。
ZRANGEBYSCORE 獲取指定分數範圍內的元素,用法:ZRANGEBYSCORE key min max,包含min和max,(min表示不包含min,(max表示不包含max,+inf表示無窮大
ZINCRBY 增加某個元素的分數,用法:ZINCRBY key increment value
ZCARD 獲取集合中元素的個數,用法:ZCARD key
ZCOUNT 獲取指定分數範圍內的元素個數,用法:ZCOUNT key min max,min和max的用法和5中的一樣
ZREM 刪除一個或多個元素,用法:ZREM key value1 [value2 ...]
ZREMRANGEBYRANK 按照排名範圍刪除元素,用法:ZREMRANGEBYRANK key start stop
ZREMRANGEBYSCORE 按照分數範圍刪除元素,用法:ZREMRANGEBYSCORE key min max,min和max的用法和4中的一樣
ZRANK 獲取正序排序的元素的排名,用法:ZRANK key value
ZREVRANK 獲取逆序排序的元素的排名,用法:ZREVRANK key value
ZINTERSTORE 計算有序集合的交集並儲存結果,用法:ZINTERSTORE destination numbers key1 key2 [key3 key4 ...] WEIGHTS weight1 weight2 [weight3 weight4 ...] AGGREGATE SUM | MIN | MAX,numbers表示參加運算的集合個數,weight表示權重,aggregate表示結果取值
ZUNIONSTORE 計算有序幾個的並集並儲存結果,用法和14一樣,不再贅述。
備註:
Redis為不同資料型別分別提供了一組引數來控制記憶體使用,Redis中雜湊是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)的時間複雜度,如果成員數量很少,則影響不大,否則會嚴重影響效能,所以要權衡好這個值的設定,總體上還是最根本的時間成本和空間成本上的權衡。
同樣類似的引數還有:
list-max-ziplist-entries 512
說明:list資料型別多少節點以下會採用去指標的緊湊儲存格式。
list-max-ziplist-value 64
說明:list資料型別節點值大小小於多少位元組會採用緊湊儲存格式。
set-max-intset-entries 512
說明:set資料型別內部資料如果全部是數值型,且包含多少節點以下會採用緊湊格式儲存。
四、持久化
Redis的強大效能很大程度上都是因為所有資料都是儲存在記憶體中的,然而當Redis重啟後,所有儲存在記憶體中的資料將會丟失,在很多情況下是無法容忍這樣的事情的。所以,我們需要將記憶體中的資料持久化!典型的需要持久化資料的場景如下:
將Redis作為資料庫使用;
將Redis作為快取伺服器使用,但是快取丟失後會對效能造成很大影響,所有快取同時失效時會造成服務雪崩,無法響應。
Redis支援兩種資料持久化方式:RDB方式和AOF方式。前者會根據配置的規則定時將記憶體中的資料持久化到硬碟上,後者則是在每次執行寫命令之後將命令記錄下來。兩種持久化方式可以單獨使用,但是通常會將兩者結合使用。當同時使用RDB和AOF時,這種情況下Redis重啟時,它會優先使用AOF檔案來還原資料集,因為AOF檔案儲存的資料集通常比RDB檔案所儲存的資料集更完整。RDB在恢復大資料集時的速度比AOF的恢復速度快。AOF檔案有序地儲存了對Redis執行的所有寫入操作。
1、RDB方式
RDB方式的持久化是通過快照的方式完成的。當符合某種規則時,會將記憶體中的資料全量生成一份副本儲存到硬碟上,這個過程稱作”快照”,Redis會在以下幾種情況下對資料進行快照:
根據配置規則進行自動快照;
使用者執行SAVE, BGSAVE命令;
執行FLUSHALL命令;
執行復制(replication)時。
1)、根據配置自動快照
Redis允許使用者自定義快照條件,當滿足條件時自動執行快照,快照規則的配置方式如下:
save 900 1
save 300 10
save 60 10000
每個快照條件獨佔一行,他們之間是或(||)關係,只要滿足任何一個就進行快照。上面配置save後的第一個引數T是時間,單位是秒,第二個引數M是更改的鍵的個數,含義是:當時間T內被更改的鍵的個數大於M時,自動進行快照。比如save 900 1的含義是15分鐘內(900s)被更改的鍵的個數大於1時,自動進行快照操作。
2)、行SAVE或BGSAVE命令
除了讓Redis自動進行快照外,當我們需要重啟,遷移,備份Redis時,我們也可以手動執行SAVE或BGSAVE命令主動進行快照操作。
SAVE命令:當執行SAVE命令時,Redis同步進行快照操作,期間會阻塞所有來自客戶端的請求,所以放資料庫資料較多時,應該避免使用該命令;
BGSAVE命令: 從命令名字就能看出來,這個命令與SAVE命令的區別就在於該命令的快照操作是在後臺非同步進行的,進行快照操作的同時還能處理來自客戶端的請求。執行BGSAVE命令後Redis會馬上返回OK表示開始進行快照操作,如果想知道快照操作是否已經完成,可以使用LASTSAVE命令返回最近一次成功執行快照的時間,返回結果是一個Unix時間戳。
3)、執行FLUSHALL命令
當執行FLUSHALL命令時,Redis會清除資料庫中的所有資料。需要注意的是:不論清空資料庫的過程是否觸發 了自動快照的條件,只要自動快照條件不為空,Redis就會執行一次快照操作,當沒有定義自動快照條件時,執行FLUSHALL命令不會進行快照操作。
4)、執行復制
當設定了主從模式時,Redis會在複製初始化是進行自動快照。
快照執行的過程如下:
Redis使用fork函式複製一份當前程式(父程式)的副本(子程式);
父程式繼續處理來自客戶端的請求,子程式開始將記憶體中的資料寫入硬碟中的臨時檔案;
當子程式寫完所有的資料後,用該臨時檔案替換舊的RDB檔案,至此,一次快照操作完成。
需要注意的是:
在執行fork是時候作業系統(類Unix作業系統)會使用寫時複製(copy-on-write)策略,即fork函式發生的一刻,父程式和子程式共享同一塊記憶體資料,當父程式需要修改其中的某片資料(如執行寫命令)時,作業系統會將該片資料複製一份以保證子程式不受影響,所以RDB檔案儲存的是執行fork操作那一刻的記憶體資料。所以RDB方式理論上是會存在丟資料的情況的(fork之後修改的的那些沒有寫進RDB檔案)。
通過上述的介紹可以知道,快照進行時時不會修改RDB檔案的,只有完成的時候才會用臨時檔案替換老的RDB檔案,所以就保證任何時候RDB檔案的都是完整的。這使得我們可以通過定時備份RDB檔案來實現Redis資料的備份。RDB檔案是經過壓縮處理的二進位制檔案,所以佔用的空間會小於記憶體中資料的大小,更有利於傳輸。
Redis啟動時會自動讀取RDB快照檔案,將資料從硬碟載入到記憶體,根據數量的不同,這個過程持續的時間也不盡相同,通常來講,一個記錄1000萬個字串型別鍵,大小為1GB的快照檔案載入到記憶體需要20-30秒的時間。
備註:
1、動態開啟與關閉RDB
config set save "1 2"
config set save ""
2、複製與bgsave都是fork一個子程式,且是將當前程式所佔的記憶體複製一份,故這兩個操作要求當前系統的記憶體必須大於Redis所佔用記憶體的2倍,否則這兩個操作執行不了;save是使用當前程式進行檔案儲存,故Redis會阻塞
上圖中紅色部分是執行bgsave之後的日誌;而粉色部分是執行save之後的日誌;而黃色的是當其新增一個從時的日誌;最後選中的部分是當前更改命令符合RDB配置規則後的日誌。
2、AOF方式
在使用Redis儲存非臨時資料時,一般都需要開啟AOF持久化來降低程式終止導致的資料丟失,AOF可以將Redis執行的每一條寫命令追加到硬碟檔案中,這已過程顯然會降低Redis的效能,但是大部分情況下這個影響是可以接受的,另外,使用較快的硬碟能提高AOF的效能。
動態開啟與關閉AOF
config set appendonly yes
config set appendonly no
如果將所有的命令都寫到AOF檔案的話,將是一個比較蠢行為,因為前面兩個命令會被第三個命令覆蓋,所以AOF檔案完全不需要儲存前面兩個檔案,事實上Redis確實就是這麼做的。刪除AOF檔案中無用的命令的過程成為”AOF重寫”,AOF重寫可以在配置檔案中做相應的配置,當滿足配置的條件時,自動進行AOF重寫操作。配置如下:
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
修改如:config set auto-aof-rewrite-min-size 1048576
第一行的意思是,目前的AOF檔案的大小超過上一次重寫時的AOF檔案的百分之多少時再次進行重寫,如果之前沒有重寫過,則以啟動時AOF檔案大小為依據。
第二行的意思是,當AOF檔案的大小大於64MB時才進行重寫,因為如果AOF檔案本來就很小時,有幾個無效的命令也是無傷大雅的事情。
這兩個配置項通常一起使用。
雖然每次執行更改資料庫的內容時,AOF都會記錄執行的命令,但是由於作業系統本身的硬碟快取的緣故,AOF檔案的內容並沒有真正地寫入硬碟,在預設情況下,作業系統會每隔30s將硬碟快取中的資料同步到硬碟,但是為了防止系統異常退出而導致丟資料的情況發生,我們還可以在Redis的配置檔案中配置這個同步的頻率:
# appendfsync always
appendfsync everysec
# appendfsync no
第一行表示每次AOF寫入一個命令都會執行同步操作,這是最安全也是最慢的方式;
第二行表示每秒鐘進行一次同步操作,一般來說使用這種方式已經足夠;
第三行表示不主動進行同步操作,這是最不安全的方式。
第一個紅色標記部分,是將appendonly由no改為yes時,執行的日誌
第二個黃色標記部分,是在Redis客戶端中執行bgrewriteaof命令時,產生的日誌
大體過程如下:
1)、redis呼叫fork ,現在有父子兩個程式
2)、子程式根據記憶體中的資料庫快照,往臨時檔案中寫入重建資料庫狀態的命令
3)、父程式繼續處理client請求,除了把寫命令寫入到原來的aof檔案中。同時把收到的寫命令快取起來。這樣就能保證如果子程式重寫失敗的話並不會出問題。
4)、當子程式把快照內容寫入已命令方式寫到臨時檔案中後,子程式發訊號通知父程式。然後父程式把快取的寫命令也寫入到臨時檔案。
5)、現在父程式可以使用臨時檔案替換老的aof檔案,並重新命名,後面收到的寫命令也開始往新的aof檔案中追加。
需要注意到是重寫aof檔案的操作,並沒有讀取舊的aof檔案,而是將整個記憶體中的資料庫內容用命令的方式重寫了一個新的aof檔案,這點和快照有點類似。
紅色標記部分是RDB方式進行持久化,黃色部分是AOF方式進行的持久化。
五、主從
我們將一臺Redis伺服器作主庫(Matser),其他三臺作為從庫(Slave),主庫只負責寫資料,每次有資料更新都將更新的資料同步到它所有的從庫,而從庫只負責讀資料。這樣一來,就有了兩個好處:
讀寫分離,不僅可以提高伺服器的負載能力,並且可以根據讀請求的規模自由增加或者減少從庫的數量,棒極了;
資料被複製成了了好幾份,就算有一臺機器出現故障,也可以使用其他機器的資料快速恢復。
需要注意的是:在Redis主從模式中,一臺主庫可以擁有多個從庫,但是一個從庫只能隸屬於一個主庫。
動態配置主從
新增從:slaveof ip port
將從提升為主:slaveof no one
配置好slave伺服器連線的master後,slave會建立和master的連線,然後傳送sync命令。無論是第一次同步建立的連線還是連線斷開後的重新連線,master都會啟動一個後臺程式,將資料庫快照儲存到檔案中.同時master主程式會開始收集新的寫命令並快取起來。當後臺程式完成寫檔案後,master就將快照檔案傳送給slave,slave將檔案儲存到磁碟上,然後載入到記憶體將資料庫快照恢復到slave上。slave完成快照檔案的恢復後,master就會把快取的命令都轉發給slave,slave更新記憶體資料庫。後續master收到的寫命令都會通過開始建立的連線傳送給slave。從master到slave的同步資料的命令和從 client到master傳送的命令使用相同的協議格式。當master和slave的連線斷開時,slave可以自動重新建立連線。如果master同時收到多個slave發來的同步連線命令,只會使用啟動一個程式來寫資料庫映象,然後傳送給所有slave。
具體的主從,可以看Redis 主從配置
參考: