深刻剖析Redis系列(五) - Redis數據結構之字符串

前言

字符串類型Redis 最基礎的數據結構。字符串類型 的值實際能夠是 字符串簡單複雜 的字符串,例如 JSONXML)、數字(整數、浮點數),甚至是 二進制(圖片、音頻、視頻),可是值最大不能超過 512MBjava

其餘文章

正文

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次命令時間

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

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

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"
複製代碼

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

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 具備支撐 高併發 的特性,因此緩存一般能起到 加速讀寫下降後端壓力 的做用。

整個功能的僞代碼以下:

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 開發與運維》


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

零壹技術棧

本賬號將持續分享後端技術乾貨,包括虛擬機基礎,多線程編程,高性能框架,異步、緩存和消息中間件,分佈式和微服務,架構學習和進階等學習資料和文章。

相關文章
相關標籤/搜索