Redis 字符串對象

2018-08-02 14:48 更新

字符串對象的編碼可以是 int 、 raw 或者 embstr 。

如果一個字符串對象保存的是整數(shù)值, 并且這個整數(shù)值可以用 long 類型來表示, 那么字符串對象會將整數(shù)值保存在字符串對象結(jié)構(gòu)的 ptr屬性里面(將 void* 轉(zhuǎn)換成 long ), 并將字符串對象的編碼設(shè)置為 int 。

舉個例子, 如果我們執(zhí)行以下 SET 命令, 那么服務(wù)器將創(chuàng)建一個如圖 8-1 所示的 int 編碼的字符串對象作為 number 鍵的值:

redis> SET number 10086
OK

redis> OBJECT ENCODING number
"int"

如果字符串對象保存的是一個字符串值, 并且這個字符串值的長度大于 39 字節(jié), 那么字符串對象將使用一個簡單動態(tài)字符串(SDS)來保存這個字符串值, 并將對象的編碼設(shè)置為 raw 。

舉個例子, 如果我們執(zhí)行以下命令, 那么服務(wù)器將創(chuàng)建一個如圖 8-2 所示的 raw 編碼的字符串對象作為 story 鍵的值:

redis> SET story "Long, long, long ago there lived a king ..."
OK

redis> STRLEN story
(integer) 43

redis> OBJECT ENCODING story
"raw"

如果字符串對象保存的是一個字符串值, 并且這個字符串值的長度小于等于 39 字節(jié), 那么字符串對象將使用 embstr 編碼的方式來保存這個字符串值。

embstr 編碼是專門用于保存短字符串的一種優(yōu)化編碼方式, 這種編碼和 raw 編碼一樣, 都使用 redisObject 結(jié)構(gòu)和 sdshdr 結(jié)構(gòu)來表示字符串對象, 但 raw 編碼會調(diào)用兩次內(nèi)存分配函數(shù)來分別創(chuàng)建 redisObject 結(jié)構(gòu)和 sdshdr 結(jié)構(gòu), 而 embstr 編碼則通過調(diào)用一次內(nèi)存分配函數(shù)來分配一塊連續(xù)的空間, 空間中依次包含 redisObject 和 sdshdr 兩個結(jié)構(gòu), 如圖 8-3 所示。

embstr 編碼的字符串對象在執(zhí)行命令時, 產(chǎn)生的效果和 raw 編碼的字符串對象執(zhí)行命令時產(chǎn)生的效果是相同的, 但使用 embstr 編碼的字符串對象來保存短字符串值有以下好處:

  1. embstr 編碼將創(chuàng)建字符串對象所需的內(nèi)存分配次數(shù)從 raw 編碼的兩次降低為一次。
  2. 釋放 embstr 編碼的字符串對象只需要調(diào)用一次內(nèi)存釋放函數(shù), 而釋放 raw 編碼的字符串對象需要調(diào)用兩次內(nèi)存釋放函數(shù)。
  3. 因為 embstr 編碼的字符串對象的所有數(shù)據(jù)都保存在一塊連續(xù)的內(nèi)存里面, 所以這種編碼的字符串對象比起 raw 編碼的字符串對象能夠更好地利用緩存帶來的優(yōu)勢。

作為例子, 以下命令創(chuàng)建了一個 embstr 編碼的字符串對象作為 msg 鍵的值, 值對象的樣子如圖 8-4 所示:

redis> SET msg "hello"
OK

redis> OBJECT ENCODING msg
"embstr"

最后要說的是, 可以用 long double 類型表示的浮點數(shù)在 Redis 中也是作為字符串值來保存的: 如果我們要保存一個浮點數(shù)到字符串對象里面, 那么程序會先將這個浮點數(shù)轉(zhuǎn)換成字符串值, 然后再保存起轉(zhuǎn)換所得的字符串值。

舉個例子, 執(zhí)行以下代碼將創(chuàng)建一個包含 3.14 的字符串表示 "3.14" 的字符串對象:

redis> SET pi 3.14
OK

redis> OBJECT ENCODING pi
"embstr"

在有需要的時候, 程序會將保存在字符串對象里面的字符串值轉(zhuǎn)換回浮點數(shù)值, 執(zhí)行某些操作, 然后再將執(zhí)行操作所得的浮點數(shù)值轉(zhuǎn)換回字符串值, 并繼續(xù)保存在字符串對象里面。

舉個例子, 如果我們執(zhí)行以下代碼的話:

redis> INCRBYFLOAT pi 2.0
"5.14"

redis> OBJECT ENCODING pi
"embstr"

那么程序首先會取出字符串對象里面保存的字符串值 "3.14" , 將它轉(zhuǎn)換回浮點數(shù)值 3.14 , 然后把 3.14 和 2.0 相加得出的值 5.14 轉(zhuǎn)換成字符串 "5.14" , 并將這個 "5.14" 保存到字符串對象里面。

表 8-6 總結(jié)并列出了字符串對象保存各種不同類型的值所使用的編碼方式。


表 8-6 字符串對象保存各類型值的編碼方式

編碼
可以用 long 類型保存的整數(shù)。 int
可以用 long double 類型保存的浮點數(shù)。 embstr 或者 raw
字符串值, 或者因為長度太大而沒辦法用 long 類型表示的整數(shù), 又或者因為長度太大而沒辦法用long double 類型表示的浮點數(shù)。 embstr 或者 raw

編碼的轉(zhuǎn)換

int 編碼的字符串對象和 embstr 編碼的字符串對象在條件滿足的情況下, 會被轉(zhuǎn)換為 raw 編碼的字符串對象。

