上一篇文章Redis之對象篇——Redis對象系統簡介簡單介紹了Redis
的對象系統。Redis
使用對象來表示數據庫中的鍵和值每一個對象都由一個redisObject
結構表示,該結構中和保存數據有關的三個屬性分別是type
屬性、 encoding
屬性和ptr
屬性。html
typedef struct redisObiect{ //類型 unsigned type:4; //編碼 unsigned encoding:4; //指向底層數據結構的指針 void *ptr; }
字符串對象是 Redis 中最基本的數據類型,也是咱們工做中最經常使用的數據類型。redis中的鍵都是字符串對象,並且其餘幾種數據結構都是在字符串對象基礎上構建的。字符串對象的值實際能夠是字符串、數字、甚至是二進制,最大不能超過512MB 。redis
Redis字符串對象底層的數據結構實現主要是int和簡單動態字符串SDS(這個字符串,和咱們認識的C字符串不太同樣,瞭解具體請看圖解Redis之數據結構篇——簡單動態字符串SDS),其經過不一樣的編碼方式映射到不一樣的數據結構。shell
字符串對象的內部編碼有3種 :int
、raw
和embstr
。Redis會根據當前值的類型和長度來決定使用哪一種編碼來實現。數據庫
若是一個字符串對象保存的是整數值,而且這個整數值能夠用long
類型來表示,那麼字符串對象會將整數值保存在字符串對象結構的ptr
屬性裏面(將void*
轉換成1ong
),並將字符串對象的編碼設置爲int
。後端
若是字符串對象保存的是一個字符串值,而且這個字符串值的長度大於32字節,那麼字符串對象將使用一個簡單動態字符串(SDS)來保存這個字符串值,並將對象的編碼設置爲raw
。緩存
若是字符串對象保存的是一個字符串值,而且這個字符申值的長度小於等於32字節,那麼字符串對象將使用一個簡單動態字符串(SDS)來保存這個字符串值,並將對象的編碼設置爲embstr
服務器
embstr
編碼是專門用於保存短字符串的一種優化編碼方式,咱們能夠看到embstr
和raw
編碼都會使用SDS
來保存值,但不一樣之處在於embstr
會經過一次內存分配函數來分配一塊連續的內存空間來保存redisObject
和SDS
。而raw
編碼會經過調用兩次內存分配函數來分別分配兩塊空間來保存redisObject
和SDS
。Redis這樣作會有不少好處。網絡
embstr
編碼將建立字符串對象所需的內存分配次數從raw編碼的兩次下降爲一次embstr
編碼的字符串對象一樣只須要調用一次內存釋放函數embstr
編碼的字符串對象的全部數據都保存在一塊連續的內存裏面能夠更好的利用CPU緩存提高性能。Redis中根據數據類型和長度來使用不一樣的編碼和數據結構存儲存在於Redis中的每一種對象類型上。其這種小細節上的優化令我歎服不止,後續咱們會看到Redis中處處都是這種內存與性能上的小細節優化!session
字符串類型的命令比較多 ,咱們先來了解幾個平常開發中經常使用的。數據結構
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可能會不推薦使用而且最終拋棄這幾個命令。
get key
返回key
的value
。若是key不存在,返回特殊值nil
。若是key
的value
不是string,就返回錯誤,由於GET
只處理string類型的values
。
redis> GET nokey (nil) redis> SET mykey "Hello World" OK redis> GET mykey "Hello World"
因爲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"
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阻塞或者網絡擁塞。
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
對於經常使用的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是字符串的長度) |
reids字符串的使用場景應該是最爲普遍的,甚至有些對redis其它幾種對象不太熟悉的人,基本全部場景都會使用字符串(序列化一下直接扔進去)。在衆多的使用場景中總結一下大概分如下幾種。
如上圖,Redis常常做爲緩存層,來緩存一些熱點數據。來加速讀寫性能從而下降後端的壓力。通常在讀取數據的時候會先從Redis中讀取,若是Redis中沒有,再從數據庫中讀取。在Redis做爲緩存層使用的時候,必須注意一些問題,如:緩存穿透、雪崩以及緩存更新問題......
計數器\限速器\分佈式ID等主要是利用Redis字符串自增自減的特性。
一般在單體系統中,Web服務將會用戶的Session信息(例如用戶登陸信息)保存在本身的服務器中。可是在分佈式系統中,這樣作會有問題。由於分佈式系統一般有不少個服務,每一個服務又會同時部署在多臺機器上,經過負載均衡機制將將用戶的訪問均衡到不一樣服務器上。這個時候用戶的請求可能分發到不一樣的服務器上,從而致使用戶登陸保存Session是在一臺服務器上,而讀取Session是在另外一臺服務器上所以會讀不到Session。
這種問題一般的作法是把Session存到一個公共的地方,讓每一個Web服務,都去這個公共的地方存取Session。而Redis就能夠是這個公共的地方。(數據庫、memecache等均可以各有優缺點)。
因爲Redis字符串能夠存儲二進制數據的特性,所以也能夠用來存儲一些二進制數據。如圖片、 音頻、 視頻等。
《Redis設計與實現》
《Redis開發與運維》
《Redis官方文檔》