<译> Redis 入门:安装、客户端命令和数据类型

本文翻译于 Dan AriasIntroduction to Redis: Installation, CLI Commands, and Data Types

文章虽然篇幅较长,但非常浅显易懂,是一篇很好的 Redis 入门文章。

在本 Redis 教程中,可以学习到如何安装、配置 Redis,以及使用其核心数据结构用命令从存储中写入、读取、更新、删除数据。

什么是 Redis?

Redis 是一款在内存中存储数据的 Key-Value 存储,可用来做数据库、缓存和消息代理等。它是 开源 的,目前使用 BSD 开源协议

有趣的事实:Redis 的本意是「REmote DIctionary Server.(远程字典服务器)」

Redis 响应时间在微妙级,每秒能处理百万次的请求,满足对性能有苛刻要求的实时应用,如游戏、广告代理、金融监控等等。

他支持基本的数据结构,如 string、list、set、sort set(有序集合:可范围查找) 和 hash。还支持更高级的数据结构,如 bitmaps(位图)、hyperloglogs(超级日志)、geospatial(地理空间索引:有半径查询)。

Alex StanciuAuth0 身份管理团队的产品负责人,解释我们用 Redis 的一个原因:

「我们使用 Redis 作为 Slack Bot 会话引擎的缓存层和Session 存储,因为它将数据存放在内存(RAM)中,提供了超快的读写速度;响应时间通常在 10 毫秒以下。」

在这个 Redis 教程中,我们将学习如何在自己系统中安装 Redis,学习如何使用其核心和最常用的数据结构在 Redis 存储数据。有了这个基础,以后的文章中,我们将学习如何使用 Redis 进行缓存,Session 存储、消息传递和实时分析。我们开始吧!

安装 Redis

第一件事情,我们需要先安装 Redis。如果你已经安装过了,你可以跳过这一节。

Redis 文档 建议使用编译源代码的方式来安装 Redis,因为 Redis 除了工作必要的 GCC 编译器libc,没有其他依赖项。我们也可以通过在 redis.io 下载最新的压缩包,或者使用始终指向最新稳定版的 URL:http://download.redis.io/redis-stable.tar.gz

Windows 用户:Redis 项目没有正式支持 Windows 系统。但是,如果你想在 Windows10 上运行,你可以 安装适用于 Linux 的 Windows子系统 来安装运行 Redis。当你已经安装、并运行适用于 Linux 的 Windows子系统了,请通过您的 Linux 命令行,跟着本教程下面针对 Linux(在指定时) 的步骤操作。

要编译 Redis,请根据以下简单步骤操作:

  • 创建一个 redis 文件夹,并切换到 redis 文件夹

macOS/Linux:

mkdir redis && cd redis
  • 获取最新 redis tar 包(tarball):

macOS/Linux:

curl -O http://download.redis.io/redis-stable.tar.gz
  • 解压 tar 包:

macOS/Linux:

tar xvzf redis-stable.tar.gz
  • 切换到解压后的 redis-stable 文件夹:

macOS/Linux:

cd redis-stable
  • 编译 Redis:

macOS/Linux:

make

如果您的系统中没有安装 make 包(则不能使用 make 命令),请按照 CLI(客户端应用)提供的说明安装。在 macOS 中,你可能需要下载 XCode 才能访问包含 make 命令和 C 编译器的命令行工具。在 Ubuntu 上安装很简单,例如,你可能希望运行以下命令来更新包管理器和安装核心包。

Ubuntu:

sudo apt update
sudo apt upgrade
sudo apt install build-essential
sudo apt-get install tcl8.5
make

需要 tcl 8.5 或更高版本来运行下一步的 Redis 测试。

  • 测试构建版本是否正常工作:

macOS/Linux:

make test

