一、简介
Redis 是一个开源的、高性能的、基于键值对的缓存和存储系统,通过多种键值数据类型来适应不同场景下的缓存和存储需求。
Redis 是一个高性能的键值对数据库,相比于其他键值对缓存产品,有以下特点:
支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
不仅仅支持简单的键值对类型数据,同时还提供 list、set、zset、hash 等数据结构的存储。
支持数据的备份,包括主从设备模式的数据备份,主设备定期更新到从设备。
功能丰富,可以用作缓存、队列等,并且可以设置键的生存时间。
简单稳定,其直观的存储结构使得通过程序与其交互十分简单。
二、准备
在安装 Redis 之前需要了解 Redis 的版本规则以选择最适合自己的版本。Redis 约定次版本号为偶数的版本是稳定版本,奇数版本是非稳定版,推荐使用稳定版本进行开发和生产环境使用。
支持在 POSIX、OSX、windows 等多种版本的系统。
启动 Redis 有直接启动和通过初始化脚本启动两种方式,分别适用于开发环境和生产环境。
通过 Redis 命令行客户端可以进行 Redis 的学习和测试。
除了端口号之外,Redis 还支持持久化、日志级别等多种设置选项。
三、入门
字符串类型时 Redis 中最基本的数据类型,它能存储任何形式的字符串,包括二进制数据。字符串类型是其他数据类型的基础,其他数据类型和字符串类型的差别从某种角度来说只是组织字符串的形式不同。
散列类型的键值也是一种字典结构,其存储了字段和字段值的映射,但字段值只能是字符串,不支持其他数据类型。换句话说,散列类型不能嵌套其他的数据类型(其他类型同样不支持数据嵌套)。一个散列类键可以包含至多 232-1 个字段。散列类型适合存储对象:使用对象类别和ID构成键名,是用字段表示对象的属性,而字段值则存储属性值。
列表类型可以存储一个有序的字符串列表,常用的操作是向列表两端添加元素,或者获得列表的某一个片段。列表类型内部是使用双向链表实现的,所以向列表两端添加元素的时间复杂度为 O(1),获取越接近两端的元素速度就越快。与散列类型键最多能容纳的字段数量相同,一个列表类型最多能容纳 232-1 个元素。
集合类型可以存储一个无序的字符串列表,且每个元素都是不同的,一个集合类型键可以存储至多232-1个字符串。集合类型的常用操作是向集合中加入或者删除元素、判断某个元素是否存在等。由于集合在 Redis 内部是使用值为空的散列表实现的,所以这些操作的事件复杂度都是 O(1)。最方便的是多个集合类型键之间还可以进行并集、交集和差集运算。
有序集合类型时在集合类型上为每个元素都关联了一个分数,不仅可以完成插入、删除、判断元素是否存在等操作,还能够获取最值、操作指定范围等。虽然集合中每个元素都是不同的,但是它们的分数却可以相同。有序集合类型是使用散列表和跳跃表实现的,所以即使读取位于中间部分的数据速度也很快,而且可以简单调整某个元素的位置。相对应的是,其存储更加耗费内存。
四、进阶
Redis 中的事务是一组命令的集合。事务同命令一样都是 Redis 的最小执行范围,一个事务中的命令要么都执行,要么都不执行。Redis 保证一个事务从开始执行到执行结束期间,不会穿插的执行其他的命令或事务。由于 Redis 不支持回滚功能,也使得 Redis 在事务上可以保持简洁和快速。
对于有时效的数据,在关系数据库中一般需要额外的一个字段记录到期时间,然后定期检测删除过期数据。当服务器内存有限时,如果大量使用缓存键且生存时间设置过长会导致内存占满,而为了节省内存将缓存键生存时间设置太短可能导致缓存命中率过低。实际开发中很难为缓存键设置合理的生存时间,为此可以限制 Redis 能够使用的最大内存,并让 Redis 按照一定的淘汰规则淘汰不需要的缓存键,这种方式在只将 Redis 用作缓存系统时非常实用。
集合类型提供了强大的集合操作能力,但是如果需要排序就要用到有序集合类型。Redis 的作者在设计 Redis 的命令时考虑到了不同数据类型的使用场景,对于不常用到的或者在不损失过多性能的墙体下可以使用现有的命令来实现的功能,Redis 就不会单独提供命令来实现。
任务队列就是传递任务的队列,与队列进行交互的实体有生产者和消费者,使用任务队列有松耦合和易于拓展的好处。发布订阅模式同样可以实现进程间的消息传递,其中包含发布者和订阅者两种角色。
客户端和 Redis 使用 TCP 协议连接,在网络传输过程中产生的时延较大。通过管道可以一次性发送多条命令并在执行完后一次性将结果返回,当一组命令中的每条命令都不依赖于之前命令的执行结果时就可以将这组命令一起通过管道发出。管道通过减少客户端与 Redis 的通信次数来实现降低往返时延累计值的目的。
精简键名和键值是最直观的减少内存占用的方式。有时候仅凭精简键名和键值所减少的空间并不足以满足需要,这时就需要根据 Redis 内部编码规则来节省更多的空间。
五、实践
Redis 官方推荐的 PHP 客户端是 Predis 和 phpredis,前者是完全使用 PHP 代码实现的原生客户端,后者则是使用 C 语言编写的 PHP 拓展。二者在功能上区别不大,就性能而言,后者会更胜一筹。
Redis 官方推荐的 Ruby 客户端是 redis-b,也是各种语言的 Redis 客户端中最为稳定的一个。
Redis 官方推荐的 Python 客户端是 redis-py。
Redis 官方推荐的 Node.js 客户端是 node_redis。
六、脚本
Redis 在 2.6 版推出了脚本功能,允许开发者使用 Lua 语言编写脚本传到 Redis 中执行,在 Lua 脚本中可以调用大部分的 Redis 命令。
使用脚本的好处:减少网络开销、原子操作、复用。
Lua 语言是一个高效的轻量级脚本语言,能够方便地嵌入到其他语言中使用。
七、管理
Redis 的强劲性能很大程度上是由于其将所有数据都存储在内存中,为了使 Redis 在重启之后仍能保证数据不丢失,需要将数据从内存中以某种形式同步到硬盘中,这一过程就是持久化。
Redis 支持两种方式的持久化,一种是 RDB 方式,一种是 AOF 方式。可以单独使用其中一种或者将二者结合使用。
为了避免单点故障,我们虚妄将数据库复制多个副本以部署在不同的服务器上,即使有一台服务器出现故障其他的服务器依然可以继续提供服务。这就要求当一台服务器上的数据库更新后,可以自动更新到其他服务器上, Redis 提供了复制功能可以实现同步的过程。
在常见的场景中,读的频率大于写,当单机 Redis 无法应付大量的读请求时,可以通过复制功能建立多个从数据库,主数据库只进行写操作,而从数据库负责读操作。
Redis 的安全设计是在 “Redis 运行在可信环境” 这个前提下作出的,在生产环境运行时不能允许外界直接连接到 Redis 服务器上,而是应该通过应用程序进行中转。运行在可信的环境中是保证 Redis 安全的最重要的方法。
Redis 通信协议是 Redis 客户端与 Redis 之间交流的语言,通信协议规定了命令和返回值的格式。Redis 支持两种通信协议,一种是二进制安全的统一请求协议,一种是比较直观的便于在 telnet 程序中输入的简单协议。
Redis 中还有许多管理工具,比如 redis-cli、phpRedisAdmin、Rdbtools 等。