深入剖析Redis系列(五) - Redis資料結構之字串
字串型別是 Redis
最基礎的資料結構。 字串型別 的值實際可以是 字串 ( 簡單 和 複雜 的字串,例如 JSON
、 XML
)、 數字 (整數、浮點數),甚至是 二進位制 (圖片、音訊、視訊),但是值最大不能超過 512MB
。

正文
1. 相關命令
1.1. 常見命令
1.1.1. 設定值
set key value [ex seconds] [px milliseconds] [nx|xx]
set
命令有幾個選項:
- ex seconds :為 鍵 設定 秒級過期時間 。
- px milliseconds :為 鍵 設定 毫秒級過期時間 。
- nx :鍵必須 不存在 ,才可以設定成功,用於 新增 。
- xx :與
nx
相反,鍵必須 存在 ,才可以設定成功,用於 更新 。
除了 set
選項, Redis
還提供了 setex
和 setnx
兩個命令:
setex key seconds value setnx key value
- setex :設定鍵的值,並指定此鍵值對應的 有效時間 。
127.0.0.1:6379> setex key1 5 value1 OK 127.0.0.1:6379> get key1 "value1" 127.0.0.1:6379> get key1 (nil) 複製程式碼
- setnx :鍵必須 不存在 ,才可以設定成功。如果鍵已經存在,返回
0
。
127.0.0.1:6379> set key2 value1 OK 127.0.0.1:6379> setnx key2 value2 (integer) 1 127.0.0.1:6379> get key2 "value1" 複製程式碼
1.1.2. 獲取值
get key
如果要獲取的 鍵不存在 ,則返回 nil
( 空 )。
127.0.0.1:6379> get not_exist_key (nil) 複製程式碼
1.1.3. 批量設定值
mset key value [key value ...]
下面操作通過 mset
命令一次性設定 4
個 鍵值對 :
127.0.0.1:6379> mset a 1 b 2 c 3 d 4 OK 複製程式碼
1.1.4. 批量獲取值
mget key [key ...]
通過下面操作 批量獲取 鍵 a
、 b
、 c
、 d
的值:
127.0.0.1:6379> mget a b c d 1) "1" 2) "2" 3) "3" 4) "4" 複製程式碼
批量操作命令,可以有效提高 開發效率 ,假如沒有 mget
這樣的命令,要執行 n
次 get
命令的過程和 耗時 如下:
n次get時間 = n次網路時間 + n次命令時間

使用 mget
命令後,執行 n
次 get
命令的過程和 耗時 如下:
n次get時間 = 1次網路時間 + n次命令時間

Redis
可以支撐 每秒數萬 的 讀寫操作 ,但這指的是 Redis
服務端 的處理能力,對於 客戶端 來說,一次命令除了 命令時間 還是有 網路時間 。
假設 網路時間 為 1
毫秒 ,命令時間為 0.1
毫秒(按照每秒處理 1
萬條命令算),那麼執行 1000
次 get
命令和 1
次 mget
命令的區別如表所示:
操作 | 時間 |
---|---|
1000次get操作 | 1000 * 1 + 1000 * 0.1 = 1100ms = 1.1s |
1次mget操作 | 1 * 1 + 1000 * 0.1 = 101ms = 0.101s |
1.1.5. 計數
incr key
incr
命令用於對值做 自增操作 ,返回結果分為三種情況:
- 值不是 整數 ,返回 錯誤 。
- 值是 整數 ,返回 自增 後的結果。
- 鍵不存在,按照值為
0
自增 ,返回結果為1
。
127.0.0.1:6379> exists key (integer) 0 127.0.0.1:6379> incr key (integer) 1 複製程式碼
除了 incr
命令, Redis
還提供了 decr
( 自減 )、 incrby
( 自增指定數字 )、 decrby
( 自減指定數字 )、 incrbyfloat
( 自增浮點數 )等命令操作:
decr key incrby key increment decrby key decrement incrbyfloat key increment
很多 儲存系統 和 程式語言 內部使用 CAS
機制實現 計數功能 ,會有一定的 CPU
開銷。但在 Redis
中完全不存在這個問題,因為 Redis
是 單執行緒架構 ,任何命令到了 Redis
服務端 都要 順序執行 。
1.2. 不常用命令
1.2.1. 追加值
append key value
append
可以向 字串尾部 追加值。
127.0.0.1:6379> get key "redis" 127.0.0.1:6379> append key world (integer) 10 127.0.0.1:6379> get key "redisworld" 複製程式碼
1.2.2. 字串長度
strlen key
比如說,當前值為 redisworld
,所以返回值為 10
:
127.0.0.1:6379> get key "redisworld" 127.0.0.1:6379> strlen key (integer) 10 複製程式碼
1.2.3. 設定並返回原值
getset key value
getset
和 set
一樣會 設定值 ,但是不同的是,它同時會返回 鍵原來的值 ,例如:
127.0.0.1:6379> getset hello world (nil) 127.0.0.1:6379> getset hello redis "world" 複製程式碼
1.2.4. 設定指定位置的字元
setrange key offeset value
下面操作將值由 pest
變為了 best
:
127.0.0.1:6379> set redis pest OK 127.0.0.1:6379> setrange redis 0 b (integer) 4 127.0.0.1:6379> get redis "best" 複製程式碼
1.2.5. 獲取部分字串
getrange key start end
start
和 end
分別是 開始 和 結束 的 偏移量 , 偏移量 從 0
開始計算,例如獲取值 best
的 前兩個字元 的命令如下:
127.0.0.1:6379> getrange redis 0 1 "be" 複製程式碼
最後給出 字串 型別命令的 時間複雜度 說明:

