您的位置:首页 > 数据库 > Redis

Redis学习记录之命令String(九)

2016-01-12 14:08 806 查看
1、INCR

INCR key

将 key 中储存的数字值增一。

如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。

如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。

本操作的值限制在 64 位(bit)有符号数字表示之内。

这是一个针对字符串的操作,因为 Redis 没有专用的整数类型,所以 key 内储存的字符串被解释为十进制 64 位有符号整数来执行 INCR 操作。

可用版本:

1.0.0及以上

返回值:

执行 INCR 命令之后 key 的值。

redis> SET page_view 20

OK

redis> INCR page_view

(integer) 21

redis> GET page_view # 数字值在 Redis 中以字符串的形式保存

“21”

模式:计数器

计数器是 Redis 的原子性自增操作可实现的最直观的模式了,它的想法相当简单:每当某个操作发生时,向 Redis 发送一个 INCR 命令。

比如在一个 web 应用程序中,如果想知道用户在一年中每天的点击量,那么只要将用户 ID 以及相关的日期信息作为键,并在每次用户点击页面时,执行一次自增操作即可。

比如用户名是 peter ,点击时间是 2012 年 3 月 22 日,那么执行命令 INCR peter::2012.3.22 。

可以用以下几种方式扩展这个简单的模式:

可以通过组合使用 INCR 和 EXPIRE ,来达到只在规定的生存时间内进行计数(counting)的目的。

客户端可以通过使用 GETSET 命令原子性地获取计数器的当前值并将计数器清零,更多信息请参考 GETSET 命令。

使用其他自增/自减操作,比如 DECR 和 INCRBY ,用户可以通过执行不同的操作增加或减少计数器的值,比如在游戏中的记分器就可能用到这些命令。

模式:限速器

限速器是特殊化的计算器,它用于限制一个操作可以被执行的速率(rate)。

限速器的典型用法是限制公开 API 的请求次数,以下是一个限速器实现示例,它将 API 的最大请求数限制在每个 IP 地址每秒钟十个之内:

FUNCTION LIMIT_API_CALL(ip)

ts = CURRENT_UNIX_TIME()

keyname = ip+”:”+ts

current = GET(keyname)

IF current != NULL AND current > 10 THEN

ERROR “too many requests per second”

END

IF current == NULL THEN

MULTI

INCR(keyname, 1)

EXPIRE(keyname, 1)

EXEC

ELSE

INCR(keyname, 1)

END

PERFORM_API_CALL()

这个实现每秒钟为每个 IP 地址使用一个不同的计数器,并用 EXPIRE 命令设置生存时间(这样 Redis 就会负责自动删除过期的计数器)。

注意,我们使用事务打包执行 INCR 命令和 EXPIRE 命令,避免引入竞争条件,保证每次调用 API 时都可以正确地对计数器进行自增操作并设置生存时间。

以下是另一个限速器实现:

FUNCTION LIMIT_API_CALL(ip):

current = GET(ip)

IF current != NULL AND current > 10 THEN

ERROR “too many requests per second”

ELSE

value = INCR(ip)

IF value == 1 THEN

EXPIRE(ip,1)

END

PERFORM_API_CALL()

END

这个限速器只使用单个计数器,它的生存时间为一秒钟,如果在一秒钟内,这个计数器的值大于 10 的话,那么访问就会被禁止。

这个新的限速器在思路方面是没有问题的,但它在实现方面不够严谨,如果我们仔细观察一下的话,就会发现在 INCR 和 EXPIRE 之间存在着一个竞争条件,假如客户端在执行 INCR 之后,因为某些原因(比如客户端失败)而忘记设置 EXPIRE 的话,那么这个计数器就会一直存在下去,造成每个用户只能访问 10 次,噢,这简直是个灾难!

要消灭这个实现中的竞争条件,我们可以将它转化为一个 Lua 脚本,并放到 Redis 中运行(这个方法仅限于 Redis 2.6 及以上的版本):

local current

current = redis.call(“incr”,KEYS[1])

