Redis 数据类型 Strings 类型详解

Redis 数据类型 Strings 类型详解

bitmap 操作
SETBIT
• SETBIT key offset value
起始版本:2.2.0
时间复杂度:O(1)
设置或者清空key的value(字符串)在offset处的bit值。那个位置的bit要么被设置,要么被清空,这个由value(只能是0或者1)来决定。
要确保这个字符串大到在offset处有bit值。参数offset需要大于等于0,并且小于232(限制bitmap大小为512)。
警告:当set最后一个bit(offset等于232-1)并且key还没有一个字符串value或者其value是个比较小的字符串时,Redis需要立即分配所有内存,这有可能会导致服务阻塞一会。在一台2010MacBook Pro上,offset为232-1(分配512MB)需要~300ms,offset为230-1(分配128MB)需要~80ms,offset为228-1(分配32MB)需要~30ms,offset为226-1(分配8MB)需要8ms。注意,一旦第一次内存分配完,后面对同一个key调用SETBIT就不会预先得到内存分配。
GETBIT
• GETBIT key offset
起始版本:2.2.0
时间复杂度:O(1)
返回key对应的string在offset处的bit值 当offset超出了字符串长度的时候,这个字符串就被假定为由0比特填充的连续空间。当key不存在的时候,它就认为是一个空字符串,所以offset总是超出范围,然后value也被认为是由0比特填充的连续空间。到内存分配。
• 例子

SETBIT mykey 7 1
(integer) 0
GETBIT mykey 0
(integer) 0
GETBIT mykey 7
(integer) 1
GETBIT mykey 100
(integer) 0

BITCOUNT
• BITCOUNT key [start end]
起始版本:2.6.0
时间复杂度:O(N)
统计字符串被设置为1的bit数
一般情况下,给定的整个字符串都会被进行计数,通过指定额外的 start 或 end 参数,可以让计数只在特定的位上进行。
假设现在我们希望记录自己网站上的用户的上线频率,比如说,计算用户 A 上线了多少天,用户 B 上线了多少天,诸如此类,以此作为数据,从而决定让哪些用户参加 beta 测试等活动 —— 这个模式可以使用 SETBIT 和 BITCOUNT 来实现。
比如说,每当用户在某一天上线的时候,我们就使用 SETBIT ,以用户名作为 key ,将那天所代表的网站的上线日作为 offset 参数,并将这个 offset 上的为设置为 1 。
举个例子,如果今天是网站上线的第 100 天,而用户 peter 在今天阅览过网站,那么执行命令 SETBIT peter 100 1 ;如果明天 peter 也继续阅览网站,那么执行命令 SETBIT peter 101 1 ,以此类推。
当要计算 peter 总共以来的上线次数时,就使用 BITCOUNT 命令:执行 BITCOUNT peter ,得出的结果就是 peter 上线的总天数。

如果你的 bitmap 数据非常大,那么可以考虑使用以下两种方法:
将一个大的 bitmap 分散到不同的 key 中,作为小的 bitmap 来处理。使用 Lua 脚本可以很方便地完成这一工作。
使用 BITCOUNT 的 start 和 end 参数,每次只对所需的部分位进行计算,将位的累积工作(accumulating)放到客户端进行,并且对结果进行缓存 (caching)。
BITFIELD
• BITFIELD key [GET type offset] [SET type
offset value] [INCRBY type offset increment] [OVERFLOW WRAP|SAT|FAIL]
起始版本:3.2.0
时间复杂度:O(1) for each subcommand specified
本命令会把Redis字符串当作位数组,并能对变长位宽和任意未字节对齐的指定整型位域进行寻址。在实践中,可以使用该命令对一个有符号的5位整型数的1234位设置指定值,也可以对一个31位无符号整型数的4567位进行取值。类似地,在对指定的整数进行自增和自减操作,本命令可以提供有保证的、可配置的上溢和下溢处理操作。
• BITFIELD mykey INCRBY i5 100 1 GET u4 0
当需要一个整型时,有符号整型需在位数前加i,无符号在位数前加u。例如,u8是一个8位的无符号整型,i16是一个16位的有符号整型。

