前言
字串型別 是 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
作為 快取層,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
的 字串資料結構 的 基本命令,內部編碼 和 相關應用場景。
參考
《Redis 開發與運維》
歡迎關注技術公眾號: 零壹技術棧
本帳號將持續分享後端技術乾貨,包括虛擬機器基礎,多執行緒程式設計,高效能框架,非同步、快取和訊息中介軟體,分散式和微服務,架構學習和進階等學習資料和文章。