Redis实战之入门进阶到精通-阿里云开发者社区

开发者社区> 开发与运维> 正文
登录阅读全文

Redis实战之入门进阶到精通

简介: Redis 是一个远程内存数据库,它不仅性能强劲,而且还具有复制特性以及为解决问题而生的独一无二的数据模型。Redis 提供了 5 种不同类型的数据结构,各式各样的问题都可以很自然地映射到这些数据结构上:Redis 的数据结构致力于帮助用户解决问题,而不会像其他数据库那样,要求用户扭曲问题来适应数据库。除此之外,通过复制、持久化(persistence)和客户端分片(client-side sharding)等特性,用户可以很方便地将 Redis 扩展成一个能够包含数百 GB 数据、每秒处理上百万次请求的系统。

本章主要内容
 Redis 与其他软件的相同之处和不同之处
 Redis 的用法
 使用 Python 示例代码与 Redis 进行简单的互动
 使用 Redis 解决实际问题

以下知识点解析来自于《Redis实战》文档其中的一部分!由于不影响观看,只选取了其中的一部分来展示。
如果你也对这份 PDF 感兴趣的话,可以通过下面的方式获取,关注公众号:有故事的程序员。

Redis 是一个远程内存数据库,它不仅性能强劲,而且还具有复制特性以及为解决问题而生的独一无二的数据模型。Redis 提供了 5 种不同类型的数据结构,各式各样的问题都可以很自然地映射到这些数据结构上:Redis 的数据结构致力于帮助用户解决问题,而不会像其他数据库那样,要求用户扭曲问题来适应数据库。除此之外,通过复制、持久化(persistence)和客户端分片(client-side sharding)等特性,用户可以很方便地将 Redis 扩展成一个能够包含数百 GB 数据、每秒处理上百万次请求的系统。
笔者第一次使用 Redis 是在一家公司里面,这家公司需要对一个保存了 6 万个客户联系方式的关系数据库进行搜索,搜索可以根据名字、邮件地址、所在地和电话号码来进行,每次搜索需要花费 10~15 秒的时间。在花了一周时间学习 Redis 的基础知识之后,我使用 Redis重写了一个新的搜索引擎,然后又花费了数周时间来仔细测试这个新系统,使它达到生产级别,最终这个新的搜索系统不仅可以根据名字、邮件地址、所在地和电话号码等信息来过滤和排序客户联系方式,并且每次操作都可以在 50 毫秒之内完成,这比原来的搜索系统足足快了 200 倍。阅读本书可以让你学到很多小技巧、小窍门以及使用 Redis 解决某些常见问题的方法。
本章将介绍 Redis 的适用范围,以及在不同环境中使用 Redis 的方法(比如怎样跟不同的组件和编程语言进行通信等);而之后的章节则会展示各式各样的问题,以及使用 Redis 来解决这些问题的方法。

第 1 章 初识 Redis
1.1 Redis 简介
安装 Redis 和 Python 附录 A 介绍了快速安装 Redis 和 Python 的方法。
在其他编程语言里面使用 Redis 本书只展示了使用 Python 语言编写的示例代码,使用 Ruby、Java 和 JavaScript(Node.js)编写的示例代码可以在这里找到:https://github.com/josiahcarlson/redis-in-action。使用 Spring 框架的读者可以通过查看 http://www.springsource.org/spring-data/redis来学习如何在 Spring 框架中使用 Redis。

前面对于Redis数据库的描述只说出了一部分真相。Redis是一个速度非常快的非关系数据库(non-relational database),它可以存储键(key)与 5 种不同类型的值(value)之间的映射(mapping),可以将存储在内存的键值对数据持久化到硬盘,可以使用复制特性来扩展读性能,还可以使用客户端分片

1.1.1 Redis 与其他数据库和软件的对比
来扩展写性能,接下来的几节将分别介绍Redis的这几个特性。
如果你熟悉关系数据库,那么你肯定写过用来关联两个表的数据的 SQL 查询 。而 Redis 则属于人们常说的 NoSQL 数据库或者非关系数据库:Redis 不使用表,它的数据库也不会预定义或者强制去要求用户对 Redis 存储的不同数据进行关联。