编译完成后,redis-stable 下的 src 文件夹会出现 Redis 的各种可执行文件。Redis 文档解释了 每个可执行文件的功能

  • redis-server: 运行 Redis Server 端本身。
  • redis-sentinel: 运行 Redis Sentinel, 一个用于监控和故障转移的工具。
  • redis-cli: 运行 命令行界面 客户端,用于跟 Redis 交互。
  • redis-benchmark: 检查 Redis 性能。
  • redis-check-aofredis-check-dump: 用于存在损坏的数据文件的罕见情况。

我们将频繁使用 redis-serverredis-cli 这两个可执行文件。为了方便起见,我们将它们复制到一个在系统范围内都能访问的位置,这可以通过以下命令来手动完成:

macOS/Linux:

sudo cp src/redis-server /usr/local/bin/
sudo cp src/redis-cli /usr/local/bin/

在将 redis-stable 作为当前的工作目录时,也可以通过运行以下命令来自动完成此操作:

macOS/Linux:

sudo make install

我们需要重新启动 shell,这些更改才能生效。一旦我们这样做了,我们就可以开始运行 Redis 了。

运行 Redis

启动 Redis

启动 Redis 服务器的最简单方法是运行 redis-server 命令。在一个新的 shell 窗口中,输入:

redis-server

如果一切正常,shell 将输出一个巨大的 ASCII Redis 标志,其中显示了 Redis 版本、运行模式、服务器运行的端口以及它的 PID(进程标识号)。

我们启动 Redis 时没有任何显式的配置文件;因此,我们将使用内部默认配置。这对于本文的范围来说是可以接受的:理解和使用基本的 Redis 数据结构。

作为第一步,我们总是需要让 Redis 服务器运行,因为 CLI 和其他服务依赖于它工作。

如何检查 Redis 是否工作

正如在 Redis 文档中提到的,外部程序使用 TCP 套接字和 Redis 特定的协议与 Redis 通信。Redis 协议是由 Redis 客户端库实现的,这些客户端库是用许多编程语言编写的,比如 JavaScript。但是我们不需要直接使用客户端库与 Redis 交互。我们可以使用 redis-cli 直接向其发送命令。要测试 Redis 是否正常工作,让我们发送 ping 命令。打开一个新的 shell 窗口并执行以下命令:

redis-cli ping

如果一切正常,shell 会返回 PONG

当我们发出 redis-cli ping 时,我们调用了后跟命令名为 pingredis-cli 可执行文件。命令名及其参数被发送到运行在 localhost: 6379上的 Redis 实例,以便对其进行处理并返回结果。

可以更改实例的主机和端口。使用 -- help 选项检查所有可以与 redis-cli 一起使用的命令:

redis-cli --help

如果我们在没有任何参数的情况下运行 redis-cli,程序将以交互模式启动。类似于 Python 等编程语言的 Read-Eval-Print Loop (REPL) ,我们可以在 shell 中键入不同的 Redis 命令,并从 Redis 实例获得返回结果。这些命令是什么,它们做什么是本文的核心学习目标!

让我们首先学习如何使用命令操作 Redis 中的数据!

在 Redis 中写入、读取、更新和删除数据

正如我们前面所了解的,Redis 是一个键值存储,它允许我们将一些称为 value(值)的数据与 key(键)关联起来。如果我们知道用于存储数据的*确切 *键,那么稍后可以用来检索存储的数据。

如果你还没有这样做,通过执行以下命令在交互模式下运行 Redis CLI:

redis-cli

当我们在 shell 提示符中看到 Redis 实例主机和端口时,我们就知道交互式 CLI 正在工作:

127.0.0.1:6379>

到了这一步,我们准备输入命令。

写入数据

要在 Redis 存储一个值,我们可以使用具有以下样式的 SET 命令

SET key value

在英语中,它读起来像 “set key to hold value”(设置键以保留值)。但值得注意的是,如果键已经对应了一个值, SET 将会覆盖原来的数据。

让我们来看一个例子,在交互式 shell 中输入:

SET service "auth0"

请注意,当您键入时,交互式 shell 会建议 Redis 命令的必需参数和可选参数。

Redis CLI showing syntax suggestions

按回车键发送命令。一旦 Redis 存储 “auth0” 作为 service 的值,它就会返回 OK,让我们知道一切都很顺利。Thank you, Redis!

