前言
- redis是一個開源的使用ANSI C語言編寫、支援網路、可基於記憶體、亦可持久化的日誌型、key-value資料庫,並提供多種語言的API。
- 它是記憶體儲存的資料結構伺服器,可用作資料庫、快取記憶體和訊息佇列代理。
- 通過資料全部in-momery的方式保證高速訪問,同時提供資料落地的功能,這是redis最主要的適用場景。
- reids內建複製、Lua指令碼、LRU收回、事物以及不同級別磁碟持久化功能,同時通過redis Sentinel提供高可用,通過Redis Cluster提供自動分割槽。
- redis支援字串、雜湊表、列表、集合、有序集合、點陣圖、hyperloglogs等資料型別。
- redis最為常用的資料型別:stirng、hash、list、set、sorted set、pub/sub、transactions。
String型別
- string型別就是簡單的key-value型別,value不僅僅是string,也可以是數字。
- 常用命令:set、get、decr、incr、mget等。
-
除了提供與memcached一樣的get、set、incr、decr 等操作外,redis還提供了下面的一些操作:
(1)獲取字串長度; (2)往字串append內容; (3)設定和獲取字串的某一段內容; (4)設定及獲取字串的某一位(bit); (5)批量設定一系列字串的內容;
Hash型別
- hash特別適合用於儲存物件。
- 常用命令:hget、hset、hgetall等。
- 應用場景:儲存一些結構化的資料,比如使用者的暱稱、年齡、性別、積分等,儲存一個使用者資訊物件資料。
-
我們舉個簡單的例項來描述下Hash的應用場景,比如我們儲存一個使用者資訊物件資料,包含以下資訊:
(1)使用者id為查詢的key; (2)儲存的value包括姓名、年齡、生日等資訊
-
例項解析:
(1)key是使用者id,value是一個Map。 (2)這個Map的key是成員的屬性名,value是屬性值; (3)這樣對資料的修改和存取都可以直接通過其內部的Map的key(redis裡稱內部Map的key為field),也就是key(使用者名稱id)+field(屬性名)就可以操作對應屬性資料了。
-
注意:
(1)redis提供了介面(hgetall)可以直接取到全部的屬性資料,但是如果內部Map的成員很多,那麼涉及到遍歷整個Map的操作。 (2)由於redis單執行緒模型的緣故,這個遍歷操作可能會比較耗時,而令其他客戶端的請求完全響應不到,這點需要注意。
List型別
- list型別實質是一個每個元素都是string型別的雙向連結串列,這使得list既可以用作棧,也可以用作佇列。
- list型別經常會被用於訊息佇列的服務,以完成多程式之間的訊息交換。
- 常用命令:lpush、rpush、lpop、rpop、lrange等。
- lpush是插入到連結串列的左邊,也就是頭部;rpush是插入到連結串列的右邊,也就是尾部;lrange(key,start,end)返回指定區間內的元素,從頭部(左端)start開始到尾部(右端)區間內。
- 應用場景:實現最新訊息排行等功能,還有訊息佇列。
-
簡單訊息佇列舉例分析:
(1)假設一個應用程式執行lpush向連結串列中新增新的元素,我們通常將這樣的程式稱之為“生產者(producer)”; (2)而另外一個應用程式正在執行rpop操作從連結串列中取出元素,我們稱這樣的程式為“消費者(consumer)”; (3)在消費者消費訊息的過程中,需要不停呼叫rpop檢視list中是否有待處理訊息。每呼叫一次都會發起一次連結,造成不必要的浪費。 (4)另外,如果生產者速度大於消費者速度,訊息佇列長度會一直增大,時間久了會佔用大量記憶體空間; (5)所以,可以使用brpop命令,這個命令只有在有元素時返回,沒有則會阻塞直到超時返回null。
Set型別
- set型別是string型別的無序集合。
- set集合的概念就是一堆不重複值的組合。
- set元素最大可以包含(2的32次方-1)個元素。
- set內部實現是一個value永遠為null的HashMap。
- set對外提供的功能與list類似是一個列表的功能,特殊之處在於set時可以自動排重的。
- 常用命令:sadd、spop、smembers、sunion等。
- 當你需要儲存一個列表資料,又不希望出現重複資料時,set是一個很好的選擇。
- 並且set提供了判斷某個成員是否在一個set集合內的重要介面,這個是list不能提供的。
- 利用set資料結構,可以儲存一些集合性的資料,比如在微博應用中,可以將一個使用者所有的關注人存在一個集合中,將其所有粉絲存在一個集合。
- redis還為集合提供了求交集、並集、差集等操作,可以非常方便的實現如共同關注、共同喜好、二度好友等功能。
Zset型別
- 和set一樣,sorted set也是stirng型別元素的集合。不同的是每個元素都會關聯一個double型別的score,元素順序由score決定。
- sorted set是插入有序的,即自動排序。
- 常用命令:zadd、zrange、zrem、zcard等。
- zRange(key,start,end)返回指定範圍內的score從低到高順序的所有元素;zRevRange(key,start,end)返回key對應的有序集合中指定區間的按照score從高到低的順序的所有元素。
- 當你需要一個有序的並且不重複的集合列表時,那麼可以選擇sorted set資料結構。
-
應用舉例:
(1)例如儲存全班同學的成績,其集合value可以是同學的學號,而score就可以是成績。 (2)排行榜應用,根據得分列出topN的使用者等。
pub/sub
- subscribe、unsubscribe和publish三個命令實現了釋出與訂閱泛型。
- 傳送者(傳送資訊的客戶端)不是直接將資訊發給特定的接收者(接受資訊的客戶端),而是將資訊發給頻道(channel),然後由頻道將資訊轉發給所有對這個頻道感興趣的訂閱者。
- 傳送者無需知道任何關於訂閱者的資訊,而訂閱者也無需知道是哪個客戶端給它傳送資訊,它只要關注自己感興趣的頻道即可。
- 釋出/訂閱在redis中,被設計的非常輕量級和簡潔,它做到了訊息的釋出和訂閱的基本能力;但是尚未提供關於訊息持久化等各種企業級的特性。
- 一個redis client釋出訊息,其他多個redis client訂閱訊息,釋出的訊息即發即失,redis不會持久儲存釋出的訊息;訊息訂閱者也只能得到訂閱後的訊息,通道中此前的訊息無從獲得。
- 訊息釋出者,即publish客戶端,無需獨佔連結,你可以在publish訊息的同時,使用同一個redis-client連結進行其他操作(如incr等);
- 訊息訂閱者,即subscribe客戶端,需要獨佔連結,即進行subscribe期間,redis-client無法穿插其他操作。
- 此時client以阻塞的方式等待publish端的訊息,因此subscribe需要使用單獨的連結,甚至需要在額外的執行緒中使用。
- tcp預設連線時間固定,如果在這世間內sub端沒有接收到pub端訊息,或pub端沒有訊息產生,sub端的連線都會強制回收。
- 這就需要特殊手段解決,用定時器來模擬pub和sub之間的保活機制,定時器時間不能超過tcp最大連線時間。
- 一旦subscribe端斷開連結,將會失去部分訊息,即連結失效期間的訊息將會被丟失,所以,這裡需要考慮redis的list來持久化;
-
如果你非常關注每個訊息,那麼你應該基於redis做一些額外的補充工作,如果你希望訂閱是持久的,那麼如下設計思路可以借鑑:
(1)subscribe端:首先向一個set集合中增加“訂閱者id”,此set集合儲存了“活躍訂閱”者;訂閱者id標記每個唯一的訂閱者,此set為“活躍訂閱者集合”。 (2)subscribe端開啟訂閱操作,並基於redis建立一個以訂閱者id為key的list資料結構,此list中儲存了所有的尚未消費的訊息,此list稱為“訂閱者訊息佇列”; (3)publish端:每釋出一條訊息之後,publish端都需要遍歷活躍訂閱者集合,並依次向每個“訂閱者訊息佇列”尾部追加此次釋出的訊息; (4)到此為止,我們基本可以保證,釋出的每一條訊息,都會持久的儲存在每個“訂閱者訊息佇列”中; (5)subscribe端,每接收到一個訂閱訊息,在消費周後,必須刪除自己的“訂閱者訊息佇列”頭部的一條訊息; (6)subscribe端啟動時,如果發現自己的“訂閱者訊息佇列”中有殘存記錄,那麼將會首先消費這些訊息,然後再去訂閱。
- 以上方法可以保證成功到達的訊息必消費不丟失
transactions
- redis事務可以一次執行多個命令。
-
一個事務從開始到執行會經歷三個階段:
(1)開始事務 (2)命令入隊 (3)執行事務
- 事務是一個單獨的隔離操作:事務中的所有命令都會序列化、按順序執行。
- 事務在執行的過程中,不會被其他客戶端傳送來的命令請求所打斷。
- 單個redis命令的執行時原子性的,但redis沒有在事務上增加任何維持原子性的機制,所以redis事務的執行並不是原子性的。
- 事務可以理解為一個打包的批量執行指令碼,但批量指令並非原子化的操作,中間某條指令的失敗不會導致前面已做指令的回滾,也不會造成後續的指令不做。
- multi、exec、discard和watch命令是redis事務的基礎。
-
multi:
(1)multi命令用於開啟一個事務,它總是返回ok。 (2)multi命令執行之後,客戶端可以繼續向伺服器傳送任意多條命令; (3)這些命令不會立即被執行,而是被放到一個佇列中; (4)當exec命令被呼叫時,所有佇列中的命令才會被執行。
-
exec:
(1)exec命令負責觸發並執行事務中的所有命令; (2)如果客戶端在使用multi開啟了一個事務後,卻因為斷線而沒有成功執行exec命令,那麼事務中所有的命令都不會被執行。 (3)另一方面,如果客戶端成功在開啟事務之後執行exec命令,那麼事務中的所有命令都會被執行。
-
discard:
(1)通過呼叫discard,客戶端可以清空事務佇列,並放棄執行事務。
-
watch:
(1)watch命令可以為redis事務提供check-and-set (CAS)行為。 (2)watch使得exec命令有條件的執行:事務只能在所有被監控健都沒有被修改的前提下執行,如果這個前提不能滿足,事務就不會執行。 (3)如果你用watch監視來一個帶過期時間的健,那麼即使這個健過期了,事務仍然可以執行。 (4)watch可以被呼叫多次,對健的監視從watch被執行之後就生效,直到呼叫exec為止。 (5)當exec被呼叫時,不管事務是否被成功執行,對所有健的監視都會被取消。 (6)當客戶端斷開連結時,該客戶端對健的監視也會被取消。