有符号整型最大支持64位,而无符号整型最大支持63位。对无符号整型的限制,是由于当前Redis协议不能在响应消息中返回64位无符号整数。
位和位偏移
bitfield命令有两种方式来指定位偏移。如果未定带数字的前缀,将会以字符串的第0位作为起始位。

不过,如果偏移量带有#前缀,那么指定的偏移量需要乘以整型宽度,例如:

BITFIELD mystring SET i8 #0 100 i8 #1 200
将会在第1个i8整数的偏移0位和第2个整数的偏移8位进行设值。如果想得到一个给定长度的普通整型数组,则不一定要在客户端进行计算。

• 溢出控制

BITFIELD mykey incrby u2 100 1 OVERFLOW SAT incrby u2 102 1

使用OVERFLOW命令,用户可以通过指定下列其中一种行为来调整自增或自减操作溢出(或下溢)后的行为:

WRAP: 回环算法,适用于有符号和无符号整型两种类型。对于无符号整型,回环计数将对整型最大值进行取模操作(C语言的标准行为)。对于有符号整型,上溢从最负的负数开始取数,下溢则从最大的正数开始取数,例如,如果i8整型的值设为127,自加1后的值变为-128。
SAT: 饱和算法,下溢之后设为最小的整型值,上溢之后设为最大的整数值。例如,i8整型的值从120开始加10后,结果是127,继续增加,结果还是保持为127。下溢也是同理,但量结果值将会保持在最负的负数值。
FAIL: 失败算法,这种模式下,在检测到上溢或下溢时,不做任何操作。相应的返回值会设为NULL,并返回给调用者。
注意每种溢出(OVERFLOW)控制方法,仅影响紧跟在INCRBY命令后的子命令,直到重新指定溢出(OVERFLOW)控制方法。

如果没有指定溢出控制方法,默认情况下,将使用WRAP算法。
• 动机(Motivations)

本命令的动机是为了能够在单个大位图(large bitmap)中高效地存储多个小整数(或对键分成多个key,避免出现超大键),同时开放Redis提供的新使用案例,尤其是在实时分析领域。这种使用案例可以通过指定的溢出控制方法来支持。
• 性能考虑(Performance considerations)
通常,BITFIELD是一个非常快的命令,但是注意,对短字符串的远地址(fat bits)寻址,将会比在存在的位执行命令更加耗时。

字节序(Orders of bits)
BITFIELD命令使用的位图表现形式,可看作是从0位开始的,例如:把一个5位的无符号整数23,对一个所有位事先置0的位图,从第7位开始赋值,其结果如下所示:

±-------±-------+
|00000001|01110000|
±-------±-------+
当偏移量和整型大小是字节边界对齐时,此时与大端模式(big endian)相同,但是,当字节边界未对齐时,那么理解字节序将变得非常重要。
BITOP
• BITOP operation destkey key [key …]
起始版本:2.6.0
时间复杂度:O(N)
对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上
BITOP 命令支持 AND 、 OR 、 NOT 、 XOR 这四种操作中的任意一种参数:
BITOP AND destkey srckey1 srckey2 srckey3 … srckeyN ,对一个或多个 key 求逻辑并,并将结果保存到 destkey 。
BITOP OR destkey srckey1 srckey2 srckey3 … srckeyN,对一个或多个 key 求逻辑或,并将结果保存到 destkey 。
BITOP XOR destkey srckey1 srckey2 srckey3 … srckeyN,对一个或多个 key 求逻辑异或,并将结果保存到 destkey 。
BITOP NOT destkey srckey,对给定 key 求逻辑非,并将结果保存到 destkey 。
除了 NOT 操作之外,其他操作都可以接受一个或多个 key 作为输入。
执行结果将始终保持到destkey里面。