读取数据

我们可以使用 GET 命令 向 Redis 获取键的值:

GET key

让我们来获取 service 的值:

GET service

Redis 返回 "auth0"

如果我们获取一个从未设置过的键的值会怎么样?

GET users

Redis 返回 (nil) ,以让我们知道键不存在于内存中。

在连接到数据库的经典 API 中,我们希望执行 CRUD 操作: 创建、读取、更新和删除。我们分别介绍了如何使用 SET 和 GET 命令在 Redis 中创建(写入)和读取数据。我们来看看剩下的。

更新数据

如前所述,我们可以通过覆盖键原来的数据来更新键的值。

让我们创建一个新的键值对:

SET framework angular

但是,我们改变了想法,现在我们想要设置的是 "react"。我们可以这样覆盖它:

SET framework react

Redis 设置对了吗?让我们来测试一下!

GET framework

Redis 确实返回 "react",我们有点犹豫不决,现在我们想设置 framework 键来对应 "vue" 值。

SET framework vue

如果我们再次运行 GET framework,就会得到 "vue" 。更新 / 覆盖的其他示例也无一例外。

删除数据

但是,我们现在不想设置任何 framework,我们需要删除这个键。我们该怎么做? 我们使用 DEL 命令

DEL key

让我们运行它:

DEL framework

Redis 返回 (integer) 1 ,以让我们知道被删除的键的数量。

仅使用三个命令:SETGETDEL,我们就能够遵守四个 CRUD 操作!

用引号包装字符串

注意一些奇怪的现象:我们不必在要存储的单个字符串值上加引号。SET framework angularSET framework "angular" 都被 Redis 接受作为一个操作来存储字符串"angular"作为键 framework 的值。

Redis 自动将单个字符串参数包装在引号中。因为键和值都是字符串,所以键名也是如此。我们可以使用 SET "framework" angular ,它也会工作得很好。但是,如果我们计划使用多个字符串作为键或值,我们必须要将字符串包装在引号中:

SET "the frameworks" "angular vue react"

返回 OK

SET the frameworks "angular vue react"

返回 (error) ERR syntax error

SET "the frameworks" angular vue react

还是返回 (error) ERR syntax error

最后,要获取该值,我们必须使用确切的键字符串:

GET "the frameworks"

返回 "angular vue react"

非破坏性写入

Redis 富有同情心,让我们可以小心地写入数据。假设我们想要创建一个键 services 对应值 "heroku aws" ,但是我们输入的不是 SET services "heroku aws",而是 SET service "heroku aws"。最后一个命令将毫不留情地覆盖 service 的当前值。所以,Redis 给我们提供了一个叫做 SETNXSET 的非破坏版本:

SETNX key value

当且仅当键不存在时,SETNX 才在内存中创建一个键(SET if Not eXists)。如果键已经存在,Redis 用 0 表示未能存储键值对,用 1 表示成功。让我们尝试一下前面的场景,但是使用 SETNX 代替 SET

SETNX service "heroku aws"

正如我们所期望的那样,返回 (integer) 0

SETNX services "heroku aws"

这一次,返回 (integer) 1。好极了!

我们可以使用 SETNX 来防止我们意外地覆盖数据。

键过期

当使用 Redis 创建密钥时,我们可以指定该密钥在内存中保存的时间。使用 EXPIRE 命令,我们可以设置一个键的过期时间,并在过期后自动删除该键:

让我们创建一个 30 秒后要删除的 notification 键:

SET notification "Anomaly detected"
EXPIRE notification 30

这将安排 Redis 在 30 秒后删除 notification 键。我们可以查看时钟,并在 30 秒后检查 notification 是否仍然可用 ,但我们不必这样做! Redis 提供 TTL 命令,告诉我们一个键在过期并被删除之前还剩多少秒:

TTL key

可能已经超过 30 秒了,所以让我们再试一次上面的例子,但是这次一执行 EXPIRE 就调用 TTL

