Redis對象——字符串

文章導航-readme

前言

    上一篇文章Redis之對象篇——Redis對象系統簡介簡單介紹了Redis的對象系統。Redis使用對象來表示數據庫中的鍵和值每一個對象都由一個redisObject結構表示,該結構中和保存數據有關的三個屬性分別是type屬性、 encoding屬性和ptr屬性。html

typedef struct redisObiect{
    //類型
    unsigned type:4;
    //編碼
    unsigned encoding:4;
    //指向底層數據結構的指針
    void *ptr;
}

    字符串對象是 Redis 中最基本的數據類型,也是咱們工做中最經常使用的數據類型。redis中的鍵都是字符串對象,並且其餘幾種數據結構都是在字符串對象基礎上構建的。字符串對象的值實際能夠是字符串、數字、甚至是二進制,最大不能超過512MB 。redis

1、內部實現

    Redis字符串對象底層的數據結構實現主要是int和簡單動態字符串SDS(這個字符串,和咱們認識的C字符串不太同樣,瞭解具體請看圖解Redis之數據結構篇——簡單動態字符串SDS),其經過不一樣的編碼方式映射到不一樣的數據結構。shell

字符串對象的內部編碼有3種 :intrawembstr。Redis會根據當前值的類型和長度來決定使用哪一種編碼來實現。數據庫

  1. 若是一個字符串對象保存的是整數值,而且這個整數值能夠用long類型來表示,那麼字符串對象會將整數值保存在字符串對象結構的ptr屬性裏面(將void*轉換成1ong),並將字符串對象的編碼設置爲int後端

  2. 若是字符串對象保存的是一個字符串值,而且這個字符串值的長度大於32字節,那麼字符串對象將使用一個簡單動態字符串(SDS)來保存這個字符串值,並將對象的編碼設置爲raw緩存

  3. 若是字符串對象保存的是一個字符串值,而且這個字符申值的長度小於等於32字節,那麼字符串對象將使用一個簡單動態字符串(SDS)來保存這個字符串值,並將對象的編碼設置爲embstr服務器

    embstr編碼是專門用於保存短字符串的一種優化編碼方式,咱們能夠看到embstrraw編碼都會使用SDS來保存值,但不一樣之處在於embstr會經過一次內存分配函數來分配一塊連續的內存空間來保存redisObjectSDS。而raw編碼會經過調用兩次內存分配函數來分別分配兩塊空間來保存redisObjectSDS。Redis這樣作會有不少好處。網絡

  • embstr編碼將建立字符串對象所需的內存分配次數從raw編碼的兩次下降爲一次
  • 釋放 embstr編碼的字符串對象一樣只須要調用一次內存釋放函數
  • 由於embstr編碼的字符串對象的全部數據都保存在一塊連續的內存裏面能夠更好的利用CPU緩存提高性能。

    Redis中根據數據類型和長度來使用不一樣的編碼和數據結構存儲存在於Redis中的每一種對象類型上。其這種小細節上的優化令我歎服不止,後續咱們會看到Redis中處處都是這種內存與性能上的小細節優化!session

2、經常使用命令

    字符串類型的命令比較多 ,咱們先來了解幾個平常開發中經常使用的。數據結構

1 設置值

redis> set testKey testValue
OK
set key value [ex seconds] [px milliseconds] [nx|xx]
  • ex seconds:爲鍵設置秒級過時時間。

    如命令:set username xiaoming ex 100至關於執行下面兩條命令

    SET username xiaoming 
    EXPIRE username 100

    set key value [ex seconds]操做是原子性的,相比連續執行上面兩個命令,它更快。

  • px milliseconds:爲鍵設置毫秒級過時時間。

  • nx:鍵必須不存在,才能夠設置成功,用於添加。

    //mykey 不存在
    redis> set mykey "Hello" nx
    (integer) 1
    //mykey 已經存在
    redis> set mykey "World" nx
    (integer) 0
    redis> GET mykey
    "Hello"
    redis>

    因爲set key value nx一樣是原子性的操做,所以能夠做爲分佈式鎖的一種實現方案。

  • xx:與nx相反,鍵必須存在,才能夠設置成功,用於更新

以上幾個命令的替代命令是SETNX, SETEX,PSETEX,可是因爲SET命令加上選項已經能夠徹底取代SETNX, SETEX,PSETEX的功能,因此在未來的版本中,redis可能會不推薦使用而且最終拋棄這幾個命令。

2 獲取值

get key

返回keyvalue。若是key不存在,返回特殊值nil。若是keyvalue不是string,就返回錯誤,由於GET只處理string類型的values

redis> GET nokey
(nil)
redis> SET mykey "Hello World"
OK
redis> GET mykey
"Hello World"

3 批量設置值

    因爲Redis目前的應用很是普遍,目前大多數公司對Redis的調用基本都會有一層本身的封裝,看起來就像是在調用本地緩存同樣,對於批量性的操做,一些對於Redis不太瞭解的可能就像使用本地緩存同樣進行循環set。這樣對性能是有很大的損耗的。實際上Redis提供了批量操做的命令。

MSET key value [key value ...]

對應給定的keys到他們相應的values上。MSET會用新的value替換已經存在的value,就像普通的SET命令同樣。若是不想覆蓋已經存在的values,可使用MSETNX key value [key value ...]

注意:MSET是原子的,因此全部給定的keys是一次性set的。客戶端不可能看到這種一部分keys被更新而另外的沒有改變的狀況。

redis> MSET key1 "Hello" key2 "World"
OK
redis> GET key1
"Hello"
redis> GET key2
"World"

4 批量獲取值

MGET key [key ...]

結果是按照傳入鍵的順序返回全部指定的key的value。對於每一個不對應string或者不存在的key,都返回特殊值nil。