处理不同长度的字符串
当 BITOP 处理不同长度的字符串时,较短的那个字符串所缺少的部分会被看作 0 。
空的 key 也被看作是包含 0 的字符串序列。
• 例子

SET key1 “foobar”
OK
SET key2 “abcdef”
OK
BITOP AND dest key1 key2
(integer) 6
GET dest
bcab”

BITPOS
• BITPOS key bit [start] [end]
起始版本:2.8.7
时间复杂度:O(N)
返回字符串里面第一个被设置为1或者0的bit位。
默认情况下整个字符串都会被检索一次,只有在指定start和end参数(指定start和end位是可行的),该范围被解释为一个字节的范围,而不是一系列的位。所以start=0 并且 end=2是指前三个字节范围内查找。
• 例子

SET mykey “\xff\xf0\x00”
OK
BITPOS mykey 0 # 查找字符串里面bit值为0的位置
(integer) 12
SET mykey “\x00\xff\xf0”
OK
BITPOS mykey 1 0 # 查找字符串里面bit值为1从第0个字节开始的位置
(integer) 8
BITPOS mykey 1 2 # 查找字符串里面bit值为1从第2个字节(12)开始的位置
(integer) 16
set mykey “\x00\x00\x00”
OK
BITPOS mykey 1 # 查找字符串里面bit值为1的位置
(integer) -1

APPEND
APPEND key value
起始版本:2.0.0
时间复杂度:O(1)
如果 key 已经存在,并且值为字符串,那么这个命令会把 value 追加到原来值(value)的结尾
如果 key 不存在,那么它将首先创建一个空字符串的key,再执行追加操作
• 例子

EXISTS mykey
(integer) 0
APPEND mykey “Hello”
(integer) 5
APPEND mykey " World"
(integer) 11
GET mykey
“Hello World”

DECR
DECR key
起始版本:1.0.0
时间复杂度:O(1)
对key对应的数字做减1操作。如果key不存在,那么在操作之前,这个key对应的值会被置为0。如果key有一个错误类型的value或者是一个不能表示成数字的字符串,就返回错误。这个操作最大支持在64位有符号的整型数字。
• 例子

SET mykey “10”
OK
DECR mykey
(integer) 9
SET mykey “234293482390480948029348230948”
OK
DECR mykey
ERR value is not an integer or out of range

DECRBY
DECRBY key decrement
起始版本:1.0.0
时间复杂度:O(1)
将key对应的数字减decrement。如果key不存在,操作之前,key就会被置为0。如果key的value类型错误或者是个不能表示成数字的字符串,就返回错误。这个操作最多支持64位有符号的正型数字。
• 例子

SET mykey “10”
OK
DECRBY mykey 5
(integer) 5

GET
GET key
起始版本:1.0.0
时间复杂度:O(1)
返回key的value。如果key不存在,返回特殊值nil。如果key的value不是string,就返回错误,因为GET只处理string类型的values。
• 例子

GET nonexisting
(nil)
SET mykey “Hello”
OK
GET mykey
“Hello”

GETRANGE
GETRANGE key start end
起始版本:2.4.0
时间复杂度:O(N) N是字符串长度,复杂度由最终返回长度决定,但由于通过一个字符串创建子字符串是很容易的,它可以被认为是O(1)。
警告:这个命令是被改成GETRANGE的,在小于2.0的Redis版本中叫SUBSTR。 返回key对应的字符串value的子串,这个子串是由start和end位移决定的(两者都在string内)。可以用负的位移来表示从string尾部开始数的下标。所以-1就是最后一个字符,-2就是倒数第二个,以此类推。

这个函数处理超出范围的请求时,都把结果限制在string内。
• 例子

SET mykey “This is a string”
OK
GETRANGE mykey 0 3
“This”
GETRANGE mykey -3 -1
“ing”
GETRANGE mykey 0 -1
“This is a string”
GETRANGE mykey 10 100
“string”