if tonumber(current) == 1 then

redis.call(“expire”,KEYS[1],1)

end

通过将计数器作为脚本放到 Redis 上运行,我们保证了 INCR 和 EXPIRE 两个操作的原子性,现在这个脚本实现不会引入竞争条件,它可以运作的很好。

关于在 Redis 中运行 Lua 脚本的更多信息,请参考 EVAL 命令。

还有另一种消灭竞争条件的方法,就是使用 Redis 的列表结构来代替 INCR 命令,这个方法无须脚本支持,因此它在 Redis 2.6 以下的版本也可以运行得很好:

FUNCTION LIMIT_API_CALL(ip)

current = LLEN(ip)

IF current > 10 THEN

ERROR “too many requests per second”

ELSE

IF EXISTS(ip) == FALSE

MULTI

RPUSH(ip,ip)

EXPIRE(ip,1)

EXEC

ELSE

RPUSHX(ip,ip)

END

PERFORM_API_CALL()

END

新的限速器使用了列表结构作为容器, LLEN 用于对访问次数进行检查,一个事务包裹着 RPUSH 和 EXPIRE 两个命令,用于在第一次执行计数时创建列表,并正确设置地设置过期时间,最后, RPUSHX 在后续的计数操作中进行增加操作。

2、INCRBY

INCRBY key increment

将 key 所储存的值加上增量 increment 。

如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCRBY 命令。

如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。

本操作的值限制在 64 位(bit)有符号数字表示之内。

关于递增(increment) / 递减(decrement)操作的更多信息,参见 INCR 命令。

可用版本:

1.0.0及以上

返回值:

加上 increment 之后, key 的值。

key 存在且是数字值

redis> SET rank 50

OK

redis> INCRBY rank 20

(integer) 70

redis> GET rank

“70”

key 不存在时

redis> EXISTS counter

(integer) 0

redis> INCRBY counter 30

(integer) 30

redis> GET counter

“30”

key 不是数字值时

redis> SET book “long long ago…”

OK

redis> INCRBY book 200

(error) ERR value is not an integer or out of range

3、INCRBYFLOAT

INCRBYFLOAT key increment

为 key 中所储存的值加上浮点数增量 increment 。

如果 key 不存在,那么 INCRBYFLOAT 会先将 key 的值设为 0 ,再执行加法操作。

如果命令执行成功,那么 key 的值会被更新为(执行加法之后的)新值,并且新值会以字符串的形式返回给调用者。

无论是 key 的值,还是增量 increment ,都可以使用像 2.0e7 、 3e5 、 90e-2 那样的指数符号(exponential notation)来表示,但是,执行 INCRBYFLOAT 命令之后的值总是以同样的形式储存,也即是,它们总是由一个数字,一个(可选的)小数点和一个任意位的小数部分组成(比如 3.14 、 69.768 ,诸如此类),小数部分尾随的 0 会被移除,如果有需要的话,还会将浮点数改为整数(比如 3.0 会被保存成 3 )。

除此之外,无论加法计算所得的浮点数的实际精度有多长, INCRBYFLOAT 的计算结果也最多只能表示小数点的后十七位。

当以下任意一个条件发生时,返回一个错误:

key 的值不是字符串类型(因为 Redis 中的数字和浮点数都以字符串的形式保存,所以它们都属于字符串类型)

key 当前的值或者给定的增量 increment 不能解释(parse)为双精度浮点数(double precision floating point number)

可用版本:

2.6.0及以上

返回值:

执行命令之后 key 的值。

值和增量都不是指数符号

redis> SET mykey 10.50

OK

redis> INCRBYFLOAT mykey 0.1

“10.6”

值和增量都是指数符号

redis> SET mykey 314e-2

OK

redis> GET mykey # 用 SET 设置的值可以是指数符号

“314e-2”

redis> INCRBYFLOAT mykey 0 # 但执行 INCRBYFLOAT 之后格式会被改成非指数符号

“3.14”

可以对整数类型执行

