“纸上得来终觉浅,绝知此事要躬行。” ——陆游《冬夜读书示子聿》

学习 Redis 最好的办法就是动手尝试它。在介绍 Redis 最核心的内容之前,本章先来介绍一下如何安装和运行 Redis,以及 Redis 的基础知识,使读者可以在之后的章节中一边学习一边实践。

2.1 安装 Redis

安装 Redis 是开始 Redis 学习之旅的第一步。在安装 Redis 前需要了解 Redis 的版本规则以选择最适合自己的版本,Redis 约定次版本号(即第一个小数点后的数字)为偶数的版本是稳定版(如2.8版、3.0版),奇数版本是非稳定版(如2.7版、2.9版),生产环境下一般需要使用稳定版本。本书的内容以 3.0 版为目标编写,同时绝大部分内容也适用于2.6版和2.8版。对于只在最新版才有的特性(如Cluster集群),本书会做特别说明。

2.1.1 在 POSIX 系统中安装

Redis 兼容大部分POSIX系统,包括 Linux、OS X 和 BSD 等,在这些系统中推荐直接下载 Redis 源代码编译安装以获得最新的稳定版本。Redis 最新稳定版本的源代码可以从地址 https://redis.io/download 下载。
下载安装包后解压即可使用 make 命令完成编译,完整的命令如下(以5.0.5版本为例):

  1. wget http://download.redis.io/releases/redis-5.0.5.tar.gz
  2. tar xzf redis-5.0.5.tar.gz
  3. cd redis-5.0.5.tar.gz
  4. make

Redis 没有其他外部依赖,安装过程很简单。编译后在 Redis 源代码目录的 src 文件夹中可以找到若干个可执行程序,最好在编译后直接执行 make install 命令来将这些可执行程序复制到 /usr/local/bin 目录中以便以后执行程序时可以不用输入完整的路径。
在实际运行 Redis 前推荐使用 make test 命令测试 Redis 是否编译正确,尤其是在编译一个不稳定版本的 Redis 时。

提示:除了手工编译外,还可以使用操作系统中的软件包管理器来安装 Redis,但目前大多数软件包管理器中的 Redis的版本都较古老。考虑到 Redis 的每次升级都提供了对以往版本的问题修复和性能提升,使用最新版本的 Redis 往往可以提供更加稳定的体验。如果希望享受包管理器带来的便利,在安装前请确认您使用的软件包管理器中 Redis 的版本并了解该版本与最新版之间的差异。http://redis.io/topics/problems 中列举了一些在以往版本中存在的已知 问题。

2.1.2 在 OS X 系统中安装

OS X 下的软件包管理工具 Homebrew 和 MacPorts 均提供了较新版本的 Redis 包,所以我们可以直接使用它们来安装 Redis,省去了像其他 POSIX 系统那样需要手动编译的麻烦。下面以使用 Homwbrew 安装 Redis 为例。

  1. 安装 Homebrew

在终端下输入下列命令即可安装 Homebrew。

  1. ruby -e "$(curl -fsSkL raw.github.com/mxcl/homebrew/go)"

如果之前安装过 Homebrew,请执行 brew update 来更新 Homebrew,以便安装较新版的 Redis。

  1. 通过 Homebrew 安装 Redis