GETSET
GETSET key value
起始版本:1.0.0
时间复杂度:O(1)
自动将key对应到value并且返回原来key对应的value。如果key存在但是对应的value不是字符串,就返回错误。

设计模式

GETSET可以和INCR一起使用实现支持重置的计数功能。举个例子:每当有事件发生的时候,一段程序都会调用INCR给key mycounter加1,但是有时我们需要获取计数器的值,并且自动将其重置为0。这可以通过GETSET mycounter “0”来实现:
• 例子

INCR mycounter
(integer) 1
GETSET mycounter “0”
“1”
GET mycounter
“0”

INCR
INCR key
起始版本:1.0.0
时间复杂度:O(1)

对存储在指定key的数值执行原子的加1操作。
如果指定的key不存在,那么在执行incr操作之前,会先将它的值设定为0。

如果指定的key中存储的值不是字符串类型(fix:)或者存储的字符串类型不能表示为一个整数,

那么执行这个命令时服务器会返回一个错误(eq:(error) ERR value is not an integer or out of range)。

这个操作仅限于64位的有符号整型数据。
• 例子

SET mykey “10”
OK
INCR mykey
(integer) 11
GET mykey
“11”

INCRBY
INCRBY key increment
起始版本:1.0.0
时间复杂度:O(1)
将key对应的数字加decrement。如果key不存在,操作之前,key就会被置为0。如果key的value类型错误或者是个不能表示成数字的字符串,就返回错误。这个操作最多支持64位有符号的正型数字。
• 例子

SET mykey “10”
OK
INCRBY mykey 5
(integer) 15

INCRBYFLOAT
INCRBYFLOAT key increment
起始版本:2.6.0
时间复杂度:O(1)

通过指定浮点数key来增长浮点数(存放于string中)的值. 当键不存在时,先将其值设为0再操作.下面任一情况都会返回错误:

key 包含非法值(不是一个string).
当前的key或者相加后的值不能解析为一个双精度的浮点值.(超出精度范围了)
如果操作命令成功, 相加后的值将替换原值存储在对应的键值上, 并以string的类型返回. string中已存的值或者相加参数可以任意选用指数符号,但相加计算的结果会以科学计数法的格式存储. 无论各计算的内部精度如何, 输出精度都固定为小数点后17位.
• 例子

SET mykey 10.50
OK
INCRBYFLOAT mykey 0.1
“10.6”
SET mykey 5.0e3
OK
INCRBYFLOAT mykey 2.0e2
“5200”

MGET
MGET key [key …]
起始版本:1.0.0
时间复杂度:O(N) where N is the number of keys to retrieve.
返回所有指定的key的value。对于每个不对应string或者不存在的key,都返回特殊值nil。正因为此,这个操作从来不会失败。
• 例子

SET key1 “Hello”
OK
SET key2 “World”
OK
MGET key1 key2 nonexisting

  1. “Hello”
  2. “World”
  3. (nil)

MSET
MSET key value [key value …]
起始版本:1.0.1
时间复杂度:O(N) where N is the number of keys to set.
对应给定的keys到他们相应的values上。MSET会用新的value替换已经存在的value,就像普通的SET命令一样。如果你不想覆盖已经存在的values,请参看命令MSETNX。

MSET是原子的,所以所有给定的keys是一次性set的。客户端不可能看到这种一部分keys被更新而另外的没有改变的情况。
• 例子

MSET key1 “Hello” key2 “World”
OK
GET key1
“Hello”
GET key2
“World”

MSETNX
MSETNX key value [key value …]
起始版本:1.0.1
时间复杂度:O(N) where N is the number of keys to set.
对应给定的keys到他们相应的values上。只要有一个key已经存在,MSETNX一个操作都不会执行。 由于这种特性,MSETNX可以实现要么所有的操作都成功,要么一个都不执行,这样可以用来设置不同的key,来表示一个唯一的对象的不同字段。

MSETNX是原子的,所以所有给定的keys是一次性set的。客户端不可能看到这种一部分keys被更新而另外的没有改变的情况。
• 例子