SET notification "Anomaly detected"
EXPIRE notification 30
TTL notification

Redis 给我返回 (integer) 27 ,表示 notification 仍然可用 27 秒。让我再等一会儿,然后再运行 TTL

TTL

这一次,Redis 返回 (integer) -2。从 Redis 2.8 开始,TTL 返回:

  • 超时时间以秒为单位。
  • -2 代表 key 不存在(要么没有被创建,要么已经被删除)。
  • -1 代表 key 存在,但没有过期设置。

我确信 30 秒已经过去了,所以 -2 是预料中的。当键存在但是没有设置过期时间时,我们看一下错误消息:

SET dialog "Continue?"
TTL dialog

正如预期的那样,在没有设置过期时间时,Redis 返回 (integer) -1

需要注意的是,我们可以通过再次使用 SET 键来重置超时:

SET notification "Anomaly detected"
EXPIRE notification 30
TTL notification
// (integer) 27
SET notification "No anomaly detected"
TTL notification
// (integer) -1

我们在前面了解到,使用 SET 与再次创建键是一样的,对于 Redis 来说,这还涉及到重置当前分配给它的任何过期时间。

我们现在在 Redis 操作数据方面有了坚实的基础。有了这些知识,我们现在可以开始探索 Redis 提供的数据类型了。

Redis 数据类型

Redis 远不是一个普通的键值存储,而是一个实际的 数据结构服务器,它支持不同类型的值。传统上,键值存储允许我们将字符串键映射到字符串值,而不允许其他任何操作。在 Redis,字符串键可以映射到不仅仅是一个简单的字符串。

作为一个数据结构服务器,我们也可以将数据类型称为数据结构。我们可以使用这些更加复杂的数据结构来同时在一个键中存储多个值。让我们从更高的层次来看一下这些类型。我们将在随后的章节中详细探讨每一种类型。

  • Binary-safe Strings 二进制安全字符串

Redis 值的最基本类型。“二进制安全” 意味着字符串可以包含能表示为字符串的任何类型的数据:例如 PNG 图像或序列化对象。

  • Lists 列表

本质上,Redis Lists 是链表。它们是字符串元素的集合,这些元素根据 插入的顺序 进行排序。

  • Sets 集合

它们表示唯一的和未排序的字符串元素的集合。

  • Sorted Sets 有序集合

与 Sets 类似,它们表示一组唯一的字符串元素;但是,每个字符串元素都链接到一个浮点数值,称为元素 score(分数)。在查询 Sorted Set 时,元素总是根据它们的 score 进行排序,这使我们能够始终如一地查找到 Set 中的一系列数据。

  • Hashes 哈希

这些哈希(映射)由链接到字符串值的字符串字段组成。

  • Bit arrays 位数组

也称为位图。它们让我们像处理位数组一样处理字符串值。

  • HyperLogLogs 超级日志

一种用于估计集合 基数 的概率数据结构,它是对 “集合中元素的数量” 的度量

我们已经在 ”在 Redis 中写入、读取、更新和删除数据“ 部分讨论了字符串。对于本教程的其余部分,我们将集中讨论除位图和超级日志以外的所有 Redis 类型。我们将在以后的文章中访问那些处理高级 Redis 用例的文章。

Lists 列表

List 是有序元素的序列。例如,1 2 4 5 6 90 19 3 是一个数字列表。在 Redis,重要的是要注意列表是以 链表 的形式实现的。这对性能有一些重要的影响。向 List 的头部和尾部添加元素的速度很快,但在 List 中搜索元素的速度较慢,因为我们没法对元素进行索引访问(就像我们在数组中所做的那样)。

List 是通过使用 Redis 命令创建的,在键名后跟 push 数据。我们可以使用两个命令:RPUSHLPUSH。如果键不存在,这些命令将返回一个新的 List,并将传递的参数作为元素。如果键已经存在或者不是 List,则返回一个错误。

RPUSH

RPUSH 在 List 的末尾(尾部)插入一个新元素:

RPUSH key value [value ...]