2. 內部編碼
字串型別的 內部編碼 有 3
種:
-
int:
8
個位元組的 長整型 。 -
embstr: 小於等於
39
個位元組的字串。 -
raw: 大於
39
個位元組的字串。
Redis
會根據當前值的 型別 和 長度 決定使用哪種 內部編碼實現 。
- 整數型別
127.0.0.1:6379> set key 8653 OK 127.0.0.1:6379> object encoding key "int" 複製程式碼
- 短字串
#小於等於39個位元組的字串:embstr 127.0.0.1:6379> set key "hello,world" OK 127.0.0.1:6379> object encoding key "embstr" 複製程式碼
- 長字串
#大於39個位元組的字串:raw 127.0.0.1:6379> set key "one string greater than 39 byte........." OK 127.0.0.1:6379> object encoding key "raw" 127.0.0.1:6379> strlen key (integer) 40 複製程式碼
3. 典型使用場景
3.1. 快取功能
下面是一種比較典型的 快取 使用場景,其中 Redis
作為 快取層 , SQL/">MySQL
作為 儲存層 ,絕大部分請求的資料都是從 Redis
中獲取。由於 Redis
具有支撐 高併發 的特性,所以快取通常能起到 加速讀寫 和 降低後端壓力 的作用。

整個功能的虛擬碼如下:
public UserInfo getUserInfo(long id) { String userRedisKey = "user:info:" + id; String value = redis.get(userRedisKey); UserInfo userInfo; if (value != null) { userInfo = deserialize(value); } else { userInfo = mysql.get(id);if (userInfo != null) { redis.setex(userRedisKey, 3600, serialize(userInfo)); } return userInfo; } } 複製程式碼
3.2. 計數
許多應用都會使用 Redis
作為 計數 的基礎工具,它可以實現 快速計數 、 查詢快取 的功能,同時資料可以 非同步落地 到其他 資料來源 。一般來說,視訊播放數系統,就是使用 Redis
作為 視訊播放數計數 的基礎元件,使用者每播放一次視訊,相應的視訊播放數就會自增 1
。
public long incrVideoCounter (long id) { String key = "video:playCount:" + id; return redis.incr(key); } 複製程式碼
實際上,一個真實的 計數系統 要考慮的問題會很多: 防作弊 、按照 不同維度 計數, 資料持久化 到 底層資料來源 等。
3.3. 共享Session
一個 分散式 Web
服務將使用者的 Session
資訊(例如 使用者登入資訊 )儲存在 各自 的伺服器中。這樣會造成一個問題,出於 負載均衡 的考慮, 分散式服務 會將使用者的訪問 均衡 到不同伺服器上,使用者 重新整理一次訪問 可能會發現需要 重新登入 ,這個問題是使用者無法容忍的。

為了解決這個問題,可以使用 Redis
將使用者的 Session
進行 集中管理 。在這種模式下,只要保證 Redis
是 高可用 和 擴充套件性的 ,每次使用者 更新 或者 查詢 登入資訊都直接從 Redis
中集中獲取。

3.4. 限速
很多應用出於安全的考慮,會在每次進行登入時,讓使用者輸入 手機驗證碼 ,從而確定是否是使用者本人。但是為了 簡訊介面 不被 頻繁訪問 ,會 限制 使用者每分鐘獲取 驗證碼 的頻率。例如一分鐘不能超過 5
次,如圖所示:
此功能可以使用 Redis
來實現,虛擬碼如下:
String phoneNum = "138xxxxxxxx"; String key = "shortMsg:limit:" + phoneNum; // SET key value EX 60 NX boolean isExists = redis.set(key, 1, "EX 60", "NX"); if (isExists != null || redis.incr(key) <= 5) { // 通過 } else { // 限速 } 複製程式碼
上述就是利用 Redis
實現了 限速功能 ,例如 一些網站 限制一個 IP
地址不能在 一秒鐘之內 訪問超過 n
次也可以採用 類似 的思路。
小結
本文簡單的介紹了 Redis
的 字串資料結構 的 基本命令 , 內部編碼 和 相關應用場景 。