對于 int 編碼的字符串對象來說, 如果我們向?qū)ο髨?zhí)行了一些命令, 使得這個對象保存的不再是整數(shù)值, 而是一個字符串值, 那么字符串對象的編碼將從 int 變?yōu)?nbsp;raw 。

在下面的示例中, 我們通過 APPEND 命令, 向一個保存整數(shù)值的字符串對象追加了一個字符串值, 因為追加操作只能對字符串值執(zhí)行, 所以程序會先將之前保存的整數(shù)值 10086 轉(zhuǎn)換為字符串值 "10086" , 然后再執(zhí)行追加操作, 操作的執(zhí)行結(jié)果就是一個 raw 編碼的、保存了字符串值的字符串對象:

redis> SET number 10086
OK

redis> OBJECT ENCODING number
"int"

redis> APPEND number " is a good number!"
(integer) 23

redis> GET number
"10086 is a good number!"

redis> OBJECT ENCODING number
"raw"

另外, 因為 Redis 沒有為 embstr 編碼的字符串對象編寫任何相應(yīng)的修改程序 (只有 int 編碼的字符串對象和 raw 編碼的字符串對象有這些程序), 所以 embstr 編碼的字符串對象實際上是只讀的: 當(dāng)我們對 embstr 編碼的字符串對象執(zhí)行任何修改命令時, 程序會先將對象的編碼從 embstr 轉(zhuǎn)換成 raw , 然后再執(zhí)行修改命令; 因為這個原因, embstr 編碼的字符串對象在執(zhí)行修改命令之后, 總會變成一個 raw 編碼的字符串對象。

以下代碼展示了一個 embstr 編碼的字符串對象在執(zhí)行 APPEND 命令之后, 對象的編碼從 embstr 變?yōu)?nbsp;raw 的例子:

redis> SET msg "hello world"
OK

redis> OBJECT ENCODING msg
"embstr"

redis> APPEND msg " again!"
(integer) 18

redis> OBJECT ENCODING msg
"raw"

字符串命令的實現(xiàn)

因為字符串鍵的值為字符串對象, 所以用于字符串鍵的所有命令都是針對字符串對象來構(gòu)建的, 表 8-7 列舉了其中一部分字符串命令, 以及這些命令在不同編碼的字符串對象下的實現(xiàn)方法。


表 8-7 字符串命令的實現(xiàn)

命令 int 編碼的實現(xiàn)方法 embstr 編碼的實現(xiàn)方法 raw 編碼的實現(xiàn)方法
SET 使用 int 編碼保存值。 使用 embstr 編碼保存值。 使用 raw 編碼保存值。
GET 拷貝對象所保存的整數(shù)值, 將這個拷貝轉(zhuǎn)換成字符串值, 然后向客戶端返回這個字符串值。 直接向客戶端返回字符串值。 直接向客戶端返回字符串值。
APPEND 將對象轉(zhuǎn)換成 raw 編碼, 然后按raw 編碼的方式執(zhí)行此操作。 將對象轉(zhuǎn)換成 raw 編碼, 然后按raw 編碼的方式執(zhí)行此操作。 調(diào)用 sdscatlen 函數(shù), 將給定字符串追加到現(xiàn)有字符串的末尾。
INCRBYFLOAT 取出整數(shù)值并將其轉(zhuǎn)換成 longdouble 類型的浮點數(shù), 對這個浮點數(shù)進行加法計算, 然后將得出的浮點數(shù)結(jié)果保存起來。 取出字符串值并嘗試將其轉(zhuǎn)換成long double 類型的浮點數(shù), 對這個浮點數(shù)進行加法計算, 然后將得出的浮點數(shù)結(jié)果保存起來。 如果字符串值不能被轉(zhuǎn)換成浮點數(shù), 那么向客戶端返回一個錯誤。 取出字符串值并嘗試將其轉(zhuǎn)換成 longdouble 類型的浮點數(shù), 對這個浮點數(shù)進行加法計算, 然后將得出的浮點數(shù)結(jié)果保存起來。 如果字符串值不能被轉(zhuǎn)換成浮點數(shù), 那么向客戶端返回一個錯誤。
INCRBY 對整數(shù)值進行加法計算, 得出的計算結(jié)果會作為整數(shù)被保存起來。 embstr 編碼不能執(zhí)行此命令, 向客戶端返回一個錯誤。 raw 編碼不能執(zhí)行此命令, 向客戶端返回一個錯誤。
DECRBY 對整數(shù)值進行減法計算, 得出的計算結(jié)果會作為整數(shù)被保存起來。 embstr 編碼不能執(zhí)行此命令, 向客戶端返回一個錯誤。 raw 編碼不能執(zhí)行此命令, 向客戶端返回一個錯誤。
STRLEN 拷貝對象所保存的整數(shù)值, 將這個拷貝轉(zhuǎn)換成字符串值, 計算并返回這個字符串值的長度。 調(diào)用 sdslen 函數(shù), 返回字符串的長度。 調(diào)用 sdslen 函數(shù), 返回字符串的長度。
SETRANGE 將對象轉(zhuǎn)換成 raw 編碼, 然后按raw 編碼的方式執(zhí)行此命令。 將對象轉(zhuǎn)換成 raw 編碼, 然后按raw 編碼的方式執(zhí)行此命令。 將字符串特定索引上的值設(shè)置為給定的字符。
GETRANGE 拷貝對象所保存的整數(shù)值, 將這個拷貝轉(zhuǎn)換成字符串值, 然后取出并返回字符串指定索引上的字符。 直接取出并返回字符串指定索引上的字符。 直接取出并返回字符串指定索引上的字符。
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號