让我们创建一个表示 List 的 engineers 键:

RPUSH engineers "Alice"
// 1
RPUSH engineers "Bob"
// 2
RPUSH engineers "Carmen"
// 3

每次插入元素时,Redis 都会在插入之后返回 List 的长度。我们希望 users 列表像这样:

Alice Bob Carmen

我们如何验证? 我们可以使用 LRANGE 命令。

LRANGE

LRANGE 基于指定的开始和停止索引返回 List 的一个子集。尽管这些索引是从零开始的,但它们不同于数组索引。给定一个完整的 List,它们只是指出 List 的分区位置:从这里(开始)到这里(停止)创建一个 slice:

LRANGE key start stop

要查看完整的 List,我们可以使用一个简洁的技巧:从 0 到它前面的元素 -1

LRANGE engineers 0 -1

Redis 返回:

1) "Alice"
2) "Bob"
3) "Carmen"

Index -1 将始终表示 List 中的最后一个元素。

为了得到 engineers 的前两个元素,我们可以用以下命令:

LRANGE engineers 0 1

LPUSH

LPUSH 的作用与 RPUSH 相同,只是它将元素插入到 List 的前面(在头部):

LPUSH key value [value ...]

让我们把 Daniel 放在 engineers 列表的前面:

LPUSH engineers "Daniel"
// 4

我们现在有四名工程师,让我们验证一下 list 是否正确:

LRANGE engineers 0 -1

返回如下:

1) "Daniel"
2) "Alice"
3) "Bob"
4) "Carmen"

这和我们之前的 List 是一样的,第一个元素是 “Daniel” ,这正是我们所期望的。

多元素插入

我们在 RPUSHLPUSH 的命令提示中看到,我们可以在每个命令中插入多个元素。让我们看看它的实际效果。

基于我们现有的 engineers 列表,让我们运行以下命令:

RPUSH engineers "Eve" "Francis" "Gary"
// 7

因为我们将它们插入到 List 的末尾,所以我们期望这三个新元素按照它们作为参数列出的相同顺序显示。让我们来验证一下:

LRANGE engineers 0 -1

Redis 返回:

1) "Daniel"
2) "Alice"
3) "Bob"
4) "Carmen"
5) "Eve"
6) "Francis"
7) "Gary"

如果我们对 LPUSH 也这样做呢:

LPUSH engineers "Hugo" "Ivan" "Jess"
// 10

让我们看看:

LRANGE 0 -1

返回:

 1) "Jess"
2) "Ivan"
3) "Hugo"
4) "Daniel"
5) "Alice"
6) "Bob"
7) "Carmen"
8) "Eve"
9) "Francis"
10) "Gary"

当列出 LPUSHRPUSH 的多个参数时,Redis 逐个地插入元素,因此,“Hugo”“Ivan”“Jess” 以相反的顺序出现,它们作为参数列出。

LLEN

我们可以使用 LLEN 命令随时查看 List 的长度:

LLEN key

让我们验证一下 engineers 的长度确实是 10

LLEN engineers

Redis 返回 (integer) 10。完美。

从 Redis 列表中删除元素

类似于我们可以 ”弹出“ 数组中的元素,我们可以从 Redis List 的头部或尾部弹出一个元素。

LPOP 删除并返回 List 的第一个元素:

LPOP key

我们可以使用它从 List 中删除 “Jess” ,即第一个元素:

LPOP engineers

Redis 确实返回 “Jess”,以表明它是被删除的元素。

RPOP 删除并返回 List 的最后一个元素:

RPOP key

是时候和 “Gary” 说再见了,这是 List 的最后一个元素:

RPOP engineers

Redis 返回 “Gary”

能够获得从 List 中删除的元素是非常有用的,因为我们可能想对它做一些特殊的处理。

Redis Lists 是以链表的形式实现的,因为它的开发团队设想,对于一个数据库系统来说,能够以非常快的方式将元素添加到一个非常长的列表中是至关重要的

Sets 集合