MSETNX key1 “Hello” key2 “there”
(integer) 1
MSETNX key2 “there” key3 “world”
(integer) 0
MGET key1 key2 key3

  1. “Hello”
  2. “there”
  3. (nil)

PSETEX
PSETEX key milliseconds value
起始版本:2.6.0
时间复杂度:O(1)
PSETEX和SETEX一样,唯一的区别是到期时间以毫秒为单位,而不是秒。
• 例子

PSETEX mykey 1000 “Hello”
OK
PTTL mykey
(integer) 999
GET mykey
“Hello”

SET
SET key value [EX seconds] [PX milliseconds] [NX|XX]
起始版本:1.0.0
时间复杂度:O(1)
将键key设定为指定的“字符串”值。

如果 key 已经保存了一个值,那么这个操作会直接覆盖原来的值,并且忽略原始类型。

当set命令执行成功之后,之前设置的过期时间都将失效
• 选项
从2.6.12版本开始,redis为SET命令增加了一系列选项:
EX seconds – Set the specified expire time, in seconds.
PX milliseconds – Set the specified expire time, in milliseconds.
NX – Only set the key if it does not already exist.
XX – Only set the key if it already exist.
EX seconds – 设置键key的过期时间,单位时秒
PX milliseconds – 设置键key的过期时间,单位时毫秒
NX – 只有键key不存在的时候才会设置key的值
XX – 只有键key存在的时候才会设置key的值
• 例子

SET mykey “Hello”
OK
GET mykey
“Hello”

• 锁机制
命令 SET resource-name anystring NX EX max-lock-time 是一种用 Redis 来实现锁机制的简单方法。
如果上述命令返回OK,那么客户端就可以获得锁(如果上述命令返回Nil,那么客户端可以在一段时间之后重新尝试),并且可以通过DEL命令来释放锁。
客户端加锁之后,如果没有主动释放,会在过期时间之后自动释放。
可以通过如下优化使得上面的锁系统变得更加鲁棒:
不要设置固定的字符串,而是设置为随机的大字符串,可以称为token。
通过脚步删除指定锁的key,而不是DEL命令。
上述优化方法会避免下述场景:a客户端获得的锁(键key)已经由于过期时间到了被redis服务器删除,但是这个时候a客户端还去执行DEL命令。而b客户端已经在a设置的过期时间之后重新获取了这个同样key的锁,那么a执行DEL就会释放了b客户端加好的锁。
SETEX
SETEX key seconds value
起始版本:2.0.0
时间复杂度:O(1)
设置key对应字符串value,并且设置key在给定的seconds时间之后超时过期。这个命令等效于执行下面的命令:

SET mykey value
EXPIRE mykey seconds
• 例子

SETEX mykey 10 “Hello”
OK
TTL mykey
(integer) 10
GET mykey
“Hello”

SETNX
SETNX key value
起始版本:1.0.0
时间复杂度:O(1)
将key设置值为value,如果key不存在,这种情况下等同SET命令。 当key存在时,什么也不做。SETNX是”SET if Not eXists”的简写。
• 例子

SETNX mykey “Hello”
(integer) 1
SETNX mykey “World”
(integer) 0
GET mykey
“Hello”