redis> SET mykey 3

OK

redis> INCRBYFLOAT mykey 1.1

“4.1”

后跟的 0 会被移除

redis> SET mykey 3.0

OK

redis> GET mykey # SET 设置的值小数部分可以是 0

“3.0”

redis> INCRBYFLOAT mykey 1.000000000000000000000 # 但 INCRBYFLOAT 会将无用的 0 忽略掉,有需要的话,将浮点变为整数

“4”

redis> GET mykey

“4”

4、MGET

MGET key [key …]

返回所有(一个或多个)给定 key 的值。

如果给定的 key 里面,有某个 key 不存在,那么这个 key 返回特殊值 nil 。因此,该命令永不失败。

可用版本:

1.0.0及以上

返回值:

一个包含所有给定 key 的值的列表。

redis> SET redis redis.com

OK

redis> SET mongodb mongodb.org

OK

redis> MGET redis mongodb

1) “redis.com”

2) “mongodb.org”

redis> MGET redis mongodb mysql # 不存在的 mysql 返回 nil

1) “redis.com”

2) “mongodb.org”

3) (nil)

5、MSET

MSET key value [key value …]

同时设置一个或多个 key-value 对。

如果某个给定 key 已经存在,那么 MSET 会用新值覆盖原来的旧值,如果这不是你所希望的效果,请考虑使用 MSETNX 命令:它只会在所有给定 key 都不存在的情况下进行设置操作。

MSET 是一个原子性(atomic)操作,所有给定 key 都会在同一时间内被设置,某些给定 key 被更新而另一些给定 key 没有改变的情况,不可能发生。

可用版本:

1.0.1及以上

返回值:

总是返回 OK (因为 MSET 不可能失败)

redis> MSET date “2012.3.30” time “11:00 a.m.” weather “sunny”

OK

redis> MGET date time weather

1) “2012.3.30”

2) “11:00 a.m.”

3) “sunny”

MSET 覆盖旧值例子

redis> SET google “google.hk”

OK

redis> MSET google “google.com”

OK

redis> GET google

“google.com”

6、MSETNX

MSETNX key value [key value …]

同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在。

即使只有一个给定 key 已存在, MSETNX 也会拒绝执行所有给定 key 的设置操作。

MSETNX 是原子性的,因此它可以用作设置多个不同 key 表示不同字段(field)的唯一性逻辑对象(unique logic object),所有字段要么全被设置,要么全不被设置。

可用版本:

1.0.1及以上

返回值:

当所有 key 都成功设置,返回 1 。

如果所有给定 key 都设置失败(至少有一个 key 已经存在),那么返回 0 。

对不存在的 key 进行 MSETNX

redis> MSETNX rmdbs “MySQL” nosql “MongoDB” key-value-store “redis”

(integer) 1

redis> MGET rmdbs nosql key-value-store

1) “MySQL”

2) “MongoDB”

3) “redis”

MSET 的给定 key 当中有已存在的 key

redis> MSETNX rmdbs “Sqlite” language “python” # rmdbs 键已经存在,操作失败

(integer) 0

redis> EXISTS language # 因为 MSET 是原子性操作,language 没有被设置

(integer) 0

redis> GET rmdbs # rmdbs 也没有被修改

“MySQL”

7、MSETNX

MSETNX key value [key value …]

同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在。

即使只有一个给定 key 已存在, MSETNX 也会拒绝执行所有给定 key 的设置操作。

MSETNX 是原子性的,因此它可以用作设置多个不同 key 表示不同字段(field)的唯一性逻辑对象(unique logic object),所有字段要么全被设置,要么全不被设置。

可用版本:

1.0.1及以上

返回值:

当所有 key 都成功设置,返回 1 。

如果所有给定 key 都设置失败(至少有一个 key 已经存在),那么返回 0 。

对不存在的 key 进行 MSETNX

redis> MSETNX rmdbs “MySQL” nosql “MongoDB” key-value-store “redis”

(integer) 1