在 Redis 中,Set 类似于 List,只是它不为元素保持任何特定的顺序,并且每个元素必须是唯一的。

SADD

我们使用 SADD 命令创建一个 Set,该命令将指定的成员添加到键中:

SADD key member [member ...]

已经是集合一部分的指定成员将被忽略。如果键不存在,则创建一个新的 Set,并添加唯一指定的成员。如果键已经存在或者不是 Set,则返回一个错误。

让我们创建一个 languages 集合。

SADD languages "english"
// 1
SADD languages "spanish"
// 1
SADD languages "french"
// 1

在这种情况下,对于每个成员添加,Redis 返回用 SADD 命令添加的成员数,而不是 Set 的大小。让我们来看看实际的情况:

SADD languages "chinese" "japanese" "german"
// 3
SADD languages "english"
// 0

第一个命令返回 3,因为我们在集合中添加了三个唯一的成员。第二个命令返回 0,因为 “english” 已经是 Set 的成员。

SREM

我们可以使用 SREM (set remove) 命令从 Set 中删除成员:

SREM key member [member ...]

我们可以同时删除一个或多个成员:

SREM languages "english" "french"
// 2
SREM languages "german"
// 0

SREM 返回被删除的成员数。

SISMEMBER

要验证某个成员是否是 Set 的一部分,我们可以使用 SISMEMBER(Set Is Member) 命令:

SISMEMBER key member

如果元素是 Set 的一部分,则该命令返回 1;否则,它返回 0

SISMEMBER languages "spanish"
// 1
SISMEMBER languages "german"
// 0

由于我们上一节中去掉了 "german" ,所以返回 0

SMEMBERS

要显示 Set 中存在的所有成员,我们可以使用 SMEMBERS 命令:

SMEMBERS key

让我们看看我们目前在 languages 集合中有哪些语言值:

SMEMBERS languages

返回:

1) "chinese"
2) "japanese"
3) "spanish"

由于集合不排序,Redis 可以在每次调用时以任何顺序返回元素。他们不能保证成员排序。

SUNION

我们可以非常快速地使用 set 来实现一个非常强大的功能,那就是使用 SUNION 命令将它们组合起来:

SUNION key [key ...]

SUNION 的每个参数代表一个集合,我们可以合并到一个更大的集合中。重要的是要注意,任何重复的成员将只被被列出一次。

为了看到这一点,我们首先创建一个 ancient-languages 集:

SADD ancient-languages "greek"
SADD ancient-languages "latin"
SMEMBERS ancient-languages

现在,让我们创建一个 languagesancient-languages 的结合体,一次看到所有的语言:

SUNION languages ancient-languages

我们得到以下:

1) "greek"
2) "spanish"
3) "japanese"
4) "chinese"
5) "latin"

如果我们将一个不存在的键传递给 SUNION,它将认为该键是一个空集(一个没有任何内容的集合)。

Hashes 哈希

在 Redis,Hash 是一种数据结构,它将字符串键映射为 field-value 对(字段-值对)。因此,哈希表示对象非常有用。它们的 是 Hash 的名称, 表示 field-name field-value 的序列。我们可以这样描述一个 computer 对象:

computer name "MacBook Pro" year 2015 disk 512 ram 16

对象的 ”属性“ 定义为对象(computer)名称后面的 ”属性名“ 和 ”属性值“ 序列。回想一下,Redis 完全是关于顺序字符串的,所以我们在创建这些字符串对象时必须非常小心,我们使用正确的字符串顺序来正确地定义对象。

为了操作哈希,我们使用与字符串类似的命令,毕竟它们是字符串。

写入和读取哈希数据

HSET

命令 HSET 将 Hash 中的 field (字段) 设置为 value。如果键不存在,则创建一个存储哈希键值对的新键。如果字段已经存在于哈希表中,则会覆盖该字段。

HSET key field value

让我们创建 computer 对象:

HSET computer name "MacBook Pro"
// 1
HSET computer year 2015
// 1
HSET computer disk 512
// 1
HSET computer ram 16
// 1

