深入剖析Redis系列(五) - Redis資料結構之字串

零壹技術棧發表於2018-10-09

前言

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

深入剖析Redis系列(五) - Redis資料結構之字串

其他文章

正文

1. 相關命令

1.1. 常見命令

1.1.1. 設定值

set key value [ex seconds] [px milliseconds] [nx|xx]

set 命令有幾個選項:

  1. ex seconds:為 設定 秒級過期時間
  2. px milliseconds:為 設定 毫秒級過期時間
  3. nx:鍵必須 不存在,才可以設定成功,用於 新增
  4. xx:與 nx 相反,鍵必須 存在,才可以設定成功,用於 更新

除了 set 選項,Redis 還提供了 setexsetnx 兩個命令:

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 ...]

通過下面操作 批量獲取abcd 的值:

127.0.0.1:6379> mget a b c d
1) "1"
2) "2"
3) "3"
4) "4"
複製程式碼

批量操作 命令,可以有效提高 開發效率,假如沒有 mget 這樣的命令,要執行 nget 命令的過程和 耗時 如下:

n次get時間 = n次網路時間 + n次命令時間

深入剖析Redis系列(五) - Redis資料結構之字串

使用 mget 命令後,執行 nget 命令的過程和 耗時 如下:

n次get時間 = 1次網路時間 + n次命令時間

深入剖析Redis系列(五) - Redis資料結構之字串

Redis 可以支撐 每秒數萬讀寫操作,但這指的是 Redis 服務端 的處理能力,對於 客戶端 來說,一次命令除了 命令時間 還是有 網路時間

假設 網路時間1 毫秒,命令時間為 0.1 毫秒(按照每秒處理 1 萬條命令算),那麼執行 1000get 命令和 1mget 命令的區別如表所示:

操作 時間
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

getsetset 一樣會 設定值,但是不同的是,它同時會返回 鍵原來的值,例如:

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

startend 分別是 開始結束偏移量偏移量0 開始計算,例如獲取值 best前兩個字元 的命令如下:

127.0.0.1:6379> getrange redis 0 1
"be"
複製程式碼

最後給出 字串 型別命令的 時間複雜度 說明:

深入剖析Redis系列(五) - Redis資料結構之字串

2. 內部編碼

字串 型別的 內部編碼3 種:

  • int8 個位元組的 長整型

  • 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 具有支撐 高併發 的特性,所以快取通常能起到 加速讀寫降低後端壓力 的作用。

深入剖析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系列(五) - Redis資料結構之字串

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

深入剖析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 開發與運維》


歡迎關注技術公眾號: 零壹技術棧

零壹技術棧

本帳號將持續分享後端技術乾貨,包括虛擬機器基礎,多執行緒程式設計,高效能框架,非同步、快取和訊息中介軟體,分散式和微服務,架構學習和進階等學習資料和文章。

相關文章