redis> MGET rmdbs nosql key-value-store

1) “MySQL”

2) “MongoDB”

3) “redis”

MSET 的给定 key 当中有已存在的 key

redis> MSETNX rmdbs “Sqlite” language “python” # rmdbs 键已经存在,操作失败

(integer) 0

redis> EXISTS language # 因为 MSET 是原子性操作,language 没有被设置

(integer) 0

redis> GET rmdbs # rmdbs 也没有被修改

“MySQL”

8、PSETEX

PSETEX key milliseconds value

这个命令和 SETEX 命令相似,但它以毫秒为单位设置 key 的生存时间,而不是像 SETEX 命令那样,以秒为单位。

可用版本:

2.6.0

返回值:

设置成功时返回 OK 。

redis> PSETEX mykey 1000 “Hello”

OK

redis> PTTL mykey

(integer) 999

redis> GET mykey

“Hello”

9、SET

SET key value [EX seconds] [PX milliseconds] [NX|XX]

将字符串值 value 关联到 key 。

如果 key 已经持有其他值, SET 就覆写旧值,无视类型。

对于某个原本带有生存时间(TTL)的键来说, 当 SET 命令成功在这个键上执行时, 这个键原有的 TTL 将被清除。

可选参数

从 Redis 2.6.12 版本开始, SET 命令的行为可以通过一系列参数来修改:

EX second :设置键的过期时间为 second 秒。 SET key value EX second 效果等同于 SETEX key second value 。

PX millisecond :设置键的过期时间为 millisecond 毫秒。 SET key value PX millisecond 效果等同于 PSETEX key millisecond value 。

NX :只在键不存在时,才对键进行设置操作。 SET key value NX 效果等同于 SETNX key value 。

XX :只在键已经存在时,才对键进行设置操作。

因为 SET 命令可以通过参数来实现和 SETNX 、 SETEX 和 PSETEX 三个命令的效果,所以将来的 Redis 版本可能会废弃并最终移除 SETNX 、 SETEX 和 PSETEX 这三个命令。

可用版本:

1.0.0及以上

返回值:

在 Redis 2.6.12 版本以前, SET 命令总是返回 OK 。

从 Redis 2.6.12 版本开始, SET 在设置操作成功完成时,才返回 OK 。

如果设置了 NX 或者 XX ,但因为条件没达到而造成设置操作未执行,那么命令返回空批量回复(NULL Bulk Reply)。

对不存在的键进行设置

redis 127.0.0.1:6379> SET key “value”

OK

redis 127.0.0.1:6379> GET key

“value”

对已存在的键进行设置

redis 127.0.0.1:6379> SET key “new-value”

OK

redis 127.0.0.1:6379> GET key

“new-value”

使用 EX 选项

redis 127.0.0.1:6379> SET key-with-expire-time “hello” EX 10086

OK

redis 127.0.0.1:6379> GET key-with-expire-time

“hello”

redis 127.0.0.1:6379> TTL key-with-expire-time

(integer) 10069

使用 PX 选项

redis 127.0.0.1:6379> SET key-with-pexpire-time “moto” PX 123321

OK

redis 127.0.0.1:6379> GET key-with-pexpire-time

“moto”

redis 127.0.0.1:6379> PTTL key-with-pexpire-time

(integer) 111939

使用 NX 选项

redis 127.0.0.1:6379> SET not-exists-key “value” NX

OK # 键不存在,设置成功

redis 127.0.0.1:6379> GET not-exists-key

“value”

redis 127.0.0.1:6379> SET not-exists-key “new-value” NX

(nil) # 键已经存在,设置失败

redis 127.0.0.1:6379> GEt not-exists-key

“value” # 维持原值不变

使用 XX 选项

redis 127.0.0.1:6379> EXISTS exists-key

(integer) 0

redis 127.0.0.1:6379> SET exists-key “value” XX

(nil) # 因为键不存在,设置失败

redis 127.0.0.1:6379> SET exists-key “value”

OK # 先给键设置一个值

