redis中最見到的數據結構,它既可以存儲文字(比如“hello world”),又可以存儲數字(比如整數10086和浮點數3.14),還可以存儲二進制數據(比如10010100)
redis為這幾種類型的值分別設置了相應的操作命令,讓用戶可以針對不同的值做不同的處理
1.基本操作
為字符串鍵設置值
SET key value
將字符串鍵 key 的值設置為 value ,命令返回 OK 表示設置成功。
如果字符串鍵 key 已經存在,那么用新值覆蓋原來的舊值。
復雜度為 O(1) 。
redis> SET msg "hello world"
OK
redis> SET msg "goodbye" # 覆蓋原來的值 "hello world"
OK
SET 命令還支持可選的 NX 選項和 XX 選項:
-
如果給定了 NX 選項,那么命令僅在鍵 key 不存在的情況下,才進行設置操作;如果鍵 key 已經存在,那么 SET ... NX 命令不做動作(不會覆蓋舊值)。
-
如果給定了 XX 選項,那么命令僅在鍵 key 已經存在的情況下,才進行設置操作;如果鍵 key 不存在,那么 SET ... XX 命令不做動作(一定會覆蓋舊值)。
在給定 NX 選項和 XX 選項的情況下,SET 命令在設置成功時返回 OK ,設置失敗時返回 nil 。
redis> SET nx-str "this will fail" XX # 鍵不存在,指定 XX 選項導致設置失敗
(nil)
redis> SET nx-str "this will success" NX # 鍵不存在,所以指定 NX 選項是可行的
OK
redis> SET nx-str "this will fail" NX # 鍵已經存在,指定 NX 選項導致設置失敗
(nil)
redis> SET nx-str "this will success again!" XX # 鍵已經存在,指定 XX 選項是可行的
OK
獲取字符串的值
GET key
返回字符串鍵 key 儲存的值。
復雜度為 O(1) 。
redis> SET msg "hello world"
OK
redis> GET msg
hello world
redis> SET number 10086
OK
redis> GET number
10086
示例:使用 Redis 來進行緩存
我們可以使用 Redis 來緩存一些經常會被用到、或者需要耗 費大量資源的內容,通過將這些內容放到Redis 里面(也即是內存里面),程序可以以極快的速度取得 這些內容。
舉個例子,對於一個網站來說,如果某個頁面經常會被訪問到,或者創建頁面時耗費的資源比較多(比
如需要多次訪問數據庫、生成時間比較長,等等),那么我們可以使用 Redis 將這個頁面緩存起來,減
輕網站的負擔,降低網站的延遲值。
@app.route("/")
def index():
cached_content = cache.get('index') # 嘗試從緩存里面獲取被緩存的頁面
if cached_content: # 緩存存在,直接返回頁面
return cached_content
else:
content = fetch_and_create_index() # 頁面沒有被緩存,訪問數據庫並重新生成頁面
cache.put('index', content) # 緩存頁面,方便下次取出
return content # 返回頁面
緩存程序的 API 及其實現
API
|
效果
|
實現
|
Cache(client)
|
設置緩存程序使用的客戶端。
|
|
Cache.put(name, content)
|
把指定的內容放到緩存里面,並使用 name 來 命名它,以便之后取出。
|
調用 SET 命令。
|
Cache.get(name)
|
從緩存中取出以 name 命名的內容。
|
調用 GET 命令。
|
緩存程序的具體實現請參考 cache.py 。
在之后我們還會實現根據時間自動失效的緩存。
cache.py
# coding: utf-8
class Cache:
def __init__(self, client):
self.client = client
def put(self, name, content):
self.client.set(name, content)
def get(self, name):
return self.client.get(name)
僅在鍵不存在的情況下進行設置
SETNX key value
僅在鍵 key 不存在的情況下,將鍵 key 的值設置為 value ,效果和 SET key value NX 一樣。
NX 的意思為“Not eXists”(不存在)。
鍵不存在並且設置成功時,命令返回 1 ;因為鍵已經存在而導致設置失敗時,命令返回 0 。
復雜度為 O(1) 。
redis> SETNX new-key "i am a new key!"
1
redis> SETNX new-key "another new key here!" # 鍵已經存在,設置失敗
0
redis> GET new-key # 鍵的值沒有改變
i am a new key!
同時設置或獲取多個字符串鍵的值
命令
|
效果
|
復雜度
|
MSET key value [key value ...]
|
一次為一個或多個字符串鍵設置 值,效果和同時執行多個 SET 命 令一樣。 命令返回 OK
|
O(N),N 為要設置的字符串鍵 數量。
|
MGET key [key ...]
|
一次返回一個或多個字符串 鍵的 值,效果和同時執行多個 GET 命 令一樣。
|
O(N),N 為要獲取的字符串鍵 數量。
|
示例:設置或獲取個人信息
很多網站都會給你一個地方,填寫自己的個人信息、聯系信息、個人簡介等等,比如右圖就是某個網站上的個人信息 設置頁面。
通過將每項信息儲存在一個字符串鍵里面(比如電子郵件在 huangz::email 鍵、個人網站在 huangz::homepage 鍵、公司在huangz::company 鍵,等等),我們可以通過調用 MSET 來一次性設置多個項,並使用MGET 來一次性獲取多個項的信息。
MSET huangz::email "
huangz1990@gmail.com" huangz::homepage "
http://huangz.
me/" huangz::company "FakeCompany" huangz::position "Programmer" huangz::
location "廣東" huangz::sign "time waits for no one"
MGET huangz::email huangz::homepage huangz::company huangz::position ...
鍵的命名
因為 Redis 的數據庫不能出現兩個同名的鍵,所以我們通常會使用 field1::field2::field3 這樣的格式來區分同一類型的多個字符串鍵。
舉個例子,像前面儲存個人信息例子,因為網站里面不可能只有 huangz 一個用戶,所以我們不能用email 鍵來直接儲存 huangz 的郵件地址,而是使用 huangz::email ,這樣 huangz 的郵件地址就不會和其他用戶的郵件地址發生沖突 —— 比如用戶名為 peter 的用戶可以將它的郵件地址儲存到peter::email 鍵,而用戶名為 jack 的用戶也可以將它的郵件地址儲存到 jack::email 鍵,大家各不相關,互不影響。
一些更為復雜的鍵名例子: user::10086::info ,ID 為 10086 的用戶的信息; news::sport::cache ,新聞網站體育分類的緩存; message::123321::content ,ID 為 123321 的消息的內容。
:: 是比較常用的分割符,你也可以 選擇自己喜歡的其他分割符來命名鍵,
比如斜線 huangz/email 、豎線 huangz|email 、或者面向對象風格的 huangz.email 。
一次設置多個不存在的鍵
MSETNX key value [key value ...]
只有在所有給定鍵都不存在的情況下, MSETNX 會為所有給定鍵設置值,效果和同時執行多個SETNX 一樣。如果給定的鍵至少有一個是存在的,那么 MSETNX 將不執行任何設置操作。
返回 1 表示設置成功,返回 0 表示設置失敗。復雜度為 O(N) , N 為給定的鍵數量。
redis> MSETNX nx-1 "hello" nx-2 "world" nx-3 "good luck"
1
redis> SET ex-key "bad key here"
OK
redis> MSETNX nx-4 "apple" nx-5 "banana" ex-key "cherry" nx-6 "durian"
0
因為 ex-key 鍵已經存在,所以第二個 MSETNX 會執行失敗,所有鍵都不會被設置。
設置新值並返回舊值
GETSET key new-value
將字符串鍵的值設置為 new-value ,並返回字符串鍵在設置新值之前儲存的舊值(old value)。
復雜度為 O(1) 。
redis> SET getset-str "i'm old value" # 先給字符串鍵設置一個值
OK
redis> GETSET getset-str "i'm new value" # 更新字符串鍵的值,並返回之前儲存的舊值
i'm old value
redis> GET getset-str # 確認一下,新值已被設置
i'm new value
用偽代碼表示 GETSET 的定義
def GETSET(key, new-value):
old-value = GET(key) # 記錄舊值
SET(key, new-value) # 設置新值
return old-value # 返回舊值
追加內容到字符串末尾
APPEND key value
將值 value 推入到字符串鍵 key 已儲存內容的末尾。
O(N), 其中 N 為被推入值的長度。
redis> SET myPhone "nokia"
OK
redis> APPEND myPhone "-1110"
(integer) 10
redis> GET myPhone
"nokia-1110"
返回值的長度
STRLEN key
返回字符串鍵 key 儲存的值的長度。
因為 Redis 會記錄每個字符串值的長度,所以獲取該值的復雜度為 O(1) 。
redis> SET msg "hello"
OK
redis> STRLEN msg
(integer) 5
redis> APPEND msg " world"
(integer) 11
redis> STRLEN msg
(integer) 11
2.索引和范圍
索引
字符串的索引(index)以 0 為開始,從字符串的開頭向字符串的結尾依次遞增,字符串第一個字符的索引為 0 ,字符串最后一個字符的索引 為 N-1 ,其中 N 為字符串的長度。
除了(正數)索引之外,字符串 還有負數索引:負數索引以 -1 為開始,從字符串的結尾向字符串的開頭依次遞減,字符串的最后一個字符的索引 為 -N ,其中 N 為字符串的長度。
范圍設置
SETRANGE key index value
從索引 index 開始,用 value 覆寫(overwrite)給定鍵 key 所儲存的字符串值。只接受正數索引。
命令返回覆寫之后,字符串 值的長度。復雜度為 O(N), N 為 value 的長度。
redis> SET msg "hello"
OK
redis> SETRANGE msg 1 "appy"
(integer) 5
redis> GET msg
"happy
范圍取值
GETRANGE key start end
返回鍵 key 儲存的字符串值中,位於 start 和 end 兩個索引之間的內容(閉區間,start 和 end 會被包括在內)。和 SETRANGE 只接受正數索引不同, GETRANGE 的索引可以是正數或者 負數。
復雜度為 O(N) , N 為被選中內容的長度。
redis> SET msg "hello world"
OK
redis> GETRANGE msg 0 4
"hello"
redis> GETRANGE msg -5 -1
"world"
3.數字操作
設置和獲取數字
只要儲存在字符串鍵里面的值可以被解釋為 64 位整數,或者 IEEE-754 標准的 64 位浮點數,
那么用戶就可以對這個字符串鍵執行針對數字值的命令。
值
|
能否執行數字值命令?
|
原因
|
10086
|
可以
|
值可以被解釋為整數
|
3.14
|
可以
|
值可以被解釋為浮點數
|
+123
|
可以
|
值可以被解釋為整數
|
123456789123456789123456789
|
不可以
|
值太大,沒辦法使用 64 位整數來儲存
|
2.0e7
|
不可以
|
Redis 不解釋以科學記數法表示的浮點數
|
123ABC
|
不可以
|
值包含文字
|
ABC
|
不可以
|
值為文字
|
增加或者減少數字的值
對於一個保存着數字的字符串 鍵 key ,我們可以使用 INCRBY 命令來增加它的值,或者使用 DECRBY命令來減少它的值。
INCRBY key increment
O(1)
DECRBY key decrement
命令
|
效果
|
復雜度
|
INCRBY key increment
|
將 key 所儲存的值加上增量 increment ,命令返回操作執行之后,鍵 key 的當前值。
|
O(1)
|
DECRBY key decrement
|
將 key 所儲存的值減去減量 decrement ,命令返回操作執行之后,鍵 key 的當前值。
|
O(1)
|
如果執行 INCRBY 或者 DECRBY 時,鍵 key 不存在,那么命令會將 鍵 key 的
值初始化為 0 ,然后再執行增加或者減少操作。
INCRBY / DECRBY 示例
redis> INCRBY num 100 # 鍵 num 不存在,命令先將 num 的值初始化為 0 ,
(integer) 100 # 然后再執行加 100 操作
redis> INCRBY num 25 # 將值再加上 25
(integer) 125
redis> DECRBY num 10 # 將值減少 10
(integer) 115
redis> DECRBY num 50 # 將值減少 50
(integer) 65
增一和減一
因為針對數字值的增一和減一操作非常常見,所以 Redis 特別為這兩個操作創建了 INCR 命令和 DECR 命令。
命令
|
效果
|
復雜度
|
INCR key
|
等同於執行 INCRBY key 1
|
O(1)
|
DECR key
|
等同於執行 DECRBY key 1
|
O(1)
|
redis> SET num 10
OK
redis> INCR num (integer)
11
redis> DECR num (integer)
10
計數器 API 及其實現
API
|
效果
|
實現
|
Counter(name, client)
|
設置計數器的名字以及客戶端。
|
|
Counter.incr()
|
將計數器的值增一,然后返回計數器的值。
|
調用 INCR 命令。
|
Counter.get()
|
返回計數器當前的值。
|
調用 GET 命令。
|
Counter.reset(n=0)
|
將計數器的值重置為 n ,默認重置為 0 。
|
調用 GETSET 命令。 雖然使用 SET 命令也可以達到重置的效果,但 使用 GETSET 可以在重置計數器的同時獲得 計數器之前的值,這有時候會有用。
|
c = Counter('page-counter', redis_client) # 創建一個名為 page-counter 的計數器
c.incr() # => 1
c.incr() # => 2
計數器實現的完整源碼請查看 counter.py 文件。
# encoding: utf-8
class Counter:
def __init__(self, key, client):
self.key = key
self.client = client
def incr(self, n=1):
counter = self.client.incr(self.key, n)
return int(counter)
def decr(self, n=1):
counter = self.client.decr(self.key, n)
return int(counter)
def reset(self, n=0):
counter = self.client.getset(self.key, n)
if counter is None:
counter = 0
return int(counter)
def get(self):
counter = self.client.get(self.key)
if counter is None:
示例:id 生成器
很多網站在創建新條目的時候,都會使用 id 生成器來為條目創建唯一標識符。
舉個例子,對於一個論壇來說,每注冊一個新用戶,論壇都會為這個新用戶創建一個用戶 id ,比如12345 ,然后訪問 /user/12345 就可以看到這個用戶的個人頁面。
又比如說,當論壇里的用戶創建一個新帖子的時候,論壇都會為這個新帖子創建一個帖子 id ,比如10086 ,然后訪問 /topic/10086 就可以看到這個帖子的內容。
被創建的 id 通常都是連續的,比如說,如果最新創建的 id 為 1003 ,那么下一個生成的 id 就會是1004 ,再下一個 id 就是 1005 ,以此類推。
id 生成器 API 及其實現
API
|
效果
|
實現
|
IdGenerator(name, client)
|
設置 id 生成器的名字和客戶端。
|
|
IdGenerator.gen()
|
生成一個新的自增 id 。
|
調用 INCR 命令。
|
IdGenerator.init(n)
|
保留前 n 個 id ,防止搶注,需要在系統開始運作 前執行,否則會出現重復 id 。 舉個例子,如果要保留前一萬個 id ,那么就需要執 行 IdGenerator.init(10000),這樣生成器創建的 id 就會從 10001 開始。
|
調用 SET 命令。
|
generator = IdGenerator('user-id', redis_client) # 創建一個用戶 id 生成器
generator.init(10000) # 保留前一萬個 id
generator.gen() # => 10001
generator.gen() # => 10002 id 生成器的源代碼可以在 id_generator.py 找到
id_generator.py
# coding: utf-8
class IdGenerator:
def __init__(self, key, client):
self.key = key
self.client = client
def init(self, n):
self.client.set(self.key, n)
def gen(self):
new_id = self.client.incr(self.key)
return int(new_id)
浮點數的自增和自減
INCRBYFLOAT key increment
為字符串鍵 key 儲存的值加上浮點數增量 increment ,命令返回操作執行之后,鍵 key 的值。
沒有相應的 DECRBYFLOAT ,但可以通過給定負值來達到 DECRBYFLOAT 的效果。
復雜度為 O(1) 。
redis> SET num 10
OK
redis> INCRBYFLOAT num 3.14
"13.14"
redis> INCRBYFLOAT num -2.04 # 通過傳遞負值來達到做減法的效果
"11.1"
注意事項
即使字符串鍵儲存的是數字值,它也可以執行 APPEND、STRLEN、SETRANGE 和 GETRANGE 。
當用戶針對一個數字值執行這些命令的時候,Redis 會先將數字值轉換為字符串,然后再執行命令。
redis> SET number 123
OK
redis> STRLEN number # 轉換為 "123" ,然后計算這個字符串的長度
3
redis > APPEND number 456 # 轉換為 "123" ,然后與 "456" 進行拼接
6
redis> GET number
123456
4.二進制數據操作
設置和獲取二進制數據
SET 、GET 、SETNX、 APPEND 等命令同樣可以用於設置二進制數據。
# 因為 Redis 自帶的客戶端 redis-cli 沒辦法方便的設置二進制數據
# 所以這里使用 Python 客戶端來進行
>>> import redis
>>> r = redis.Redis()
>>> r.set('bits', 0b10010100) # 將字符串鍵 bits 的值設置為二進制 10010100
True
>>> bin(int(r.get('bits'))) # 獲取字符串鍵 bits 儲存的二進制值(需要進行轉換)
'0b10010100'
>>> r.append('bits', 0b111) # 將 0b111 (也即是十進制的 7)推入到 bits 已有二進制位的末尾
4L
>>> bin(int(r.get('bits'))) # 推入之前的值為 0b10010100 = 148
'0b10111001111' # 推入之后的值為 0b10111001111 = 1487
二進制位的索引
和儲存文字時一樣,字符串鍵在儲存二進制位時,索引也是從 0 開始的。
但是和儲存文字時,索引從左到右依次遞增不同,當字符串鍵儲存的是二進制位時,二進制位的索引會 從左到右依次遞減。
設置二進制位的值
SETBIT key index value
將給定索引上的二進制位的值設置為 value ,命令返回被設置的位原來儲存的舊值。
復雜度為 O(1) 。
redis> SETBIT bits 2 1 (integer) 0
獲取二進制位的值
GETBIT key index
返回給定索引上的二進制位的值。
復雜度為 O(1) 。
redis> GETBIT bits 7
(integer) 1
redis> GETBIT bits 6
(integer) 0
redis> GETBIT bits 4
(integer) 1
計算值為 1 的二進制位的數量
BITCOUNT key [start] [end]
計算並返回字符串鍵儲存的值中,被設置為 1 的二進制位的數量。
一般情況下,給定的整個字符串鍵都會進行計數操作,但通過指定額外的 start 或 end 參數,可以讓計數只在特定索引范圍的位上進行。
start 和 end 參數的設置和 GETRANGE 命令類似,都可以使用負數值:比如 -1 表示最后一個位,而 -2表示倒數第二個位,以此 類推。
復雜度為 O(N) ,其中 N 為被計算二進制位的數量。
BITCOUNT 示例
帶有 start 和 end 參數的BITCOUNT 示例
redis> BITCOUNT bits 10 3
4
二進制位運算
BITOP operation destkey key [key ...]
對一個或多個保存二進制位的字符串鍵執行位元操作,並將結果保存到 destkey 上。
operation 可以是 AND 、 OR 、 NOT 、 XOR 這四種操作中的任意一種:
命令
|
效果
|
BITOP AND destkey key [key ...]
|
對一個或多個 key 求邏輯並,並將結果保存到 destkey 。
|
BITOP OR destkey key [key ...]
|
對一個或多個 key 求邏輯或,並將結果保存到 destkey 。
|
BITOP XOR destkey key [key ...]
|
對一個或多個 key 求邏輯或,並將結果保存到 destkey
|
BITOP NOT destkey key
|
對一個或多個 key 求邏輯異或,並將結果保存到 destkey 。
|
對給定 key 求邏輯非,並將結果保存到 destkey 。
除了 NOT 操作之外,其他操作都可以接受一個或以上數量的 key 作為輸入。
復雜度為 O(N) , N 為進行計算的二進制位數量的總和。
命令的返回值為計算所得結果的字節長度,相當於對 destkey 執行 STRLEN 。
BITOP 示例
假設現在 b1 鍵儲存了二進制 01001101 ,而 b2 鍵儲存了二進制 10110101 。
redis> BITOP AND b1-and-b2 b1 b2 # b1-and-b2 = 00000101
(integer) 1
redis> BITOP OR b1-or-b2 b1 b2 # b1-or-b2 = 11111101
(integer) 1
redis> BITOP XOR b1-xor-b2 b1 b2 # b1-xor-b2 = 11111000
(integer) 1
redis> BITOP NOT not-b1 b1 # not-b1 = 10110010
(integer) 1
示例:實現在線人數統計
一些網站具備了在線人數統計功能,通過這個功能可以看到一段時間以內(比如這個小時,或者這一天),曾經登錄過這個網站的會員人數。
某網站的在線人數統計結果,顯示目前有 289 個會員在線。
通過使用字符串鍵以及二進制數據處理命令,我們也可以構建一個高效並且 節約內存的在線人數統計實現。
在用戶 id 和位索引之間進行關聯
之前說過,字符串鍵儲存的每個二進制位都有與之對應的索引,比如對於一個 8 位長的二進制值來說,它的各個二進制位的索引值為 0 至 7 。
因為通常網站的每個會員都有一個自己的數字 id ,比如 peter的 id 可能是 3 ,而 jack 的 id 可能是 5 ,所以我們可以在用戶id 和二進制位的索引之間進行關聯:
• 如果 id 為 N 的用戶在線,我們就將索引為 N 的二進制位的值設置為 1 。
• 如果索引為 N 的二進制位的值為 0 ,這表示 id 為 N 用戶不在線。
• 使用 BITCOUNT 可以統計有多少個用戶在線。
• 通過為每段時間分別儲存一個二進制值,我們就可以為每段時間都記錄在線用戶的數量。(每小時創建一個鍵或者每天創建一個鍵,諸如此類。)
在線用戶統計的 API 及其實現
API
|
作用
|
實現
|
OnlineCount(when, client)
|
記錄給定時間內的在線用戶數量。
|
|
OnlineCount.include(user_id)
|
將給定的用戶記錄為在線。
|
調用 SETBIT 命令。
|
OnlineCount.result()
|
返回給定時間內的在線用戶數量。
|
調用 BITCOUNT 命令。
|
count = OnlineCount(‘2014-8-3 10a.m.’) # 記錄 2014 年 8 月 3 日上午 10 點的在線用戶數量
count.include(4) # 將 id 為 4 的用戶設置為在線
count.include(5) # 將 id 為 5 的用戶設置為在線
count.include(7) # 將 id 為 7 的用戶設置為在線
count.result() # 返回 3 ,表示有三個用戶在線
在線用戶統計程序的完整實現代碼可以在 online_count.py 查看。
# encoding: utf-8
class OnlineCount:
def __init__(self, when, client):
self.when = when
self.client = client
def include(self, user_id):
return self.client.setbit(self.when, user_id, 1)
def result(self):
return self.client.bitcount(self.when)
關於用戶在線統計的更多信息
目前這個實現的優點:
-
將用戶設置為在線的速度非常快, O(1) 。
-
即使用戶數量非常大,占用的內存也不多:記錄一百萬用戶僅需一百萬位,也即是 0.125 MB;記錄一千萬用戶僅需一千萬位,也即是 1.25 MB 。
-
可以在現有程序的基礎上,做進一步的操作。舉個例子,我們可以使用 BITOP AND 命令,將多個在線記錄作為輸入,計算出全勤用戶的數量(全勤指的是,用戶在所有輸入的在線統計記錄中,都顯示為在線)。
目前這個實現的缺點:
-
每次進行統計的復雜度為 O(N) 。
-
沒辦法輕易地獲取所有在線用戶的名單,只能遍歷整個二進制值,復雜度為 O(N) ,其中 N 為二進制位數量。
進一步的優化:
-
用戶量不大並且需要獲取在線用戶名單的話,可以使用之后介紹的集合數據結構來實現。
-
不需要獲取在線用戶名單,並且不需要精確的在線統計數量,可以使用之后介紹的 HyperLogLog 來實現。
示例:使用 Redis 緩存熱門圖片
圖片網站要儲存大量的圖片(通常放在硬盤里面),而少部分熱門的圖片會被經常地訪問到。
為了加快網站獲取熱門圖片的速度,我們可以利用 Redis 能夠儲存二進制數據這一特性,使用之前構建的緩存程序來緩存圖片網站中的熱門圖片。
cache = Cache(redis_client) # 設置緩存的客戶端
file = open('redis-logo.jpg', 'r') # 打開文件
data = file.read() # 讀取文件數據
file.close() # 關閉文件
cache.put('redis-logo', data) # 以 redis-logo 為名字,將圖片緩存起來
cache.get('redis-logo') # 取出 redis-logo 圖片的數據
5.儲存中文時的注意事項
STRLEN、SETRANGE 和 GETRANGE 不適用於中文
注意事項
一個英文字符只需要使用 單個字節來儲存,而一個中文字符卻需要使用多個字 節來儲存。
STRLEN、SETRANGE 和 GETRANGE 都是為英文設置的,它們只會在字符為單個字節的情況下正常工作,而一旦我們儲存的是類似中文這樣的多字節字符,那么這三個命令就不再適用了。
STRLEN 示例
$ redis-cli --raw # 在 redis-cli 中使用中文時,必須打開 --raw 選項,才能正常顯示中文
redis> SET msg "世界你好" # 設置四個中文字符
OK
redis> GET msg # 儲存中文沒有問題
世界你好
redis> STRLEN msg # 這里 STRLEN 顯示了“世界你好”的字節長度為 12 字節
12 # 但我們真正想知道的是 msg 鍵里面包含多少個字符
SETRANGE 和 GETRANGE 示例
SETRANGE 和 GETRANGE 的情況也是類似的:因為這兩個命令所使用的索引是根據字 節而不是字符來編排的,所以調用 SETRANGE 或者 GETRANGE 來處理中文,得不到我們想要的結果。
r
edis> SET msg "世界你好"
OK
redis> GETRANGE msg 2 3 # 試圖獲取 "你好"
�
redis> SETRANGE msg 2 "歡迎你" # 試圖構建 "世界歡迎你",其中"歡迎你"為 9 字節長
12
redis> GET msg
��歡迎你�
結論
不要使用 STRLEN、SETRANGE 和 GETRANGE 來處理中文。
例外情況:如果你想知道被 儲存的中文包含多少個字節,那么可以使用 STRLEN 。