點擊上方 藍字 加關注吧
作者 | 王爺科技
來源 | www.toutiao.com/i6713520017595433485
Redis 簡介
Redis 是完全開源免費的,遵守 BSD 協議,是一個高性能的 key - value 數據庫
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 的所有操作都是原子性的,意思就是要麼成功執行要麼失敗完全不執行。單個操作是原子性的。多個操作也支持事務,即原子性,通過 MULTI 和 EXEC 指令包起來。
其他特性 - Redis 還支持 publish/subscribe 通知,key 過期等特性。
Redis 數據類型
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 : value1、
sub-key2 : value2、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 事務的相關命令:
序號命令及描述:
1. DISCARD 取消事務,放棄執行事務塊內的所有命令。
2. EXEC 執行所有事務塊內的命令。
3. MULTI 標記一個事務塊的開始。
4. UNWATCH 取消 WATCH 命令對所有 key 的監視。
5. WATCH 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。
關注後端開發者社區微信公衆號,後臺回覆:碼農大禮包 可以獲取最新整理的技術資料一份。涵蓋Java 框架學習、架構師學習等!