redis 127.0.0.1:6379> SET exists-key “new-value” XX

OK # 设置新值成功

redis 127.0.0.1:6379> GET exists-key

“new-value”

NX 或 XX 可以和 EX 或者 PX 组合使用

redis 127.0.0.1:6379> SET key-with-expire-and-NX “hello” EX 10086 NX

OK

redis 127.0.0.1:6379> GET key-with-expire-and-NX

“hello”

redis 127.0.0.1:6379> TTL key-with-expire-and-NX

(integer) 10063

redis 127.0.0.1:6379> SET key-with-pexpire-and-XX “old value”

OK

redis 127.0.0.1:6379> SET key-with-pexpire-and-XX “new value” PX 123321

OK

redis 127.0.0.1:6379> GET key-with-pexpire-and-XX

“new value”

redis 127.0.0.1:6379> PTTL key-with-pexpire-and-XX

(integer) 112999

EX 和 PX 可以同时出现,但后面给出的选项会覆盖前面给出的选项

redis 127.0.0.1:6379> SET key “value” EX 1000 PX 5000000

OK

redis 127.0.0.1:6379> TTL key

(integer) 4993 # 这是 PX 参数设置的值

redis 127.0.0.1:6379> SET another-key “value” PX 5000000 EX 1000

OK

redis 127.0.0.1:6379> TTL another-key

(integer) 997 # 这是 EX 参数设置的值

使用模式

命令 SET resource-name anystring NX EX max-lock-time 是一种在 Redis 中实现锁的简单方法。

客户端执行以上的命令:

如果服务器返回 OK ,那么这个客户端获得锁。

如果服务器返回 NIL ,那么客户端获取锁失败,可以在稍后再重试。

设置的过期时间到达之后,锁将自动释放。

可以通过以下修改,让这个锁实现更健壮:

不使用固定的字符串作为键的值,而是设置一个不可猜测(non-guessable)的长随机字符串,作为口令串(token)。

不使用 DEL 命令来释放锁,而是发送一个 Lua 脚本,这个脚本只在客户端传入的值和键的口令串相匹配时,才对键进行删除。

这两个改动可以防止持有过期锁的客户端误删现有锁的情况出现。

以下是一个简单的解锁脚本示例:

if redis.call(“get”,KEYS[1]) == ARGV[1]

then

return redis.call(“del”,KEYS[1])

else

return 0

end

这个脚本可以通过 EVAL …script… 1 resource-name token-value 命令来调用。

10、SETBIT

SETBIT key offset value

对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)。

位的设置或清除取决于 value 参数,可以是 0 也可以是 1 。

当 key 不存在时,自动生成一个新的字符串值。

字符串会进行伸展(grown)以确保它可以将 value 保存在指定的偏移量上。当字符串值进行伸展时,空白位置以 0 填充。

offset 参数必须大于或等于 0 ,小于 2^32 (bit 映射被限制在 512 MB 之内)。

对使用大的 offset 的 SETBIT 操作来说,内存分配可能造成 Redis 服务器被阻塞。具体参考 SETRANGE 命令,warning(警告)部分。

可用版本:

2.2.0及以上

返回值:

指定偏移量原来储存的位。

redis> SETBIT bit 10086 1

(integer) 0

redis> GETBIT bit 10086

(integer) 1

redis> GETBIT bit 100 # bit 默认被初始化为 0

(integer) 0

11、SETEX

SETEX key seconds value

将值 value 关联到 key ,并将 key 的生存时间设为 seconds (以秒为单位)。

如果 key 已经存在, SETEX 命令将覆写旧值。

这个命令类似于以下两个命令:

SET key value

EXPIRE key seconds # 设置生存时间

不同之处是, SETEX 是一个原子性(atomic)操作,关联值和设置生存时间两个动作会在同一时间内完成,该命令在 Redis 用作缓存时,非常实用。

可用版本:

2.0.0及以上

返回值:

设置成功时返回 OK 。

当 seconds 参数不合法时,返回一个错误。