使用 brew install 软件包名可以安装相应的包,此处执行 brew install redis来安装 Redis:

  1. $ brew install redis
  2. ==> Downloading https://downloads.sf.net/project/machomebrew/Bottles/redis-3.0.0.yosemite.bottle.tar.gz ########################################################################
  3. 100.0%
  4. ==> Pouring redis-3.0.0.yosemite.bottle.tar.gz ==> Caveats
  5. To have launchd start redis at login:
  6. ln -sfv /usr/local/opt/redis/*.plist ~/Library /LaunchAgents Then to load redis now:
  7. launchctl load ~/Library/LaunchAgents/homebrew.mxcl.redis.plist Or, if you don't want/need launchctl, you can just run:
  8. redis-server /usr/local/etc/redis.conf ==> Summary
  9. /usr/local/Cellar/redis/3.0.0: 10 files, 1.4M

OS X 系统从 Tiger 版本开始引入了 launchd 工具来管理后台程序,如果想让 Redis 随系统自动运行可以通过以下命令配置 launchd:

  1. ln -sfv /usr/local/opt/redis/*.plist ~/Library /LaunchAgents
  2. launchctl load ~/Library/LaunchAgents/homebrew.mxcl.redis.plist

通过 launchd 运行的 Redis 会加载位于 /usr/local/etc/redis.conf 的配置文件,关于配置文件会在2.4节中介绍。

2.1.3 在 Windows 中安装

Redis 官方不支持 Windows。2011年微软[1] 向 Redis 提交了一个补丁,以使 Redis 可以在 Windows 下编译运行,但被 Salvatore Sanfilippo 拒绝了,原因是在服务器领域上 Linux 已经得到了广泛的使用,让 Redis 能在 Windows 下运行相比而言显得不那么重要。并且 Redis 使用了如写时复制等很多操作系统相关的特性,兼容 Windows 会耗费太大的精力而影响 Redis 其他功能的开发。尽管如此微软还是发布了一个可以在 Windows 运行的 Redis 分支[2] ,而且更新相当频繁,截止到本书交稿时,Windows 下的 Redis 版本为2.8。
如果想使用 Windows 学习或测试 Redis 可以通过 Cygwin 软件或虚拟机(如VirtualBox) 来完成。Cygwin 能够在 Windows中 模拟 Linux 系统环境。Cygwin 实现了一个 Linux API 接口,使得大部分 Linux 下的软件可以重新编译后在 Windows 下运行。Cygwin 还提供了自己的软件包管理工具,让用户能够方便地安装和升级几千个软件包。借助 Cygwin,我们可以在 Windows 上通过源代码编译安装最新版的 Redis。

  1. 安装 Cygwin

从 Cygwin官方网站(http://cy gwin.com)下载 setup.exe 程序,setup.exe 既是 Cygwin 的安 装包,又是 Cygwin 的软件包管理器。运行 setup.exe 后进入安装向导。前几步会要求选择下载源、安装路径、代理和下载镜像等,可以根据具体需求选择,一般来说一路点击“Next”即可。之后会出现软件包管理界面,如图2-1所示。
image.png
图2-1 Cygwin 包管理界面

编译安装 Redis 需要用到的包有 gcc 和 make,二者都可以在“Devel”分类中找到。
在“New”字段中标记为“Skip”的包表示不安装 ,单击“Skip”切换成需要安装的版本号即可令
Cygwin 在稍后安装该版本的包。图2-1中所示 gcc 包的状态为“Keep”是因为作者之前已经安装过该包了,同样如果读者在退出安装向导后还想安装其他软件包,只需要重新运行 setup.exe 程序再次进入此界面即可。
为了方便使用,我们还可以安装 wget(用于下载Redis源代码,也可以手动下载并使用 Windows 资源管理器将其复制到 Cygwin 对应的目录中,见下文介绍)和 vim(用于修改Redis 的源代码使之可以在 Cygwin 下正常编译)。
之后单击“Next”,安装向导就会自动完成下载和安装工作了。
安装成功后打开 Cygwin Terminal 程序即可进入 Cygwin 环境,Cygwin 会将 Windows 中的目录映射到 Cygwin 中。如果安装时没有更改安装目录,Cygwin 环境中的根目录对应的 Windows 中的目录是 C:\cygwin。

  1. 修改 Redis 源代码

下载和解压 Redis 的过程和2.1.1节中介绍的一样,不过在 make 之前还需要修改 Redis 的源代码以使其可以在 Cygwin 下正常编译。
首先编辑 src 目录下的 redis.h 文件,在头部加入:

  1. #ifdef CYGWIN
  2. #ifndef SA ONSTACK
  3. #define SA ONSTACK 0x08000000
  4. #endif
  5. #endif

而后编辑 src 目录下的 object.c 文件,在头部加入:

  1. #define strtold(a,b) ((long double)strtod((a),(b)))
  1. 编译 Redis

同2.1.1节一样,执行 make 命令即可完成编译。

注意:Cygwin 环境无法完全模拟 Linux 系统,比如 Cygwin 的 fork 不支持写时复制;另外,Redis 官方也并不提供对 Cygwin 的支持,Cygwin 环境只能用于学习 Redis。运行 Redis 的最佳系统是 Linux 和 OS X,官方推荐的生产系统是 Linux。

2.2 启动和停止 Redis

安装完 Redis 后的下一步就是启动它,本节将分别介绍在开发环境和生产环境中运行 Redis 的方法以及正确停止 Redis 的步骤。
在这之前首先需要了解 Redis 包含的可执行文件都有哪些,表 2-1 中列出了这些程序的名称以及对应的说明。如果在编译后执行了 make install 命令,这些程序会被复制到 /usr/local/bin 目录内,所以在命令行中直接输入程序名称即可执行。
表2-1 Redis可执行文件说明
image.png
我们最常使用的两个程序是 redis-server 和 redis-cli,其中 redis-server 是 Redis 的服务器, 启动 Redis 即运行 redis-server;而 redis-cli 是 Redis 自带的 Redis 命令行客户端,是学习 Redis 的重要工具,2.3节会详细介绍它。

2.2.1 启动 Redis

启动 Redis 有直接启动和通过初始化脚本启动两种方式,分别适用于开发环境和生产环境。

  1. 直接启动

直接运行 redis-server 即可启动 Redis,十分简单:

  1. $ redis-server
  2. [5101] 14 Dec 20:58:59.944 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
  3. [5101] 14 Dec 20:58:59.948 * Max number of open files set to 10032
  4. ...
  5. [5101] 14 Dec 20:58:59.949 # Server started, Redis version 2.6.9
  6. [5101] 14 Dec 20:58:59.949 * The server is now ready to accept connections on port 6379

Redis服务器默认会使用6379端口[3] ,通过 —port 参数可以自定义端口号:

  1. $ redis-server --port 6380
  1. 通过初始化脚本启动 Redis

在Linux系统中可以通过初始化脚本启动 Redis,使得 Redis 能随系统自动运行,在生产环境中推荐使用此方法运行 Redis,这里以 Ubuntu 和 Debian发行版为例进行介绍。在 Redis 源代码目录的 utils 文件夹中有一个名为 redis_init_script 的初始化脚本文件,内容如下:

  1. #!/bin/sh
  2. #
  3. # Simple Redis init.d script conceived to work on Linux systems
  4. # as it does use of the /proc filesystem.
  5. ### BEGIN INIT INFO
  6. # Provides: redis_6379
  7. # Default-Start: 2 3 4 5
  8. # Default-Stop: 0 1 6
  9. # Short-Description: Redis data structure server
  10. # Description: Redis data structure server. See https://redis.io
  11. ### END INIT INFO
  12. REDISPORT=6379
  13. EXEC=/usr/local/bin/redis-server
  14. CLIEXEC=/usr/local/bin/redis-cli
  15. PIDFILE=/var/run/redis_${REDISPORT}.pid
  16. CONF="/etc/redis/${REDISPORT}.conf"
  17. case "$1" in
  18. start)
  19. if [ -f $PIDFILE ]
  20. then
  21. echo "$PIDFILE exists, process is already running or crashed"
  22. else
  23. echo "Starting Redis server..."
  24. $EXEC $CONF
  25. fi
  26. ;;
  27. stop)
  28. if [ ! -f $PIDFILE ]
  29. then
  30. echo "$PIDFILE does not exist, process is not running"
  31. else
  32. PID=$(cat $PIDFILE)
  33. echo "Stopping ..."
  34. $CLIEXEC -p $REDISPORT shutdown
  35. while [ -x /proc/${PID} ]
  36. do
  37. echo "Waiting for Redis to shutdown ..."
  38. sleep 1
  39. done
  40. echo "Redis stopped"
  41. fi
  42. ;;
  43. *)
  44. echo "Please use start or stop as first argument"
  45. ;;
  46. esac

我们需要配置 Redis 的运行方式和持久化文件、日志文件的存储位置等,具体步骤如下。
(1)配置初始化脚本。首先将初始化脚本复制到 /etc/init.d 目录中,文件名为 redis_端口 号,其中端口号表示要让 Redis监听的端口号,客户端通过该端口连接 Redis。然后修改脚本第 14 行 的 REDISPORT 变量的值为同样的端口号。
(2)建立需要的文件夹。建立表2-2中列出的目录。

表2-2 需要建立的目录及说明
image.png
(3)修改配置文件。首先将配置文件模板(见 2.4 节介绍)复制到 /etc/redis 目录中,以端口号命名(如“6379.conf”),然后按照表2-3对其中的部分参数进行编辑。

表2-3 需要修改的配置及说明
image.png
现在就可以使用 /etc/init.d/redis_端口号 start 来启动 Redis 了,而后需要执行下面的命令使 Redis 随系统自动启动:

  1. $ sudo update-rc.d redis_端口号 defaults

2.2.2 停止 Redis

考虑到 Redis 有可能正在将内存中的数据同步到硬盘中,强行终止 Redis 进程可能会导致数据丢失。正确停止 Redis 的方式应该是向 Redis 发送 SHUTDOWN 命令,方法为:

  1. $ redis-cli SHUTDOWN

当 Redis 收到 SHUTDOWN 命令后,会先断开所有客户端连接,然后根据配置执行持久化,最后完成退出。
Redis可以妥善处理 SIGTERM 信号,所以使用 kill Redis 进程的 PID 也可以正常结束 Redis,效果与发送 SHUTDOWN 命令一样。

2.3 Redis 命令行客户端

还记得我们刚才编译出来的 redis-cli 程序吗? redis-cli(Redis Command Line Interface) 是 Redis 自带的基于命令行的 Redis 客户端,也是我们学习和测试 Redis 的重要工具,本书后面会使用它来讲解 Redis 各种命令的用法。
本节将会介绍如何通过 redis-cli 向Redis发送命令,并且对 Redis 命令返回值的不同类型进行简单介绍。

2.3.1 发送命令

通过 redis-cli 向 Redis 发送命令有两种方式,第一种方式是将命令作为 redis-cli 的参数执行,比如在2.2.2节中用过的 redis-cli SHUTDOWN。redis-cli 执行时会自动按照默认配置(服务器地址为127.0.0.1,端口号为6379)连接 Redis,通过 -h 和 -p 参数可以自定义地址和端口号:

  1. $ redis-cli -h 127.0.0.1 -p 6379

Redis 提供了 PING 命令来测试客户端与 Redis 的连接是否正常,如果连接正常会收到回复 PONG。如:

  1. $ redis-cli PING
  2. PONG

第二种方式是不附带参数运行 redis-cli,这样会进入交互模式,可以自由输入命令,例如:

  1. $ redis-cli
  2. redis 127.0.0.1:6379> PING
  3. PONG
  4. redis 127.0.0.1:6379> ECHO hi
  5. "hi"

这种方式在要输入多条命令时比较方便,也是本书中主要采用的方式。为了简便起见,后文中我们将用
redis> 表示 redis 127.0.0.1:6379>。

2.3.2 命令返回值

在大多数情况下,执行一条命令后我们往往会关心命令的返回值,如 1.2.4 节中的 HGET 命令的返回值就是我们需要的指定键的 title 字段的值。命令的返回值有5种类型,对于每种类型 redis-cli 的展现结果都不同,下面分别说明。

  1. 状态回复

状态回复(status reply)是最简单的一种回复,比如向 Redis 发送 SET 命令设置某个键的值时,Redis 会回复状态 OK 表示设置成功。另外之前演示的对 PING 命令的回复 PONG 也是状态回复。状态回复直接显示状态信息,如:

  1. redis> PING
  2. PONG
  1. 错误回复

当出现命令不存在或命令格式有错误等情况时 Redis 会返回错误回复(error reply)。错误回复以(error)开头,并在后面跟上错误信息。如执行一个不存在的命令:

  1. redis> ERRORCOMMEND
  2. (error) ERR unknown command 'ERRORCOMMEND'

在2.6版本时,错误信息均是以“ERR”开头,而在2.8版以后,部分错误信息会以具体的错误类型开头,如:

  1. redis> LPUSH key 1
  2. (integer) 1
  3. redis> GET key
  4. (error) WRONGTYPE Operation against a key holding the wrong kind of value

这里错误信息开头的“WRONGTYPE”就表示类型错误,这个改进使得在调试时能更容易地知道遇到的是哪种类型的错误。

  1. 整数回复

Redis 虽然没有整数类型,但是却提供了一些用于整数操作的命令,如递增键值的 INCR 命令会以整数形式返回递增后的键值。除此之外,一些其他命令也会返回整数,如可以获取当前数据库中键的数量的 DBSIZE 命令等。整数回复(integer reply)以(integer)开头,并在后面跟上整数数据:

  1. redis> INCR foo
  2. (integer) 1
  1. 字符串回复

字符串回复(bulkreply)是最常见的一种回复类型,当请求一个字符串类型键的键值或一个其他类型键中的某个元素时就会得到一个字符串回复。字符串回复以双引号包裹:

  1. redis> GET foo
  2. "1"

特殊情况是当请求的键值不存在时会得到一个空结果,显示为(nil)。如:

  1. redis> GET noexists
  2. (nil)
  1. 多行字符串回复

多行字符串回复(multi-bulkreply)同样很常见,如当请求一个非字符串类型键的元素列表时就会收到多行字符串回复。多行字符串回复中的每行字符串都以一个序号开头,如:

  1. redis> KEYS *
  2. 1) "bar"
  3. 2) "foo"

提示 KEYS命令的作用是获取数据库中符合指定规则的键名,由于读者的Redis中还没有存储数据,所以得到的返回值应该是(empty list or set)。3.1 节会具体介绍KEYS命令,此处读者只需了解多行字符串回复的格式即可。

2.4 配置

2.2.1 节中我们通过 redis-server 的启动参数 port 设置了 Redis 的端口号,除此之外 Redis 还支持其他配置选项,如是否开启持久化、日志级别等。由于可以配置的选项较多,通过启动参数设置这些选项并不方便,所以 Redis 支持通过配置文件来设置这些选项。启用配置文件的方法是在启动时将配置文件的路径作为启动参数传递给redis-server,如:

  1. $ redis-server /path/to/redis.conf

通过启动参数传递同名的配置选项会覆盖配置文件中相应的参数,就像这样:

  1. $ redis-server /path/to/redis.conf --loglevel warning

Redis提供了一个配置文件的模板 redis.conf,位于源代码目录的根目录中。 除此之外还可以在Redis 运行时通过 CONFIG SET 命令在不重新启动 Redis 的情况下动态修改部分 Redis配置。就像这样:

  1. redis> CONFIG SET loglevel warning
  2. OK

并不是所有的配置都可以使用 CONFIG SET 命令修改,附录B 列出了哪些配置能够使用该命令修改。同样在运行的时候也可以使用 CONFIG GET 命令获得 Redis 当前的配置情况, 如:

  1. redis> CONFIG GET loglevel
  2. 1) "loglevel"
  3. 2) "warning"

其中第一行字符串回复表示的是选项名,第二行即是选项值。

2.5 多数据库

第1章介绍过 Redis 是一个字典结构的存储服务器,而实际上一个 Redis 实例提供了多个用来存储数据的字典,客户端可以指定将数据存储在哪个字典中。这与我们熟知的在一个关系数据库实例中可以创建多个数据库类似,所以可以将其中的每个字典都理解成一个独立的数据库。
每个数据库对外都是以一个从0开始的递增数字命名,Redis 默认支持16个数据库,可以通过配置参数 databases 来修改这一数字。客户端与 Redis 建立连接后会自动选择0号数据库,不过可以随时使用 SELECT命令更换数据库,如要选择1号数据库:

  1. redis> SELECT 1
  2. OK
  3. redis [1]> GET foo
  4. (nil)

然而这些以数字命名的数据库又与我们理解的数据库有所区别。首先 Redis 不支持自定义数据库的名字,每个数据库都以编号命名,开发者必须自己记录哪些数据库存储了哪些数 据。另外 Redis 也不支持为每个数据库设置不同的访问密码,所以一个客户端要么可以访问全部数据库,要么连一个数据库也没有权限访问。最重要的一点是多个数据库之间并不是完全隔离的,比如 FLUSHALL 命令可以清空一个Redis实例中所有数据库中的数据。综上所述,这些数据库更像是一种命名空间,而不适宜存储不同应用程序的数据。比如可以使用0号数据库存储某个应用生产环境中的数据,使用1号数据库存储测试环境中的数据,但不适宜使用0号数据库存储A应用的数据而使用1号数据库存储B应用的数据,不同的应用应该使用不同的 Redis 实例存储数据。由于Redis 非常轻量级,一个空 Redis 实例占用的内存只有1MB左右,所以不用担心多个 Redis 实例会额外占用很多内存。

注释

[1] 微软开放技术有限公司(Microsoft Open Technologies Inc.),专注于参与开源项 目、开放标准工作组以及提出倡议。
[2] https://github.com/MSOpenTech/Redis
[3] 6379 是手机键盘上MERZ对应的数字,MERZ是一名意大利歌女的名字。