对于每个 HSET 命令,Redis 返回一个整数,如下:

  • 1,如果 field 是哈希表中的一个新字段,并设置了值。
  • 0,如果 field 已经存在于哈希表中,并且该值已被更新。

让我们将字段 year 的值更新为 2018

HSET computer year 2018
// 0

HGET

HGET 返回 Hash 中与字段关联的值:

HGET key field

让我们验证一下,我们把字段 year 的值由 2015 变成了 2018

HGET computer year

Redis 返回 2018,结果没问题。

HGETALL

从哈希表中获取所有字段及其值的一个快速方法是使用 HGETALL

HGETALL key

让我们来测试一下:

HGETALL computer

返回:

1) "name"
2) "MacBook Pro"
3) "year"
4) "2018"
5) "disk"
6) "512"
7) "ram"
8) "16"

当提供的键参数不存在时,HGETALL 返回一个空列表。

HMSET

我们也可以使用 HMSET 一次设置多个字段:

HMSET key field value [field value ...]

让我们用它来创建一个 tablet 哈希表:

HMSET tablet name "iPad" year 2016 disk 64 ram 4

HMSET 返回 OK,告诉我们已经成功创建了 tablet 哈希表。

HMGET

如果我们只想获取两个字段呢? 我们使用 HMGET 来指定我们希望从哈希表中哪些字段获得值:

HMGET key field [field ...]

让我们获得 tabletdiskram

HMGET tablet disk ram

返回 diskram 的值:

1) "64"
2) "4"

这差不多就是在 Redis 使用哈希表的要点。您可以查看 哈希命令的完整列表 并尝试使用它们。

Sorted Sets 有序集合

在 Redis 1.2 中引入的一个 Sorted Set 实质上是一个 Set:它包含 唯一的、不重复的字符串成员。然而,尽管 Set 的成员是无序的(Redis 可以在 Set 的 每次调用中以任何顺序返回元素) ,但是 Sorted Set 的每个成员都链接到一个称为 score(分数) 的浮点值,Redis 使用这个值来确定 Sorted Set 成员的顺序。因为 Sorted Set 的每个元素都映射到一个值,所以它也有一个类似于 Hash 的体系结构。

在 Redis,一个有序集合可以被看作是一个集合和一个散列的混合。

如何确定有序集合成员的顺序? 正如 Redis 文档 中所述:

  • 如果 A 和 B 是两个分数不同的成员,那么如果 A > B,那么 A.score > B.score。
  • 如果 A 和 B 的分数完全相同,那么如果 A 字符串在字母顺序上大于 B 字符串,那么 A > B。A 和 B 字符串不能相等,因为有序集合只有唯一的元素。

我们用来与 Sorted Sets 交互的一些命令类似于我们用来与 Sets 交互的命令:我们将 Set 命令中的 S 替换为 Z,例如,SADD => ZADD。然而,我们的命令对于两者都是独一无二的。我们来看看。

ZADD

使用 ZADD 将具有指定分数的所有指定成员添加到 Sorted Set:

ZADD key [NX|XX] [CH] [INCR] score member [score member ...]

与集合一样,如果键不存在,则创建具有指定成员作为唯一成员的新 “有序集合”。如果键存在但不是有序集合,则返回一个错误。

从 Redis 3.0.2开始,ZADD 提供了可选参数,可以控制插入:

  • XX:只更新已存在的成员。不添加成员。
  • NX:不要更新已经存在的成员。总是添加新成员。
  • CH: 将返回值从添加的新成员数目修改为更改的成员总数(CH 是更改的缩写)。更改的成员是新添加的成员和已经存在的成员,该成员的分数已经更新。因此,在命令行中指定的与过去分数相同的成员不计算在内。
  • INCR:当指定此选项时,ZADD 的作用类似于 ZINCRBY。在此模式中只能指定一个成员对。

很高兴知道这些可选参数的存在以及它们的作用,但是在本教程中,我们将专注于添加成员而不使用其中任何一个,但是请随意探索它们! 在以后的文章中,我们将在更复杂的用例中重新讨论它们!