高性能键值缓存服务器 memcached 也经常被拿来与 Redis 进行比较:这两者都可用于存储键值映射,彼此的性能也相差无几,但是 Redis 能够自动以两种不同的方式将数据写入硬盘,并且 Redis 除了能存储普通的字符串键之外,还可以存储其他 4 种数据结构,而 memcached 只能存储普通的字符串键。这些不同之处使得 Redis 可以用于解决更为广泛的问题,并且既可以用作主数据库(primary database)使用,又可以作为其他存储系统的辅助数据库(auxiliary database)使用。

本书的后续章节会分别介绍将 Redis 用作主存储(primary storage)和二级存储(secondary storage)时的用法和查询模式。一般来说,许多用户只会在 Redis 的性能或者功能是必要的情况下,才会将数据存储到 Redis 里面:如果程序对性能的要求不高,又或者因为费用原因而没办法将大量数据存储到内存里面,那么用户可能会选择使用关系数据库,或者其他非关系数据库。在实际中,读者应该根据自己的需求来决定是否使用 Redis,并考虑是将 Redis 用作主存储还是辅

① 分片是一种将数据划分为多个部分的方法,对数据的划分可以基于键包含的 ID、基于键的散列值,或者
基于以上两者的某种组合。通过对数据进行分片,用户可以将数据存储到多台机器里面,也可以从多台
机器里面获取数据,这种方法在解决某些问题时可以获得线性级别的性能提升。

助存储,以及如何通过复制、持久化和事务等手段保证数据的完整性。
表 1-1 展示了一部分在功能上与 Redis 有重叠的数据库服务器和缓存服务器,从这个表可以看出 Redis 与这些数据库及软件之间的区别。
image.png

1.1.2 附加特性
在使用类似 Redis 这样的内存数据库时,一个首先要考虑的问题就是“当服务器被关闭时,服务器存储的数据将何去何从呢?”Redis 拥有两种不同形式的持久化方法,它们都可以用小而紧凑的格式将存储在内存中的数据写入硬盘:第一种持久化方法为时间点转储(point-in-time dump),转储操作既可以在“指定时间段内有指定数量的写操作执行”这一条件被满足时执行,又可以通过调用两条转储到硬盘(dump-to-disk)命令中的任何一条来执行;第二种持久化方法将所有修改了数据库的命令都写入一个只追加(append-only)文件里面,用户可以根据数据的重要程度,将只追加写入设置为从不同步(sync)、每秒同步一次或者每写入一个命令就同步一次。

我们将在第 4 章中更加深入地讨论这些持久化选项。另外,尽管 Redis 的性能很好,但受限于 Redis 的内存存储设计,有时候只使用一台 Redis服务器可能没有办法处理所有请求。因此,为了扩展 Redis 的读性能,并为 Redis 提供故障转移

5 (failover)支持 ,Redis 实现了主从复制特性:执行复制的从服务器会连接上主服务器,接收主服务器发送的整个数据库的初始副本(copy);之后主服务器执行的写命令,都会被发送给所有连接着的从服务器去执行,从而实时地更新从服务器的数据集。因为从服务器包含的数据会不断地进行更新,所以客户端可以向任意一个从服务器发送读请求,以此来避免对主服务器进行集中式的访问。我们将在第 4 章中更加深入地讨论 Redis 从服务器。

1.1.3 使用 Redis 的理由
有 memcached 使用经验的读者可能知道,用户只能用 APPEND 命令将数据添加到已有字符串的末尾。memcached 的文档中声明,可以用 APPEND 命令来管理元素列表。这很好!用户可以将元素追加到一个字符串的末尾,并将那个字符串当作列表来使用。但随后如何删除这些元素呢?memcached 采用的办法是通过黑名单(blacklist)来隐藏列表里面的元素,从而避免对元素执行读取、更新、写入(包括在一次数据库查询之后执行的 memcached 写入)等操作。相反地,Redis 的 LIST 和 SET 允许用户直接添加或者删除元素。使用 Redis 而不是 memcached 来解决问题,不仅可以让代码变得更简短、更易懂、更易维护,而且还可以使代码的运行速度更快(因为用户不需要通过读取数据库来更新数据)。除此之外,在其他许多情况下,Redis 的效率和易用性也比关系数据库要好得多。

