做者:王爺科技java
https://www.toutiao.com/i6713520017595433485redis
Redis 簡介 & 優點算法
Redis 數據類型數據庫
發佈訂閱緩存
訂閱者的客戶端顯示以下安全
事務服務器
持久化網絡
複製數據結構
哨兵app
分片
Redis 是徹底開源免費的,遵照 BSD 協議,是一個高性能的 key - value 數據庫
Redis 與 其餘 key - value 緩存產品有如下三個特色:
Redis 支持數據持久化,能夠將內存中的數據保存在磁盤中,重啓的時候能夠再次加載進行使用。
Redis 不只僅支持簡單的 key - value 類型的數據,同時還提供 list,set,zset,hash 等數據結構的存儲
Redis 支持數據的備份,即 master - slave 模式的數據備份
性能極高 – Redis 讀的速度是 110000 次 /s, 寫的速度是 81000 次 /s 。
豐富的數據類型 - Redis 支持二進制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 數據類型操做。
原子性 - Redis 的全部操做都是原子性的,意思就是要麼成功執行要麼失敗徹底不執行。單個操做是原子性的。多個操做也支持事務,即原子性,經過 MULTI 和 EXEC 指令包起來。
其餘特性 - Redis 還支持 publish/subscribe 通知,key 過時等特性。
Redis 支持 5 中數據類型:string(字符串),hash(哈希),list(列表),set(集合),zset(sorted set:有序集合)
string
string 是 redis 最基本的數據類型。一個 key 對應一個 value。
string 是二進制安全的。也就是說 redis 的 string 能夠包含任何數據。好比 jpg 圖片或者序列化的對象。
string 類型是 redis 最基本的數據類型,string 類型的值最大能存儲 512 MB。
理解:string 就像是 java 中的 map 同樣,一個 key 對應一個 value
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> get hello
"world"
hash
Redis hash 是一個鍵值對(key - value)集合。
Redis hash 是一個 string 類型的 key 和 value 的映射表,hash 特別適合用於存儲對象。
理解:能夠將 hash 當作一個 key - value 的集合。也能夠將其想成一個 hash 對應着多個 string。
與 string 區別:string 是 一個 key - value 鍵值對,而 hash 是多個 key - value 鍵值對。
// hash-key 能夠當作是一個鍵值對集合的名字,在這裏分別爲其添加了 sub-key1 : value一、
sub-key2 : value二、sub-key3 : value3 這三個鍵值對
127.0.0.1:6379> hset hash-key sub-key1 value1
(integer) 1
127.0.0.1:6379> hset hash-key sub-key2 value2
(integer) 1
127.0.0.1:6379> hset hash-key sub-key3 value3
(integer) 1
// 獲取 hash-key 這個 hash 裏面的全部鍵值對
127.0.0.1:6379> hgetall hash-key
1) "sub-key1"
2) "value1"
3) "sub-key2"
4) "value2"
5) "sub-key3"
6) "value3"
// 刪除 hash-key 這個 hash 裏面的 sub-key2 鍵值對
127.0.0.1:6379> hdel hash-key sub-key2
(integer) 1
127.0.0.1:6379> hget hash-key sub-key2
(nil)
127.0.0.1:6379> hget hash-key sub-key1
"value1"
127.0.0.1:6379> hgetall hash-key
1) "sub-key1"
2) "value1"
3) "sub-key3"
4) "value3"
list
Redis 列表是簡單的字符串列表,按照插入順序排序。咱們能夠網列表的左邊或者右邊添加元素。
127.0.0.1:6379> rpush list-key v1
(integer) 1
127.0.0.1:6379> rpush list-key v2
(integer) 2
127.0.0.1:6379> rpush list-key v1
(integer) 3
127.0.0.1:6379> lrange list-key 0 -1
1) "v1"
2) "v2"
3) "v1"
127.0.0.1:6379> lindex list-key 1
"v2"
127.0.0.1:6379> lpop list
(nil)
127.0.0.1:6379> lpop list-key
"v1"
127.0.0.1:6379> lrange list-key 0 -1
1) "v2"
2) "v1"
咱們能夠看出 list 就是一個簡單的字符串集合,和 Java 中的 list 相差不大,區別就是這裏的 list 存放的是字符串。list 內的元素是可重複的。
set
redis 的 set 是字符串類型的無序集合。集合是經過哈希表實現的,所以添加、刪除、查找的複雜度都是 O(1)
127.0.0.1:6379> sadd k1 v1
(integer) 1
127.0.0.1:6379> sadd k1 v2
(integer) 1
127.0.0.1:6379> sadd k1 v3
(integer) 1
127.0.0.1:6379> sadd k1 v1
(integer) 0
127.0.0.1:6379> smembers k1
1) "v3"
2) "v2"
3) "v1"
127.0.0.1:6379>
127.0.0.1:6379> sismember k1 k4
(integer) 0
127.0.0.1:6379> sismember k1 v1
(integer) 1
127.0.0.1:6379> srem k1 v2
(integer) 1
127.0.0.1:6379> srem k1 v2
(integer) 0
127.0.0.1:6379> smembers k1
1) "v3"
2) "v1"
redis 的 set 與 java 中的 set 仍是有點區別的。
redis 的 set 是一個 key 對應着 多個字符串類型的 value,也是一個字符串類型的集合
可是和 redis 的 list 不一樣的是 set 中的字符串集合元素不能重複,可是 list 能夠。
Zset
redis zset 和 set 同樣都是 字符串類型元素的集合,而且集合內的元素不能重複。
不一樣的是,zset 每一個元素都會關聯一個 double 類型的分數。redis 經過分數來爲集合中的成員進行從小到大的排序。
zset 的元素是惟一的,可是分數(score)卻能夠重複。
127.0.0.1:6379> zadd zset-key 728 member1
(integer) 1
127.0.0.1:6379> zadd zset-key 982 member0
(integer) 1
127.0.0.1:6379> zadd zset-key 982 member0
(integer) 0
127.0.0.1:6379> zrange zset-key 0 -1 withscores
1) "member1"
2) "728"
3) "member0"
4) "982"
127.0.0.1:6379> zrangebyscore zset-key 0 800 withscores
1) "member1"
2) "728"
127.0.0.1:6379> zrem zset-key member1
(integer) 1
127.0.0.1:6379> zrem zset-key member1
(integer) 0
127.0.0.1:6379> zrange zset-key 0 -1 withscores
1) "member0"
2) "982"
zset 是按照分數的大小來排序的。
通常不用 Redis 作消息發佈訂閱。
簡介
Redis 發佈訂閱 (pub/sub) 是一種消息通訊模式:發送者 (pub) 發送消息,訂閱者 (sub) 接收消息。
Redis 客戶端能夠訂閱任意數量的頻道。
下圖展現了頻道 channel1 , 以及訂閱這個頻道的三個客戶端 —— client2 、 client5 和 client1 之間的關係:
學Redis這篇就夠了
當有新消息經過 PUBLISH 命令發送給頻道 channel1 時, 這個消息就會被髮送給訂閱它的三個客戶端:
學Redis這篇就夠了
實例
如下實例演示了發佈訂閱是如何工做的。在咱們實例中咱們建立了訂閱頻道名爲 redisChat:
127.0.0.1:6379> SUBsCRIBE redisChat
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redisChat"
如今,咱們先從新開啓個 redis 客戶端,而後在同一個頻道 redisChat 發佈兩次消息,訂閱者就能接收到消息。
127.0.0.1:6379> PUBLISH redisChat "send message"
(integer) 1
127.0.0.1:6379> PUBLISH redisChat "hello world"
(integer) 1
# 訂閱者的客戶端顯示以下
1) "message"
2) "redisChat"
3) "send message"
1) "message"
2) "redisChat"
3) "hello world"
發佈訂閱經常使用命令
自行查閱
redis 事務一次能夠執行多條命令,服務器在執行命令期間,不會去執行其餘客戶端的命令請求。
事務中的多條命令被一次性發送給服務器,而不是一條一條地發送,這種方式被稱爲流水線,它能夠減小客戶端與服務器之間的網絡通訊次數從而提高性能。
Redis 最簡單的事務實現方式是使用 MULTI 和 EXEC 命令將事務操做包圍起來。
批量操做在發送 EXEC 命令前被放入隊列緩存。
收到 EXEC 命令後進入事務執行,事務中任意命令執行失敗,其他命令依然被執行。也就是說 Redis 事務不保證原子性。
在事務執行過程當中,其餘客戶端提交的命令請求不會插入到事務執行命令序列中。
一個事務從開始到執行會經歷如下三個階段:
開始事務。
命令入隊。
執行事務。
實例
如下是一個事務的例子, 它先以 MULTI 開始一個事務, 而後將多個命令入隊到事務中, 最後由 EXEC 命令觸發事務, 一併執行事務中的全部命令:
redis 127.0.0.1:6379> MULTI
OK
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"
單個 Redis 命令的執行是原子性的,但 Redis 沒有在事務上增長任何維持原子性的機制,因此 Redis 事務的執行並非原子性的。
事務能夠理解爲一個打包的批量執行腳本,但批量指令並不是原子化的操做,中間某條指令的失敗不會致使前面已作指令的回滾,也不會形成後續的指令不作。
這是官網上的說明 From redis docs on transactions:
It's important to note that even when a command fails, all the other commands in the queue are processed – Redis will not stop the processing of commands.
好比:
redis 127.0.0.1:7000> multi
OK
redis 127.0.0.1:7000> set a aaa
QUEUED
redis 127.0.0.1:7000> set b bbb
QUEUED
redis 127.0.0.1:7000> set c ccc
QUEUED
redis 127.0.0.1:7000> exec
1) OK
2) OK
3) OK
若是在 set b bbb 處失敗,set a 已成功不會回滾,set c 還會繼續執行。
Redis 事務命令
下表列出了 redis 事務的相關命令:
序號命令及描述:
1DISCARD 取消事務,放棄執行事務塊內的全部命令。
2EXEC 執行全部事務塊內的命令。
3MULTI 標記一個事務塊的開始。
4UNWATCH 取消 WATCH 命令對全部 key 的監視。
5WATCH key [key …]監視一個 (或多個) key ,若是在事務執行以前這個 (或這些) key 被其餘命令所改動,那麼事務將被打斷。
Redis 是內存型數據庫,爲了保證數據在斷電後不會丟失,須要將內存中的數據持久化到硬盤上。
RDB 持久化
將某個時間點的全部數據都存放到硬盤上。
能夠將快照複製到其餘服務器從而建立具備相同數據的服務器副本。
若是系統發生故障,將會丟失最後一次建立快照以後的數據。
若是數據量大,保存快照的時間會很長。
AOF 持久化
將寫命令添加到 AOF 文件(append only file)末尾。
使用 AOF 持久化須要設置同步選項,從而確保寫命令同步到磁盤文件上的時機。
這是由於對文件進行寫入並不會立刻將內容同步到磁盤上,而是先存儲到緩衝區,而後由操做系統決定何時同步到磁盤。
選項同步頻率always每一個寫命令都同步eyerysec每秒同步一次no讓操做系統來決定什麼時候同步
always 選項會嚴重減低服務器的性能
everysec 選項比較合適,能夠保證系統崩潰時只會丟失一秒左右的數據,而且 Redis 每秒執行一次同步對服務器幾乎沒有任何影響。
no 選項並不能給服務器性能帶來多大的提高,並且會增長系統崩潰時數據丟失的數量。
隨着服務器寫請求的增多,AOF 文件會愈來愈大。Redis 提供了一種將 AOF 重寫的特性,可以去除 AOF 文件中的冗餘寫命令。
經過使用 slaveof host port 命令來讓一個服務器成爲另外一個服務器的從服務器。
一個從服務器只能有一個主服務器,而且不支持主主複製。
鏈接過程
主服務器建立快照文件,即 RDB 文件,發送給從服務器,並在發送期間使用緩衝區記錄執行的寫命令。
快照文件發送完畢以後,開始像從服務器發送存儲在緩衝區的寫命令。
從服務器丟棄全部舊數據,載入主服務器發來的快照文件,以後從服務器開始接受主服務器發來的寫命令。
主服務器每執行一次寫命令,就向從服務器發送相同的寫命令。
主從鏈
隨着負載不斷上升,主服務器沒法很快的更新全部從服務器,或者從新鏈接和從新同步從服務器將致使系統超載。
爲了解決這個問題,能夠建立一箇中間層來分擔主服務器的複製工做。中間層的服務器是最上層服務器的從服務器,又是最下層服務器的主服務器。
Sentinel(哨兵)能夠監聽集羣中的服務器,並在主服務器進入下線狀態時,自動從從服務器中選舉處新的主服務器。
分片是將數據劃分爲多個部分的方法,能夠將數據存儲到多臺機器裏面,這種方法在解決某些問題時能夠得到線性級別的性能提高。
假設有 4 個 Redis 實例 R0, R1, R2, R3, 還有不少表示用戶的鍵 user:1, user:2, … , 有不一樣的方式來選擇一個指定的鍵存儲在哪一個實例中。
最簡單的是範圍分片,例如用戶 id 從 0 ~ 1000 的存儲到實例 R0 中,用戶 id 從 1001 ~ 2000 的存儲到實例 R1中,等等。可是這樣須要維護一張映射範圍表,維護操做代價高。
還有一種是哈希分片。使用 CRC32 哈希函數將鍵轉換爲一個數字,再對實例數量求模就能知道存儲的實例。
根據執行分片的位置,能夠分爲三種分片方式:
客戶端分片:客戶端使用一致性哈希等算法決定應當分佈到哪一個節點。
代理分片:將客戶端的請求發送到代理上,由代理轉發到正確的節點上。
服務器分片:Redis Cluster。