redis/try.redis 项目中教程部分的中文翻译
Redis 属于键值存储数据库家族。
键值存储的本质是能够在一个键中存储一些称为值的数据。只有当我们知道用于存储它的确切键时,才能稍后检索这些数据。
Redis 通常被称为数据结构服务器,因为它有一个外部的键值存储外壳,但每个值可以包含复杂的数据结构,如字符串、列表、哈希,或称为有序集合的有序数据结构以及像 hyperloglog 这样的概率数据结构。
作为第一个例子,我们可以使用 SET
命令在键 server:name
中存储值 "fido"
:
SET server:name "fido"
Redis 将永久存储我们的数据,所以我们稍后可以询问"存储在键 server:name 中的值是什么?",Redis 将回答 "fido"
:
GET server:name => "fido"
有一个命令用于测试给定的键是否存在:
EXISTS server:name => 1
EXISTS server:blabla => 0
Redis 提供的其他基本操作包括 DEL
用于删除给定的键和关联的值,INCR
用于原子地递增存储在给定键中的数字:
SET connections 10
INCR connections => 11
INCR connections => 12
DEL connections
INCR connections => 1
也可以将键中包含的数字增加特定的数量:
INCRBY connections 100 => 101
还有类似的命令用于递减键的值。
DECR connections => 100
DECRBY connections 10 => 90
当你使用递增和递减命令操作 Redis 字符串时,你就是在实现计数器。计数器是 Redis 的一个非常流行的应用。
INCR
有一些特别之处。为什么我们要提供这样一个操作,如果我们可以自己用一点代码来完成呢? 毕竟它就像这样简单:
x = GET count
x = x + 1
SET count x
问题是,只有在单个客户端使用该键的情况下,以这种方式进行递增才能正常工作。看看当两个客户端同时访问这个键时会发生什么:
- 客户端 A 读取 count 为 10。
- 客户端 B 读取 count 为 10。
- 客户端 A 将 10 递增并将 count 设置为 11。
- 客户端 B 将 10 递增并将 count 设置为 11。
我们希望值为 12,但实际上是 11! 这是因为以这种方式递增值不是一个原子操作。在 Redis 中调用 INCR
命令将防止这种情况发生,因为它是一个原子操作。
Redis 中由单个命令实现的所有操作都是原子的,包括那些操作更复杂数据结构的操作。所以当你使用修改某些值的 Redis 命令时,你不必考虑并发访问。
可以告诉 Redis 一个键应该只存在一定的时间。这是通过 EXPIRE
和 TTL
命令实现的,以及类似的 PEXPIRE
和 PTTL
命令,这些命令使用毫秒而不是秒来操作时间。
SET resource:lock "Redis Demo"
EXPIRE resource:lock 120
这会导致键 resource:lock 在 120 秒后被删除。你可以用 TTL
命令测试一个键还会存在多长时间。它返回直到被删除的秒数。
TTL resource:lock => 113
// 113秒后
TTL resource:lock => -2
键的 TTL
为 -2 意味着该键不存在(或不再存在)。键的 TTL
为 -1 意味着它永远不会过期。注意,如果你 SET
一个键,它的 TTL
将被重置。
SET resource:lock "Redis Demo 1"
EXPIRE resource:lock 120
TTL resource:lock => 119
SET resource:lock "Redis Demo 2"
TTL resource:lock => -1
SET
命令实际上能够接受更多参数,以直接为键设置生存时间(TTL),因此你可以在单个原子操作中同时改变键的值并设置其 TTL:
SET resource:lock "Redis Demo 3" EX 5
TTL resource:lock => 5
也可以取消键的生存时间,移除过期并再次使键永久化。
SET resource:lock "Redis Demo 3" EX 5
PERSIST resource:lock
TTL resource:lock => -1
Redis 还支持几种更复杂的数据结构。我们要看的第一个是列表。列表是一系列有序的值。用于与列表交互的一些重要命令是 RPUSH
、LPUSH
、LLEN
、LRANGE
、LPOP
和 RPOP
。只要一个键还不存在作为不同的类型,你就可以立即开始将其作为列表使用。
这个概念对每个 Redis 数据结构都普遍适用:你不需要先创建一个键,然后再添加内容,而是可以直接使用命令来添加新元素。作为副作用,如果键不存在,它将被创建。同样,执行某些命令后变为空的键将自动从键空间中删除。
RPUSH
将新元素放在列表的末尾。
RPUSH friends "Alice"
RPUSH friends "Bob"
LPUSH
将新元素放在列表的开头。
LPUSH friends "Sam"
LRANGE
给出列表的一个子集。它将你想检索的第一个元素的索引作为第一个参数,将你想检索的最后一个元素的索引作为第二个参数。第二个参数值为 -1 表示检索到列表末尾的元素,-2 表示包括到倒数第二个,以此类推。
LRANGE friends 0 -1 => 1) "Sam", 2) "Alice", 3) "Bob"
LRANGE friends 0 1 => 1) "Sam", 2) "Alice"
LRANGE friends 1 2 => 1) "Alice", 2) "Bob"
到目前为止,我们探讨了让你向列表添加元素的命令,以及让你检查列表范围的 LRANGE
。Redis 列表的一个基本功能是能够同时从列表的头部或尾部移除并返回给客户端元素。
LPOP
移除并返回列表的第一个元素。
LPOP friends => "Sam"
RPOP
移除并返回列表的最后一个元素。
RPOP friends => "Bob"
注意,现在列表只有一个元素:
LLEN friends => 1
LRANGE friends 0 -1 => 1) "Alice"
RPUSH
和 LPUSH
命令都是可变参数的,所以你可以在同一个命令执行中指定多个元素。
RPUSH friends 1 2 3 => 4
提示: RPUSH 和 LPUSH 返回操作后列表的总长度。
你也可以使用 LLEN
来获取列表的当前长度。
LLEN friends => 4
我们要看的下一个数据结构是集合。集合类似于列表,除了它没有特定的顺序,每个元素只能出现一次。这两种数据结构都非常有用,因为虽然在列表中快速访问靠近顶部或底部的元素,并且元素的顺序得到保留,但在集合中测试成员资格非常快,也就是说,立即知道是否添加了给定元素。此外,在集合中,给定元素只能以单一副本存在。
处理集合的一些重要命令是 SADD
、SREM
、SISMEMBER
、SMEMBERS
和 SUNION
。
SADD
将给定成员添加到集合中,同样,这个命令也是可变参数的。
SADD superpowers "flight"
SADD superpowers "x-ray vision" "reflexes"
SREM
从集合中移除给定成员,返回 1 或 0 表示该成员是否实际存在。
SREM superpowers "reflexes" => 1
SREM superpowers "making pizza" => 0
SISMEMBER
测试给定值是否在集合中。如果值存在则返回 1,不存在则返回 0。
SISMEMBER superpowers "flight" => 1
SISMEMBER superpowers "reflexes" => 0
SMEMBERS
返回此集合的所有成员列表。
SMEMBERS superpowers => 1) "flight", 2) "x-ray vision"
SUNION
组合两个或更多集合并返回所有元素的列表。
SADD birdpowers "pecking"
SADD birdpowers "flight"
SUNION superpowers birdpowers => 1) "pecking", 2) "x-ray vision", 3) "flight"
SADD
的返回值和 SREM
的一样重要。如果我们尝试添加的元素已经在里面,则返回 0,否则 SADD
返回 1:
SADD superpowers "flight" => 0
SADD superpowers "invisibility" => 1
集合还有一个非常类似于 LPOP
和 RPOP
的命令,用于从集合中提取元素并在单个操作中将它们返回给客户端。然而,由于集合是无序的数据结构,在这种情况下,返回(和移除)的元素是完全随机的。
SADD letters a b c d e f => 6
SPOP letters 2 => 1) "c" 2) "a"
SPOP
命令中键名后的参数是我们希望它返回并从集合中移除的元素数量。
现在集合将只有剩余的元素:
SMEMBERS letters => 1) "b" 2) "d" 3) "e" 4) "f"
还有一个命令可以返回随机元素而不从集合中移除这些元素,它叫做 SRANDMEMBER
。你可以自己尝试,参数与 SPOP
类似,但如果你指定一个负数而不是正数,它也可能返回重复的元素。
集合是一种非常方便的数据类型,但由于它们是无序的,对于一些问题来说效果不佳。这就是为什么 Redis 1.2 引入了有序集合。
有序集合类似于常规集合,但现在每个值都有一个关联的分数。这个分数用于对集合中的元素进行排序。
ZADD hackers 1940 "Alan Kay"
ZADD hackers 1906 "Grace Hopper"
ZADD hackers 1953 "Richard Stallman"
ZADD hackers 1965 "Yukihiro Matsumoto"
ZADD hackers 1916 "Claude Shannon"
ZADD hackers 1969 "Linus Torvalds"
ZADD hackers 1957 "Sophie Wilson"
ZADD hackers 1912 "Alan Turing"
在这些例子中,分数是出生年份,值是著名黑客的名字。
ZRANGE hackers 2 4 => 1) "Claude Shannon", 2) "Alan Kay", 3) "Richard Stallman"
简单字符串、集合和有序集合已经可以完成很多工作,但还有一种 Redis 可以处理的数据类型:哈希。
哈希是字符串字段和字符串值之间的映射,所以它们是表示对象的完美数据类型(例如:一个具有多个字段如名字、姓氏、年龄等的用户):
HSET user:1000 name "John Smith"
HSET user:1000 email "john.smith@example.com"
HSET user:1000 password "s3cret"
要获取保存的数据,使用 HGETALL
:
HGETALL user:1000
你也可以一次设置多个字段:
HMSET user:1001 name "Mary Jones" password "hidden" email "mjones@example.com"
如果你只需要单个字段值,也是可以的:
HGET user:1001 name => "Mary Jones"
哈希字段中的数值处理方式与简单字符串完全相同,并且有操作以原子方式递增这个值。
HSET user:1000 visits 10
HINCRBY user:1000 visits 1 => 11
HINCRBY user:1000 visits 10 => 21
HDEL user:1000 visits
HINCRBY user:1000 visits 1 => 1
查看完整的哈希命令列表以获取更多信息。
发表回复