数据库的一个常见用法是存储长期的报告数据,并将这些报告数据用作固定时间范围内的聚合数据(aggregates)。收集聚合数据的常见做法是:先将各个行插入一个报告表里面,之后再通过扫描这些行来收集聚合数据,并根据收集到的聚合数据来更新聚合表中已有的那些行。之所以使用插入行的方式来存储,是因为对于大部分数据库来说,插入行操作的执行速度非常快(插入行只会在硬盘文件末尾进行写入)。不过,对表里面的行进行更新却是一个速度相当慢的操作,因为这种更新除了会引起一次随机读(random read)之外,还可能会引起一次随机写(random write)。而在Redis里面,用户可以直接使用原子的(atomic)INCR命令及其变种来计算聚合数据,并且因为Redis将数据存储在内存里面 ①

① 客观来讲,memcached 也能用在这个简单的场景里,但使用 Redis 存储聚合数据有以下 3 个好处:
1、首先,使用 Redis 可以将彼此相关的聚合数据放在同一个结构里面,这样访问聚合数据就会变得更为容易;
2、其次,使用 Redis 可以将聚合数据放到有序集合里面,构建出一个实时的排行榜;
3、最后,Redis 的聚合数据可以是整数或者浮点数,而 memcached 的聚合数据只能是整数。而且发送给Redis的命令请求并不需要经过典型的查询分析器(parser)或者查询优化器(optimizer)进行处理,所以对Redis存储的数据执行随机写的速度总是非常迅速的。

使用 Redis 而不是关系数据库或者其他硬盘存储数据库,可以避免写入不必要的临时数据,也免去了对临时数据进行扫描或者删除的麻烦,并最终改善程序的性能。虽然上面列举的都是一些简单的例子,但它们很好地证明了“工具会极大地改变人们解决问题的方式”这一点。

1.2 Redis 数据结构简介
正如之前的表 1-1 所示,Redis 可以存储键与 5 种不同数据结构类型之间的映射,这 5 种数据结构类型分别为 STRING(字符串)、LIST(列表)、SET(集合)、HASH(散列)和 ZSET(有序集合)。有一部分 Redis 命令对于这 5 种结构都是通用的,如 DEL、TYPE、RENAME 等;但也有一部分 Redis 命令只能对特定的一种或者两种结构使用,第 3 章将对 Redis 提供的命令进行更深入的介绍。
大部分程序员应该都不会对 Redis 的 STRING、LIST、HASH 这 3 种结构感到陌生,因为它们和很多编程语言内建的字符串、列表和散列等结构在实现和语义(semantics)方面都非常相似。有些编程语言还有集合数据结构,在实现和语义上类似于 Redis 的 SET。ZSET 在某种程度上是一种 Redis 特有的结构,但是当你熟悉了它之后,就会发现它也是一种非常有用的结构。表 1-2 对比了 Redis 提供的 5 种结构,说明了这些结构存储的值,并简单介绍了它们的语义。
image.png

命令列表 本节在介绍每个数据类型的时候,都会在一个表格里面展示一小部分处理这些数据结 构的命令,之后的第 3会展示一个更详细(但仍不完整)的命令列表,完整的 Redis 命令列表 可以http://redis.io/commands 找到。

这一节将介绍如何表示 Redis 的这 5 种结构,并且还会介绍 Redis 命令的使用方法,从而为本书的后续内容打好基础。本书展示的所有示例代码都是用 Python 写的,如果读者已经按照附录 A 里面描述的方法安装好了 Redis,那么应该也已经安装好了 Python,以及在 Python 里面使用Redis 所需的客户端库。只要读者在电脑里面安装了 Redis、Python 和 redis-py 库,就可以在阅读本书的同时,尝试执行书中展示的示例代码了。