• RedisLock
设计模式:使用!SETNX加锁
Please note that:
请注意:
不鼓励以下模式来实现the Redlock algorithm ,该算法实现起来有一些复杂,但是提供了更好的保证并且具有容错性。
无论如何,我们保留旧的模式,因为肯定存在一些已实现的方法链接到该页面作为引用。而且,这是一个有趣的例子说明Redis命令能够被用来作为编程原语的。
无论如何,即使假设一个单例的加锁原语,但是从 2.6.12 开始,可以创建一个更加简单的加锁原语,相当于使用SET命令来获取锁,并且用一个简单的 Lua 脚本来释放锁。该模式被记录在SET命令的页面中。
也就是说,SETNX能够被使用并且以前也在被使用去作为一个加锁原语。例如,获取键为foo的锁,客户端可以尝试一下操作:
SETNX lock.foo <current Unix time + lock timeout + 1>
如果客户端获得锁,SETNX返回1,那么将lock.foo键的Unix时间设置为不在被认为有效的时间。客户端随后会使用DEL lock.foo去释放该锁。
如果SETNX返回0,那么该键已经被其他的客户端锁定。如果这是一个非阻塞的锁,才能立刻返回给调用者,或者尝试重新获取该锁,直到成功或者过期超时。
处理死锁
以上加锁算法存在一个问题:如果客户端出现故障,崩溃或者其他情况无法释放该锁会发生什么情况?这是能够检测到这种情况,因为该锁包含一个Unix时间戳,如果这样一个时间戳等于当前的Unix时间,该锁将不再有效。
当以下这种情况发生时,我们不能调用DEL来删除该锁,并且尝试执行一个SETNX,因为这里存在一个竞态条件,当多个客户端察觉到一个过期的锁并且都尝试去释放它。
C1 和 C2 读lock.foo检查时间戳,因为他们执行完SETNX后都被返回了0,因为锁仍然被 C3 所持有,并且 C3 已经崩溃。
C1 发送DEL lock.foo
C1 发送SETNX lock.foo命令并且成功返回
C2 发送DEL lock.foo
C2 发送SETNX lock.foo命令并且成功返回
错误:由于竞态条件导致 C1 和 C2 都获取到了锁
幸运的是,可以使用以下的算法来避免这种情况,请看 C4 客户端所使用的好的算法:
C4 发送SETNX lock.foo为了获得该锁
已经崩溃的客户端 C3 仍然持有该锁,所以Redis将会返回0给 C4
C4 发送GET lock.foo检查该锁是否已经过期。如果没有过期,C4 客户端将会睡眠一会,并且从一开始进行重试操作
另一种情况,如果因为 lock.foo键的Unix时间小于当前的Unix时间而导致该锁已经过期,C4 会尝试执行以下的操作:
GETSET lock.foo <current Unix timestamp + lock timeout + 1>
由于GETSET 的语意,C4会检查已经过期的旧值是否仍然存储在lock.foo中。如果是的话,C4 会获得锁
如果另一个客户端,假如为 C5 ,比 C4 更快的通过GETSET操作获取到锁,那么 C4 执行GETSET操作会被返回一个不过期的时间戳。C4 将会从第一个步骤重新开始。请注意:即使 C4 在将来几秒设置该键,这也不是问题。
为了使这种加锁算法更加的健壮,持有锁的客户端应该总是要检查是否超时,保证使用DEL释放锁之前不会过期,因为客户端故障的情况可能是复杂的,不止是崩溃,还会阻塞一段时间,阻止一些操作的执行,并且在阻塞恢复后尝试执行DEL(此时,该LOCK已经被其他客户端所持有)
SETRANGE
SETRANGE key offset value
起始版本:2.2.0
时间复杂度:O(1)

这个命令的作用是覆盖key对应的string的一部分,从指定的offset处开始,覆盖value的长度。如果offset比当前key对应string还要长,那这个string后面就补0以达到offset。不存在的keys被认为是空字符串,所以这个命令可以确保key有一个足够大的字符串,能在offset处设置value。
• 例子

SET key1 “Hello World”
OK
SETRANGE key1 6 “Redis”
(integer) 11
GET key1
“Hello Redis”

补0的例子:

SETRANGE key2 6 “Redis”
(integer) 11
GET key2
“\x00\x00\x00\x00\x00\x00Redis”

STRLEN
STRLEN key
起始版本:2.2.0
时间复杂度:O(1)
返回key的string类型value的长度。如果key对应的非string类型,就返回错误。
• 例子

SET mykey “Hello world”
OK
STRLEN mykey
(integer) 11
STRLEN nonexisting
(integer) 0

参考文档 :http://redis.cn/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值