让我们创建一个存储服务台支持票据 tickets的有序集合。支持票据是唯一的,但也需要排序,因此,这种数据结构是一个很好的选择:

ZADD tickets 100 HELP204
// 1
ZADD tickets 90 HELP004
// 1
ZADD tickets 180 HELP330
// 1

ZADD 返回新添加元素的数量计数。在上面的命令中,我们使用票据在队列中的位置作为分数,后跟票号(都是虚构的)。

ZRANGE

我们现在想看看我们的有序集合看起来如何。对于集合,我们使用 SMEMBERS 列出无序成员。使用 Sorted Sets 时,我们使用的命令更符合使用 Lists 时使用的命令,这个命令向我们显示一个元素范围。

ZRANGE 返回在 Sorted Set 中指定的成员范围:

ZRANGE key start stop [WITHSCORES]

它的行为非常类似于 Lists 列表的 LRANGE。我们可以使用它来获取有序集合的一个子集。要获得完整的有序集合,我们可以再次使用 0 -1 范围:

ZRANGE tickets 0 -1

返回如下:

1) "HELP004"
2) "HELP204"
3) "HELP330"

我们可以通过 ZRANGEWITHSCORES 参数来包含每个成员的分数:

ZRANGE tickets 0 -1 WITHSCORES

返回:

1) "HELP004"
2) "90"
3) "HELP204"
4) "100"
5) "HELP330"
6) "180"

注意成员和分数是如何按顺序排列的,而不是挨着排列的。正如我们所看到的,成员根据他们的分数按升序存储在 tickets 中。

使用 Redis 作为 Session(会话)存储

在 web 应用程序的身份验证和授权工作流中,Redis 最相关的用途是充当会话存储。

正如 Amazon Web Services 所承认的那样,Redis 的内存架构为开发人员提供了高可用性和持久性,使其成为网络规模应用程序存储和管理会话数据的流行选择。它闪电般的性能为我们提供了超低延迟、最佳规模和弹性,这些是我们管理会话数据(如用户配置文件、用户设置、会话状态和凭证管理)所需的。

Redis 实验室 Roshan Kumar 在他的 “Cache vs. Session Store” 文章中解释说,面向会话的 web 应用程序在用户登录时启动会话。会话处于活动状态,直到用户注销或会话超时为止。在会话生命周期中,web 应用程序将所有与会话相关的数据存储在主存(RAM)或会话存储中,当应用程序宕机时,这些数据不会丢失。这个会话存储区可以使用 Redis 实现,尽管它是一个内存存储区,但它能够通过在磁盘中顺序写入事务日志来持久保存数据。

A session-oriented application diagram

图片来源:Redis Labs: Cache vs. Session Store

Roshan 进一步解释说,会话存储依赖于读写数据的内存数据库。会话存储数据不是临时的,当会话处于活动状态时,它成为唯一的真实数据源。出于这个原因,会话存储需要满足 ”真正数据库的数据持久性需求“。

“根据 @redislabs 的说法,会话存储需要高可用性和持久性来支持事务数据和不间断的用户参与。使用 redis 就可以轻松做到这一点。”

总结

Redis 是一个强大、灵活和灵活的数据库,可以加快您的体系结构。它可以提供很多服务,包括缓存、数据复制、发布 / 订阅消息系统、会话存储等等。Redis 有大量的客户端,覆盖了所有流行的编程语言。我希望每当您有一个符合其价值主张的用例时,您都可以尝试一下。


我觉得对于初学者来说,找到学习的入口非常重要。不然就会望而却步,感觉高不可攀。但一旦打开了这扇门,后面的学习相对来说好开展一些。这也是我翻译这篇文章的初衷。

为了更好的理解与使用 Redis,强烈建议大家看一看《Redis 开发与运维》《Redis 设计与实现》这两本书。

ps:阅读英语文档时,可以借助 彩云小译 双语对照网页翻译插件。

Depp Wang wechat
个人公众号