请安装 Redis 和 Python 在阅读后续内容之前,请读者先按照附录 A 中介绍的方法安装 Redis 和Python。如果读者觉得附录 A 描述的安装方法过于复杂,那么这里有一个更简单的方法,但这 个方法只能用于 Debian系统(或者该系统的衍生系统):从 http://redis.io/download 下载 Redis 的 压缩包,解压压缩包,执行 make&& sudo make install,之后再执行 sudo python -m easy_install redis hiredis(hiredis 是可选的,它是一个使用 C 语言编写的高性能 Redis 客户端)。

如果读者熟悉过程式编程语言或者面向对象编程语言,那么即使没有使用过 Python,应该也
可以看懂 Python 代码。另一方面,如果读者决定使用其他编程语言来操作 Redis,那么就需要自
己来将本书的 Python 代码翻译成正在使用的语言的代码。

使用其他语言编写的示例代码 尽管没有包含在书中,但本书展示的 Python 示例代码已经被翻译成 了 Ruby 代码、Java 代码和JavaScript 代码,这些翻译代码可以在 https://github.com/josiahcarlson/redis-in-action 下载到。跟 Python 编写的示例代码一样,这些翻译代码也包含相应的注释,方便读 者参考。

为了让示例代码尽可能地简单,本书会尽量避免使用 Python 的高级特性,并使用函数而不是类或者其他东西来执行 Redis 操作,以此来将焦点放在使用 Redis 解决问题上面,而不必过多地关注 Python 的语法。本节将使用 redis-cli 控制台与 Redis 进行互动。首先,让我们来了解一下Redis 中最简单的结构:STRING。

1.2.1 Redis 中的字符串

Redis 的字符串和其他编程语言或者其他键值存储提供的字符串非常相似。本书在使用
图片表示键和值的时候,通常会将键名(key name)和值的类型放在方框的顶部,并将值
放在方框的里面。图 1-1 以键为 hello、值为 world 的字符串为例,分别标记了方框的各
个部分。
image.png
字符串拥有一些和其他键值存储相似的命令,比如 GET(获取值)、SET(设置值)和 DEL(删除值)。如果读者已经按照附录 A 中给出的方法安装了 Redis,那么可以根据代码清单 1-1 展示的例子,尝试使用 redis-cli 执行 SET、GET 和 DEL,表 1-3 描述了这 3 个命令的基本用法。

image.png

使用 redis-cli 为了让读者在一开始就能便捷地与Redis 进行交互,本章将使用 redis-cli 这个交互式 客户端来介绍Redis 命令。

除了能够 GET、SET 和 DEL 字符串值之外,Redis 还提供了一些可以对字符串的其中一部分内容进行读取和写入的命令,以及一些能对字符串存储的数值执行自增或者自减操作的命令。第 3 章将对这些命令进行介绍,但是在此之前,我们还有许多基础知识需要了解,下面来看一下 Redis 的列表及其功能。

1.2.2 Redis 中的列表
Redis 对链表(linked-list)结构的支持使得它在键值存储的世界中独树一帜。一个列表结构可以有序地存储多个字符串,和表示字符串时使用的方法一样,本节使用带有标签的方框来表示列表,并将列表包含的元素放在方框里面。图 1-2 展示了一个这样的示例。
image.png

Redis 列表可执行的操作和很多编程语言里面的列表操作非常相似:LPUSH 命令和 RPUSH 命令分别用于将元素推入列表的左端(left end)和右端(right end);LPOP 命令和 RPOP 命令分别用于从列表的左端和右端弹出元素;LINDEX 命令用于获取列表在给定位置上的一个元素;LRANGE 命令用于获取列表在给定范围上的所有元素。代码清单 1-2 展示了一些列表命令的使用示例,表 1-4 简单介绍了示例中用到的各个命令。
image.png