redis> SET key1 "Hello"
OK
redis> SET key2 "World"
OK
redis> MGET key1 key2 nokey
1) "Hello"
2) "World"
3) (nil)

    Redis能夠支撐每秒數萬的讀寫操做,可是這指的是Redis服務端的處理能力,對於客戶端來講,一次命令除了命令時間仍是有網絡時間,如n次get操做

使用get命令

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

mget操做

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

而在實際開發中由於Redis的處理能力已經足夠高,性能瓶頸的因素每每是網絡。

學會使用批量操做,有助於提升效率,可是要掌握一個平衡的度,每次批量操做所發送的命令數並非無節制的因爲Redis是單線程架構,若是數量過多可能形成Redis阻塞或者網絡擁塞。

5 計數

incr key

對存儲在指定key的數值執行原子的加1操做。

若是指定的key不存在,那麼在執行incr操做以前,會先將它的值設定爲0

若是指定的key中存儲的值不是字符串類型(fix:)或者存儲的字符串類型不能表示爲一個整數,

那麼執行這個命令時服務器會返回一個錯誤(eq:(error) ERR value is not an integer or out of range)。

這個操做僅限於64位的有符號整型數據。

注意: 因爲redis並無一個明確的類型來表示整型數據,因此這個操做是一個字符串操做。

執行這個操做的時候,key對應存儲的字符串被解析爲10進制的64位有符號整型數據

事實上,Redis 內部採用整數形式(Integer representation)來存儲對應的整數值,因此對該類字符串值其實是用整數保存,也就不存在存儲整數的字符串表示(String representation)所帶來的額外消耗。

redis> SET mykey "1"
OK
redis> INCR mykey
(integer) 2
redis> GET mykey
"3"
redis>

除了incr命令, Redis提供了decr(自減)incrby(自增指定數字)decrby(自減指定數字)incrbyfloat(自增浮點數)

decr key
incrby key increment
decrby key decrement
incrbyfloat key increment

6 其它

    對於經常使用的redis字符串命令和一些其它的命令咱們列一個表格以便來更直觀的看到。

命令 描述 時間複雜度
set key value [ex seconds] [px milliseconds] [nx|xx] 設置值 O(1)
get key 獲取值 O(1)
del key [key ...] 刪除key O(N)(N是鍵的個數)
mset key [key value ...] 批量設置值 O(N)(N是鍵的個數)
mget key [key ...] 批量獲取值 O(N)(N是鍵的個數)
incr key 將 key 中儲存的數字值增一 O(1)
decr key 將 key 中儲存的數字值減一 O(1)
incrby key increment 將 key 所儲存的值加上給定的增量值(increment) O(1)
decrby key increment key 所儲存的值減去給定的減量值(decrement) O(1)
incrbyfloat key increment 將 key 所儲存的值加上給定的浮點增量值(increment) O(1)
append key value 若是 key 已經存在而且是一個字符串, APPEND 命令將指定的 value 追加到該 key 原來值(value)的末尾 O(1)
strlen key 返回 key 所儲存的字符串值的長度。 O(1)
setrange key offset value 用 value 參數覆寫給定 key 所儲存的字符串值,從偏移量 offset 開始 O(1)
getrange key start end 返回 key 中字符串值的子字符 O(N)(N是字符串的長度)

3、經常使用場景

    reids字符串的使用場景應該是最爲普遍的,甚至有些對redis其它幾種對象不太熟悉的人,基本全部場景都會使用字符串(序列化一下直接扔進去)。在衆多的使用場景中總結一下大概分如下幾種。

1. 做爲緩存層

    如上圖,Redis常常做爲緩存層,來緩存一些熱點數據。來加速讀寫性能從而下降後端的壓力。通常在讀取數據的時候會先從Redis中讀取,若是Redis中沒有,再從數據庫中讀取。在Redis做爲緩存層使用的時候,必須注意一些問題,如:緩存穿透、雪崩以及緩存更新問題......

2. 計數器\限速器\分佈式系統ID

    計數器\限速器\分佈式ID等主要是利用Redis字符串自增自減的特性。

  • 計數器:常常能夠被用來作計數器,如微博的評論數、點贊數、分享數,抖音做品的收藏數,京東商品的銷售量、評價數等。
  • 限速器:如驗證碼接口訪問頻率限制,用戶登錄時須要讓用戶輸入手機驗證碼,從而肯定是不是用戶本人,可是爲了短信接口不被頻繁訪問,會限制用戶每分鐘獲取驗證碼的頻率,例如一分鐘不能超過5次。
  • 分佈式ID:因爲Redis自增自減的操做是原子性的所以也常常在分佈式系統中用來生成惟一的訂單號、序列號等。

3. 分佈式系統共享session

    一般在單體系統中,Web服務將會用戶的Session信息(例如用戶登陸信息)保存在本身的服務器中。可是在分佈式系統中,這樣作會有問題。由於分佈式系統一般有不少個服務,每一個服務又會同時部署在多臺機器上,經過負載均衡機制將將用戶的訪問均衡到不一樣服務器上。這個時候用戶的請求可能分發到不一樣的服務器上,從而致使用戶登陸保存Session是在一臺服務器上,而讀取Session是在另外一臺服務器上所以會讀不到Session。

    這種問題一般的作法是把Session存到一個公共的地方,讓每一個Web服務,都去這個公共的地方存取Session。而Redis就能夠是這個公共的地方。(數據庫、memecache等均可以各有優缺點)。

4. 二進制存儲

    因爲Redis字符串能夠存儲二進制數據的特性,所以也能夠用來存儲一些二進制數據。如圖片、 音頻、 視頻等。

參考

《Redis設計與實現》

《Redis開發與運維》

《Redis官方文檔》

-----END-----

相關文章
相關標籤/搜索