在 key 不存在时进行 SETEX

redis> SETEX cache_user_id 60 10086

OK

redis> GET cache_user_id # 值

“10086”

redis> TTL cache_user_id # 剩余生存时间

(integer) 49

key 已经存在时,SETEX 覆盖旧值

redis> SET cd “timeless”

OK

redis> SETEX cd 3000 “goodbye my love”

OK

redis> GET cd

“goodbye my love”

redis> TTL cd

(integer) 2997

12、SETNX

SETNX key value

将 key 的值设为 value ,当且仅当 key 不存在。

若给定的 key 已经存在,则 SETNX 不做任何动作。

SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。

可用版本:

1.0.0及以上

返回值:

设置成功,返回 1 。

设置失败,返回 0 。

redis> EXISTS job # job 不存在

(integer) 0

redis> SETNX job “programmer” # job 设置成功

(integer) 1

redis> SETNX job “code-farmer” # 尝试覆盖 job ,失败

(integer) 0

redis> GET job # 没有被覆盖

“programmer”

13、SETRANGE

SETRANGE key offset value

用 value 参数覆写(overwrite)给定 key 所储存的字符串值,从偏移量 offset 开始。

不存在的 key 当作空白字符串处理。

SETRANGE 命令会确保字符串足够长以便将 value 设置在指定的偏移量上,如果给定 key 原来储存的字符串长度比偏移量小(比如字符串只有 5 个字符长,但你设置的 offset 是 10 ),那么原字符和偏移量之间的空白将用零字节(zerobytes, “\x00” )来填充。

注意你能使用的最大偏移量是 2^29-1(536870911) ,因为 Redis 字符串的大小被限制在 512 兆(megabytes)以内。如果你需要使用比这更大的空间,你可以使用多个 key 。

当生成一个很长的字符串时,Redis 需要分配内存空间,该操作有时候可能会造成服务器阻塞(block)。在2010年的Macbook Pro上,设置偏移量为 536870911(512MB 内存分配),耗费约 300 毫秒, 设置偏移量为 134217728(128MB 内存分配),耗费约 80 毫秒,设置偏移量 33554432(32MB 内存分配),耗费约 30 毫秒,设置偏移量为 8388608(8MB 内存分配),耗费约 8 毫秒。 注意若首次内存分配成功之后,再对同一个 key 调用 SETRANGE 操作,无须再重新内存。

可用版本:

2.2.0及以上

时间复杂度:

对小(small)的字符串,平摊复杂度O(1)。(关于什么字符串是”小”的,请参考 APPEND 命令)

否则为O(M), M 为 value 参数的长度。

返回值:

被 SETRANGE 修改之后,字符串的长度。

对非空字符串进行 SETRANGE

redis> SET greeting “hello world”

OK

redis> SETRANGE greeting 6 “Redis”

(integer) 11

redis> GET greeting

“hello Redis”

对空字符串/不存在的 key 进行 SETRANGE

redis> EXISTS empty_string

(integer) 0

redis> SETRANGE empty_string 5 “Redis!” # 对不存在的 key 使用 SETRANGE

(integer) 11

redis> GET empty_string # 空白处被”\x00”填充

“\x00\x00\x00\x00\x00Redis!”

模式

因为有了 SETRANGE 和 GETRANGE 命令,你可以将 Redis 字符串用作具有O(1)随机访问时间的线性数组,这在很多真实用例中都是非常快速且高效的储存方式,具体请参考 APPEND 命令的『模式:时间序列』部分。

14、STRLEN

STRLEN key

返回 key 所储存的字符串值的长度。

当 key 储存的不是字符串值时,返回一个错误。

可用版本:

= 2.2.0

复杂度:

O(1)

返回值:

字符串值的长度。

当 key 不存在时,返回 0 。

获取字符串的长度

redis> SET mykey “Hello world”

OK

redis> STRLEN mykey

(integer) 11

不存在的 key 长度为 0

redis> STRLEN nonexisting

(integer) 0
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: