Redis--字符串(String)
Posted ykyk
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis--字符串(String)相关的知识,希望对你有一定的参考价值。
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 "[email protected]" 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 。