(翻译)官网:Redis 数据类型和概念的简介(持续更新)

467 阅读9分钟

Redis 不是一个简单的 Key-value 数据库,它实际上是一个可以支持不同类型数据的数据结构服务器。意思就是说,一些传统的 K-V 存储你是通过 string 的 key 对应上 string 的 value,在 Redis 中,value 值不一定需要是普通的 string,它还能是其他的复杂的数据结构。下面的列表列举了 Redis 支持的所有的数据结构,本教程将会分别介绍这些数据结构:

  • String: 二进制安全的字符串;
  • List: 根据查询顺序来排序的 string 集合。它是基于链表来实现的;
  • Sets: 不重复,无序的 string 集合
  • Sorted Sets: 和 Sets 比较类似,但它每个 string 元素都会和一个称为 score 的浮点型数值关联在一起。这些元素会根据它的分数进行排序,它可以搜索出一系列元素,譬如:可以过得集合的前10名和后10名,这点和 Sets 不一样;
  • Hashes: 这是一个由字段-值组成的的映射数据结构。字段和值都是 string 类型。这个和 Ruby 或者 Python 的 hashes 结构很像。
  • Bit arrays (位图 bitmaps),这种数据结构可以通过一些特别的命令想处理bit数组一样处理string:譬如可以 set 或 clear 某个 bit 位,计算所有 bit 为 1 的 bit 数量, 找到第一个 set 或 unset 的 bit 位,等等
  • HyperLogLogs: 这是一种概率性数据结构,可以用来估算集合的数量
  • Streams: append-only collections of map-like entries that provide an abstract log data type. They are covered in depth in the Introduction to Redis Streams.

通过阅读命令参考文档是很难掌握这些数据类型原理的,具体问题该用哪种数据类型去解决也没法掌握,这个文档是一个速成课程,帮助你去了解 Redis 的数据类型还有这些数据类型常用的模式。所有的例子我们都会使用 redis-cli (实用并且简单的命令行工具) 去对 Redis 服务器发出命令。

Redis keys

Redis 键值是二进制安全的,这就意味着你可以使用任何的二进制序列作为 key 去使用,简单的 "foo" 字符串或一个 JPEG 二进制文件数据也可以。空字符串也是一个有效的key。

这里列举一些使用 keys 的规则:

  • 不要使用太长的 keys。例如,一个1k的key除了在内存上会比较占空间外,在查找这些keys的时候,匹配会非常占用计算资源。即使任务的就是做一个匹配工作,对keys做一个hash(譬如使用 SHA1 进行 hash)会更加好,特别是从内存还有带宽的角度上分析。
  • 太短的 keys 也不好。相比之下,"user:1000:followers"会比"u1000flw"好得多。前者更加可读,相比于键值对,增加的一点空间是微不足道的。虽然短键会消耗更少的内存,但合适才是更重要的。
  • 尝试使用一些结构化的keys。"object-type:id" 就是一种很好设计,像"user:1000"。"." 或者 "-" 经常用于多单词的字段。譬如:"comment:1234:reply.to" or "comment:1234:reply-to"。
  • key 的最大长度是 512MB。

Redis Strings

String 类型是 Redis 中最简单的类型。这也是 Memcached 唯一的书类型,所以 Redis 初学者使用这种类型的时候会比较熟悉。

因为 Redis 的keys 也是 String 类型,当我们使用 String 为 Value 时,我们就会得到一个 K-V 两者都是 String 的键值映射。这种数据类型适合非常多的场景,譬如缓存 HTML 的片段或整个页面。

我们先试一试使用 string 数据类型

myredis:7002> set myKey somevalue
OK
myredis:7002> get myKey
"somevalue"

正如你所看到的, SETGET 命令是我们用于写入和读取 string 类型的。请注意,SET 命令会把这个键已经映射保存的旧值给替换掉,就算这个 keys 映射的不是 string 类型。所以 SET 是一个执行分配的命令。值可以是任何类型的 string (包括二进制数据),譬如,你可以保存一个 jpeg 格式的图面。一个值,同样不能大于 512 MB。

SET命令有很有附加的参数。如下,假如 key 值存在,参数可以让我失败,相反地,也可以通过参数让我只能更新存在的值。

myredis:7002> set myKey newValue NX     // 只能设置空值
(nil)
myredis:7002> set myKey2 newValue XX    // 只能设置旧值
(nil)
myredis:7002> set myKey newValue XX
OK
myredis:7002> set myKey2 newValue NX
OK

即使 string 是 Redis 的基本数据类型,仍然可以使用它去执行一些有趣的操作(译者注:超越字符串的功能),譬如,原子地递增:

