Redis数据结构之字符串
字符串
字符串(string)键是Redis中最基本的键值对类型,这种类型的键值对会在数据库中把单独的一个键和单独的一个值 关联起来,被关联的键和值可以是普通的文字数据,也可以是图片、视频、音频、压缩文件等更为复杂的二进制数据。
存储结构
字符串类型键的存储结构如下图所示:
- 与键 “message” 相关联的值是 “hello world”
- 与键 “number” 相关联的值是 “10086”
- 与键 “homepage” 相关联的值是 “redis.io”
- 与键 “redis-log.jpg” 相关联的值是 二进制数据
Redis为字符串类型键提供了一系列操作命令,通过这些命令,用户可以:
- 为字符串键设置值
- 获取字符串键的值
- 在获取旧值的同时为字符串键设置新值
- 同时为多个字符串键设置值,或者同时获取多个字符串键的值
- 获取字符串值的长度
- 获取字符串值指定索引范围内的内容,或者对字符串值指定索引范围内的内容进行修改
- 将一些内容追加到字符串值的末尾
- 对字符串键存储的整数值或者浮点数值执行加法操作或减法操作
命令
命令 | 用法及参数 | 说明 |
---|---|---|
SET | SET key value [expiration EX seconds | PX milliseconds] [NX | XX] | 为指定的字符串键设置相应的值,NX 和 XX选项用来指示覆盖规则,EX 和 PX用来指定键的过期时间,单位分别为秒和毫秒 |
GET | GET key | 获取指定键的值 |
MSET | MSET key value [key value…] | 设置多个键值对 |
MGET | MGET key [key …] | 获取多个键的值 |
MSETNX | MSETNX key value [key value …] | 对多个键设置值,只在给定所有键都不存在时执行设置操作,只要有一个键存在则放弃执行操作 |
STRLEN | STRLEN key | 获取指定字符串键的值的字节长度 |
GETRANGE | GETRANGE key start end | 获取指定键的值的字符串索引区间的内容,从start 到end直接的所有内容 |
SETRANGE | SETRANGE key index substitute | 将字符串键的值从索引 index 开始的部分替换为指定的内容 |
APPEND | APPEND key suffix | 为给定键的值追加内容 |
INCRBY | INCRBY key increment | 适用于字符串键值为整数时,针对值执行加法操作 |
DECRBY | DECRBY key decrement | 适用于字符串键值为整数时,针对值执行减法操作 |
INCR | INCR key | 指定键的值加1,等价于 INCRBY key 1 |
DECR | DECR key | 指定键的值减1,等价于 DECRBY key 1 |
INCRBYFLOAT | INCRBYFLOAT key increment | 字符串键的浮点数值执行加法运算 |
SET命令
SET 命令可以为指定的字符串键设置相应的值,在最基本的情况下,只需要向SET命令提供一个键和一个值即可:
SET key value
SET命令在成功后会返回OK作为结果
改变覆盖规则
在默认情况下,对一个已经设置了值的字符串键执行SET命令将导致键的旧值被新值覆盖,如下所示:
127.0.0.1:6379> SET msg "hello world"
OK
127.0.0.1:6379> SET msg "hello redis"
OK
在第二条命令执行完毕后,msg键的值将从原来的 “hello world” 变为 “hello redis”。
从Redis 2.6.12版本开始,用户可以通过向SET命令提供可选的 NX 选项或者 XX 选项来指示SET命令是否覆盖一个已经存在的值:
SET key value [NX | XX]
- NX:指示SET命令只会在键没有值的情况下执行设置操作,并返回OK表示成功;如果键已经存在,那么SET命令将放弃执行设置操作,并返回控制 nil 表示设置失败。
127.0.0.1:6379> SET password "123456" NX
OK -- 对尚未有值的password键进行设置,成功
127.0.0.1:6379> SET password "999999" NX
(nil) -- password键已经有值,设置失败
- XX:指示SET命令只会在键已经有值的情况下执行设置操作,并返回OK表示成功;如果给定的键并没有值,那么SET命令将放弃设置操作,并返回 nil 表示设置失败。
127.0.0.1:6379> SET homepage "redis.io" XX
(nil) -- homepage键没有值,设置失败
127.0.0.1:6379> SET homepage "redis.io"
OK -- 为homepage键设置一个值
127.0.0.1:6379> SET homepage "baidu.com" XX
OK -- homepage键有值,设置成功
GET命令
使用GET命令,可以从数据库中获取指定字符串键的值:
GET key
给出指定的键即可返回对应的值,如果键的值不存在,则返回空 (nil)。
GETSET命令
GETSET命令就像是GET命令和SET命令的组合版本,GETSET首先获取字符串键目前已有的值,接着为键设置新值,最后把之前获取的旧值返回给用户:
GETSET key new_value
127.0.0.1:6379> GET number -- number键现在的值为 "123456"
"123456"
127.0.0.1:6379> GETSET number "10086"
"123456" -- 返回旧值
127.0.0.1:6379> GET number -- number键的值已被更新为 "10086"
"10086"
MSET命令
除了SET命令和GETSET命令外,Redis还提供了MSET 命令用于对字符串键进行设置。与SET命令和GESET命令只能对单个字符串键设置不同,MSET命令可以一次为多个字符串键设置值:
MSET key value [key value ...]
127.0.0.1:6379> MSET msg "hello world" number "123456"
OK
127.0.0.1:6379> GET msg
"hello world"
127.0.0.1:6379> GET number
"123456"
如果给定的键已存在,那么MSET命令会直接覆盖原来的值
MGET命令
MGET命令就是一个多键版本的GET命令,MGET接受一个或多个字符串键作为参数,并返回这些字符串键的值:
MGET key [key ...]
127.0.0.1:6379> MGET msg number
1) "hello world"
2) "123456"
当指定的键不存在时,返回空值 (nil)。
MSETNX命令
MSETNX命令与MSET一样,都可以对多个字符串键设置值:
MSETNX key value [key value ...]
MSETNX与MSET的主要区别在于:MSETNX命令只会在所有给定键都不存在的情况下对键进行设置,而不会像MSET那样直接覆盖原值;如果给定的键中,即使只有一个键存在,那么MSETNX命令也会放弃对所有给定键的设置操作。MSETNX命令成功时返回1,失败时返回0。
127.0.0.1:6379> MGET k1 k2 k3 k4
1) (nil) -- 键k1、k2和k3都不存在
2) (nil)
3) (nil)
4) "hello world" -- 键k4已存在
127.0.0.1:6379> MSETNX k1 "one" k2 "two" k3 "three" k4 "four"
(integer) 0 -- 因为键k4已存在,所以MSETNX未能执行设置操作
127.0.0.1:6379> MGET k1 k2 k3 k4
1) (nil)
2) (nil)
3) (nil)
4) "hello world"
STRLEN命令
STRLEN命令可以用来获取指定字符串键存储值的字节长度:
STRLEN key
127.0.0.1:6379> GET number
"12345"
127.0.0.1:6379> STRLEN number
(integer) 5
版本要求:STRLEN命令从Redis 2.2.0开始可用
GETRANGE
通过使用GETRANGE命令,可以获取字符串值从 start 索引开始,直到 end 索引为止的所有内容:
GETRANGE key start end
127.0.0.1:6379> SET msg "hello world"
OK
127.0.0.1:6379> GETRANGE msg 0 4
"hello"
127.0.0.1:6379> GETRANGE msg 6 10
"world"
GETRANGE命令的索引支持正向和反向两种方式:
- 正向索引:类似于数组的下标一样,从左到右依次为 0、1、2 … n
- 反向索引:从右到左,起始值为 -1 、-2 … -n
版本要求:GETRANGE命令从Redis 2.4.0开始可用。
SETRANGE命令
通过SETRANGE命令,用户可以将字符串键的值从索引 index 开始的部分替换为指定的新内容,被替换内容的长度取决于新内容的长度:
SETRANGE key index substitute
SETRANGE命令执行成功后,会返回字符串值当前的长度作为结果。
127.0.0.1:6379> GET msg
"hello world"
127.0.0.1:6379> SETRANGE msg 6 "redis"
(integer) 11
127.0.0.1:6379> GET msg
"hello redis"
SETRANGE命令扩展字符串,规则如下:
- 根据新内自动扩展字符串键的值,以适应新内容。
- 根据给定的 index 索引扩展字符串,当给定的索引 index 超出字符串值的长度时,字符串值末尾直到索引 index - 1之间的部分将用空字节进行填充,这些字节的所有二进制位都会被设置为 0。
127.0.0.1:6379> GET msg
"hello"
127.0.0.1:6379> STRLEN msg
(integer) 5 -- 当前字符串值的字节长度为5
127.0.0.1:6379> SETRANGE msg 10 "world" -- 指定在索引 10 的位置处开始设置 world子串
(integer) 15
127.0.0.1:6379> GET msg
"hello\x00\x00\x00\x00\x00world"
- 先将原字符串值 “hello” 的长度扩展至15个字节长度;
- 然后将原字符串 "hello"末尾直到索引 9之间的所有字节都填充为空字节;
- 最后再降索引10到索引14之间的内容设置为 “world”。
上面的步骤如下图所示:
复杂度:
O
(
N
)
O(N)
O(N),其中N为被修改内容的长度。
版本要求:SETRANGE命令从Redis 2.2.0开始可用。
APPEND命令
通过调用APPEND命令,用户可以将给定的内容追加到字符串键已有的值的末尾:
APPEND key suffix
APPEND命令在执行追加操作之后,会返回字符串值当前的长度作为命令的返回值。
127.0.0.1:6379> GET msg
"hello"
127.0.0.1:6379> APPEND msg " world"
(integer) 11
处理不存在的键
如果用户给定的键不存在,那么APPEND命令会先将键的值初始化为空字符串 “”,然后再执行追加操作,如下所示:
127.0.0.1:6379> GET name
(nil)
127.0.0.1:6379> APPEND name "admin"
(integer) 5
127.0.0.1:6379> GET name
"admin"
复杂度:
O
(
N
)
O(N)
O(N),其中N为新追加的内容的长度
版本要求:APPEND命令从Redis 2.0.0开始可用
存储数字值
每当用户将一个值存储到字符串键里面的时候,Redis都会对这个值进行检测,如果这个值能够被解释为以下两种类型的其中一种,那么Redis就会把这个值当做数字来处理:
- 能够使用C语言的 long long int 类型存储的整数;
- 能够使用C语言的 long double类型存储的浮点数。
以下是不同类型的值,并说明了Redis对他们的解释方式:
INCRBY 和 DECRBY命令
当字符串键存储的值能够被Redis解释为整数时,用户就可以通过 INCRBY命令和DECRBY命令对被存储的整数值执行加法或减法操作。
INCRBY key increment -- 加法操作
DECRBY key decrement -- 减法操作
127.0.0.1:6379> SET number 100
OK
127.0.0.1:6379> GET number
"100"
127.0.0.1:6379> INCRBY number 300
(integer) 400
127.0.0.1:6379> INCRBY number 25
(integer) 425
127.0.0.1:6379> GET number
"425"
127.0.0.1:6379> DECRBY number 25
(integer) 400
类型限制
当字符串键的值不能被Redis解释为整数时,对键执行INCRBY命令或 DECRBY命令将返回一个错误:
127.0.0.1:6379> SET pi 3.14
OK
127.0.0.1:6379> INCRBY pi 100 --不能对浮点数执行INCRBY命令
(error) ERR value is not an integer or out of range
127.0.0.1:6379> SET msg "hello world"
OK
127.0.0.1:6379> INCRBY msg -- 不能对字符串执行INCRBY命令
(error) ERR wrong number of arguments for 'incrby' command
127.0.0.1:6379> SET big-number 11111111111111111111111111111111111111111111
OK
127.0.0.1:6379> INCRBY big-number -- 不能对超过64位长度的整数执行INCRBY
(error) ERR value is not an integer or our of range
处理不存在的键
当INCRBY命令或DECRBY命令遇到不存在的键时,命令会先将键的值初始化为0,然后再执行相应的加法或减法操作:
127.0.0.1:6379> GET x
(nil)
127.0.0.1:6379> INCRBY x 100 -- 先将键的值初始化为0,然后再执行
(integer) 100
127.0.0.1:6379> GET x
"100"
复杂度:
O
(
1
)
O(1)
O(1)
版本要求:INCRBY命令和DECRBY命令从Redis 1.0.0开始可用。
INCR 和 DECR 命令
因为对整数执行加 1 或减 1的操作场景经常出现,所以Redis提供了 INCR命令和DECR命令来简化加法和减法操作:
INCR key 等价于 INCRBY key 1 -- 执行加一操作
DECR key 等价于 DECRBY key 1 -- 执行减一操作
127.0.0.1:6379> SET counter 100
OK
127.0.0.1:6379> INCR counter
(integer) 101
127.0.0.1:6379> DECR counter
(integer) 100
复杂度:
O
(
1
)
O(1)
O(1)
版本要求:INCR命令和DECR命令从Redis 1.0.0开始可用。
INCRBYFLOAT命令
除了用于执行整数的加法操作 INCR命令和INCRBY命令外,Redis还提供了用于执行浮点数的加法操作的 INCRBYFLOAT命令:
INCRBYFLOAT key increment
INCRBYFLOAT命令可以把一个浮点数增量加到字符串键存储的数字值上面,并返回键在执行加法后的数字值作为结果。
127.0.0.1:6379> SET price 10.14
OK
127.0.0.1:6379> INCRBYFLOAT price 2.55
"12.6899999999999995"
处理不存在的键
与INCR命令或INCRBY命令一样,当INCRBYFLOAT命令遇到键不存在时,会先将键的值初始化为0,然后再执行操作。
执行浮点数减法操作
Redis为INCR命令或INCRBY命令都提供了对应的减法命令,如 DECR命令和DECRBY命令,但是并没有为 INCRBYFLOAT命令提供相应的减法操作命令,因此用户只能通过给INCRBYFLOAT命令传入负数的增量来执行减法操作。
INCRBYFLOAT 与 整数值
INCRBYFLOAT命令对于类型限制的要求比INCRBY命令和INCR命令要宽松得多:
- INCRBYFLOAT命令既可以用于浮点数值,也可以用于整数值
- INCRBYFLOAT命令的增量既可以是浮点数,也可以是整数
- 当INCRBYFLOAT命令的执行结果可以表示为整数时,命令的执行结果将以整数形式存储
127.0.0.1:6379> SET pi 1
OK
127.0.0.1:6379> GET pi
"1"
127.0.0.1:6379> INCRBYFLOAT pi 2.14
"3.14"
127.0.0.1:6379> INCRBYFLOAT pi 0.86
"4"
小数位的长度限制
虽然Redis并不限制字符串键存储的浮点数的小数位长度,但是使用 INCRBYFLOAT命令处理浮点数的时候,命令最多只会保留计算结果小数点后的17位数字,超过这个范围的小数将被截断。
精度问题
在上面第一个例子中,我们看到 INCRBYFLOAT命令在处理浮点数计算的时候,会有精度不准确的问题,针对这个问题,目前Redis没有处理方案,因此,我们在确定业务需要的情况下,尽可能的通过乘以倍数的方式,将浮点数升级为整数形式,然后使用 INCR系列命令进行计算
复杂度:
O
(
1
)
O(1)
O(1)
版本要求:INCRBYFLOAT命令从Redis 2.6.0开始可用。