即使 Redis 的列表只支持以上提到的几个命令,它也已经可以用来解决很多问题了,但 Redis 并没有就此止步—除了上面提到的命令之外,Redis 列表还拥有从列表里面移除元素的命令、将元素插入列表中间的命令、将列表修剪至指定长度(相当于从列表的其中一端或者两端移除元素)的命令,以及其他一些命令。第 3 章将介绍许多列表命令,但是在此之前,让我们先来了解一下 Redis 的集合。
1.2.3 Redis 的集合
Redis 的集合和列表都可以存储多个字符串,它们之间的不同在于,列表可以存储多个相同的字符串,而
集合则通过使用散列表来保证自己存储的每个字符串都是各不相同的(这些散列表只有键,但没有与键相关联的值)。本书表示集合的方法和表示列表的方法基本相同,图 1-3 展示了一个包含 3 个元素的示例集合。
image.png

因为 Redis 的集合使用无序(unordered)方式存储元素,所以用户不能像使用列表那样,将元素推入集合的某一端,或者从集合的某一端弹出元素。不过用户可以使用 SADD 命令将元素添加到集合,或者使用 SREM命令从集合里面移除元素。另外还可以通过 SISMEMBER 命令快速地检查一个元素是否已经存在于集合中,或者使用 SMEMBERS 命令获取集合包含的所有元素(如果集合包含的元素非常多,那么SMEMBERS 命令的执行速度可能会很慢,所以请谨慎地使用这个命令)。代码清单 1-3 展示了一些集合命令的使用示例,表 1-5 简单介绍了代码清单里面用到的各个命令。
image.png
image.png

跟字符串和列表一样,集合除了基本的添加操作和移除操作之外,还支持很多其他操作,比如 SINTER、SUNION、SDIFF 这 3 个命令就可以分别执行常见的交集计算、并集计算和差集计算。第 3 章将对集合的相关命令进行更详细的介绍,另外第 7 章还会展示如何使用集合来解决多个问题。不过别心急,因为在 Redis 提供的 5 种数据结构中,还有两种我们尚未了解,让我们先来看看 Redis 的散列。
1.2.4 Redis 的散列
Redis 的散列可以存储多个键值对之间的映射。和字符串一样,散列存储的值既可以是字符串又可以是数字值,并且用户同样可以对散列存储的数字值执行自增操作或者自减操作。图 1-4展示了一个包含两个键值对的散列。
image.png

散列在很多方面就像是一个微缩版的 Redis,不少字符串命令都有相应的散列版本。代码清单 1-4 展示了怎样对散列执行插入元素、获取元素和移除元素等操作,表 1-6 简单介绍了代码清单里面用到的各个命令。
image.png
image.png

熟悉文档数据库的读者可以将 Redis 的散列看作是文档数据库里面的文档,而熟悉关系数据库的读者则可以将 Redis 的散列看作是关系数据库里面的行,因为散列、文档和行这三者都允许用户同时访问或者修改一个或多个域(field)。最后,让我们来了解一下Redis 的 5 种数据结构中的最后一种:有序集合。

1.2.5 Redis 的有序集合
有序集合和散列一样,都用于存储键值对:有序集合的键被称为成员(member),每个成员都是各不相同的;而有序集合的值则被称为分值(score),分值必须为浮点数。有序集合是 Redis 里面唯一一个既可以根据成员访问元素(这一点和散列一样),又可以根据分值以及分值的排列顺序来访问元素的结构。图 1-5 展示了一个包含两个元素的有序集合示例。
image.png

和 Redis 的其他结构一样,用户可以对有序集合执行添加、移除和获取等操作,代码清单 1-5
展示了这些操作的执行示例,表 1-7 简单介绍了代码清单里面用到的各个命令。
image.png
image.png

现在读者应该已经知道有序集合是什么和它能干什么了,到目前为止,我们基本了解了 Redis提供的 5 种结构。接下来的一节将展示如何通过结合散列的数据存储能力和有序集合内建的排序能力来解决一个常见的问题。

以上知识点解析来自于《Redis实战》文档其中的一部分!由于不影响观看,只选取了其中的一部分来展示。
如果你也对这份 PDF 感兴趣的话,可以通过下面的方式获取,关注公众号:有故事的程序员,获取领取方式!

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
开发与运维
使用钉钉扫一扫加入圈子
+ 订阅

集结各类场景实战经验,助你开发运维畅行无忧

其他文章