myredis:7002> set couter 1000
OK
myredis:7002> incr couter
(integer) 1001
myredis:7002> incr couter
(integer) 1002
myredis:7002> incrby couter 50
(integer) 1052

INCR 命令会把 string 类型先解析为一个 Integer 类型,然后递增1,接着把最终的值设置为新值。还有一些类似的命令如 INCRBY, DECR 和 DECRBY,内部实现他们都是执行相同的命令,只是执行略有不同。

INCR 是原子的,这个是什么意思?即使多个客户端提交 INCR 命令到同一个 key 中,也不会进入竞态条件。譬如,client1 读取 10 ,client2 也读取了10,两者都通过INCR递增这个值到11,但假如有两次提交,他们就只能读到12了,感觉就像,当他们执行 read-increment-set 操作时,其他所有的客户端都不会同时执行这个命令一样。

译者注:原理上是因为Redis本身就是单进程单线程的程序设计,所以用于不会进入竞态。省了不少事。

Redis 还有一些列的命令来设置 string的。例如 GETSET 用来为一个key设置新值的同时,也会返回旧值。譬如,你需要通过INCR命令来统计网站访问数,你可能希望每小时收集一次这个数据,但都不希望丢失任何的记录。那就可以使用GETSET命令,指定 key 的值为 0,同时又返回旧值。(译者注:因为 redis 的命令都是序列执行的)

假如有一个命令可以写入或读取多个 keys ,那会非常有用,而且能减少等待时间。因此,就有了MSETMGET 命令。

myredis:7002> mset a 10 b 20 c 30
OK
myredis:7002> mget a b c 
1) "10"
2) "20"
3) "30"

当执行 MGET 时, Redis 会返回一个 value 的数组。

更改和查询 keys 空间

有一些命令并不是为了操作任何一个数据类型的,而是面向 keys 空间的,因此,这些命令是可以用在任何type上的。(译者注:就是说有一些命令是只面向keys的,和key关联的类型无关)

譬如,EXISTS 命令会传入一个 key,假如 key 存在 Redis 中,就会返回1,否则返回0。又譬如,DEL 命令会删除一个 key 还有它映射的值,不管这个key或者值是否存在。

myredis:7002> exists mykey
(integer) 1 // 存在
myredis:7002> exists mykey2
(integer) 0 // 不存在
myredis:7002> del mykey
(integer) 1 // 删除成功
myredis:7002> del mykey
(integer) 0 // 删除失败
myredis:7002> 

从上面的例子你可以发现,DEL 命令根据 key 是否存在返回 1 或 0.

除此以外,还有很多面向 key 空间的命令,但以上两个命令加上TYPE命令是非常常用的(必不可少的),TYPE 命令可以返回指定key的值的类型。

myredis:7002> type mykey
none
myredis:7002> type a
string

Redis 过期设定 —— 会过期的 keys

在继续更复杂的数据结构前,我们需要讨论另外一个功能,也是和值类型无关的,称作:Redis expires(Redis 过期设定)。简单来说,就是为一个key设置一个 timeout 的属性,这个属性会让 key 有一个有限的生命周期。当这个时间过期,这个 key 会自动的被销毁,就像用户调用EDL命令一样。

以下是关于 Redis expires 的几条简单的设定:

  • 过期值可以是 毫秒 为单位;
  • 但是识别的分辨率依然是 1 毫秒;
  • 关于过期信息的数据会被复制并保存在磁盘中,这就意味着,假如你的 Redis 服务器停止了,数据还会保存在 Rdis 中。

为一个 key 设置一个过期时间是非常简单的:

myredis:7002> set key some-value
OK
myredis:7002> expire key 5
(integer) 1
myredis:7002> get key
"some-value"
myredis:7002> get key (过了一会)
(nil)

如上图所示,由于我们为 key 设置了 5s 的过期时间,在没有任何操作时,key 就失效了。在上面这个例子中,我们使用 EXPIRE 命令来设置过期时间(它也可以用于重设某个key的过期时间,PERSIST可以用于移除一个 key 的有限时间设定,让key一直存在)。然而,我们还可以直接使用其他的命令来构建 key 的过期时间,下面是 SET 的例子。

myredis:7002> set mykey myvalue ex 10
OK
myredis:7002> ttl mykey (过了8s)
(integer) 2
myredis:7002> ttl mykey
(integer) 1
myredis:7002> ttl mykey (过期)
(integer) -2

以上的例子先给 mykey 设置一个值 myvalue,过期时间是 10s 。然后我们使用 TTL 命令来查询某个key的存活时间(秒数)。

要是希望可以设置并检查毫秒级别的数值,那可以使用 PEXPIREPTTL 命令,当然,SET 命令的其他参数也是可以设定的。