学习Redis从这里开始

Redis是一个远程内存数据库 , 它不仅性能强劲 , 而且还具有复制特性以及为解决问题而生的独一无二的数据模型 。 Redis提供了5种不同类型的数据结构 , 各式各样的问题都可以很自然地映射到这些数据结构上:Redis的数据结构致力于帮助用户解决问题 , 而不会像其他数据库那样 , 要求用户扭曲问题来适应数据库 。 除此之外 , 通过复制、持久化(persistence)和客户端分片(client-sidesharding)等特性 , 用户可以很方便地将Redis扩展成一个能够包含数百GB数据、每秒处理上百万次请求的系统 。
笔者第一次使用Redis是在一家公司里面 , 这家公司需要对一个保存了6万个客户联系方式的关系数据库进行搜索 , 搜索可以根据名字、邮件地址、所在地和电话号码来进行 , 每次搜索需要花费10~15秒的时间 。 在花了一周时间学习Redis的基础知识之后 , 我使用Redis重写了一个新的搜索引擎 , 然后又花费了数周时间来仔细测试这个新系统 , 使它达到生产级别 , 最终这个新的搜索系统不仅可以根据名字、邮件地址、所在地和电话号码等信息来过滤和排序客户联系方式 , 并且每次操作都可以在50毫秒之内完成 , 这比原来的搜索系统足足快了200倍 。 阅读本书可以让你学到很多小技巧、小窍门以及使用Redis解决某些常见问题的方法 。
本章将介绍Redis的适用范围 , 以及在不同环境中使用Redis的方法(比如怎样跟不同的组件和编程语言进行通信等);而之后的章节则会展示各式各样的问题 , 以及使用Redis来解决这些问题的方法 。
现在你已经知道我是怎样开始使用Redis的了 , 也知道了这本书大概要讲些什么内容了 , 是时候更详细地介绍一下Redis , 并说明为什么应该使用Redis了 。
(使用优惠码Redis_EPUBIT_T5BY7 , 在异步社区购买《Redis实战》纸书可以享受七折优惠)
在其他编程语言里面使用Redis本书只展示了使用Python语言编写的示例代码 , 使用Ruby、Java和JavaScript(Node.js)编写的示例代码可以在这里找到: 。 使用Spring框架的读者可以通过查看来学习如何在Spring框架中使用Redis 。
前面对于Redis数据库的描述只说出了一部分真相 。 Redis是一个速度非常快的非关系数据库(non-relationaldatabase) , 它可以存储键(key)与5种不同类型的值(value)之间的映射(mapping) , 可以将存储在内存的键值对数据持久化到硬盘 , 可以使用复制特性来扩展读性能 , 还可以使用客户端分片1来扩展写性能 , 接下来的几节将分别介绍Redis的这几个特性 。
如果你熟悉关系数据库 , 那么你肯定写过用来关联两个表的数据的SQL查询 。 而Redis则属于人们常说的NoSQL数据库或者非关系数据库:Redis不使用表 , 它的数据库也不会预定义或者强制去要求用户对Redis存储的不同数据进行关联 。
高性能键值缓存服务器memcached也经常被拿来与Redis进行比较:这两者都可用于存储键值映射 , 彼此的性能也相差无几 , 但是Redis能够自动以两种不同的方式将数据写入硬盘 , 并且Redis除了能存储普通的字符串键之外 , 还可以存储其他4种数据结构 , 而memcached只能存储普通的字符串键 。 这些不同之处使得Redis可以用于解决更为广泛的问题 , 并且既可以用作主数据库(primarydatabase)使用 , 又可以作为其他存储系统的辅助数据库(auxiliarydatabase)使用 。
本书的后续章节会分别介绍将Redis用作主存储(primarystorage)和二级存储(secondarystorage)时的用法和查询模式 。 一般来说 , 许多用户只会在Redis的性能或者功能是必要的情况下 , 才会将数据存储到Redis里面:如果程序对性能的要求不高 , 又或者因为费用原因而没办法将大量数据存储到内存里面 , 那么用户可能会选择使用关系数据库 , 或者其他非关系数据库 。 在实际中 , 读者应该根据自己的需求来决定是否使用Redis , 并考虑是将Redis用作主存储还是辅助存储 , 以及如何通过复制、持久化和事务等手段保证数据的完整性 。
表1-1展示了一部分在功能上与Redis有重叠的数据库服务器和缓存服务器 , 从这个表可以看出Redis与这些数据库及软件之间的区别 。
学习Redis从这里开始
文章图片
表1-1一些数据库和缓存服务器的特性与功能
在使用类似Redis这样的内存数据库时 , 一个首先要考虑的问题就是“当服务器被关闭时 , 服务器存储的数据将何去何从呢?”Redis拥有两种不同形式的持久化方法 , 它们都可以用小而紧凑的格式将存储在内存中的数据写入硬盘:第一种持久化方法为时间点转储(point-in-timedump) , 转储操作既可以在“指定时间段内有指定数量的写操作执行”这一条件被满足时执行 , 又可以通过调用两条转储到硬盘(dump-to-disk)命令中的任何一条来执行;第二种持久化方法将所有修改了数据库的命令都写入一个只追加(append-only)文件里面 , 用户可以根据数据的重要程度 , 将只追加写入设置为从不同步(sync)、每秒同步一次或者每写入一个命令就同步一次 。 我们将在第4章中更加深入地讨论这些持久化选项 。
另外 , 尽管Redis的性能很好 , 但受限于Redis的内存存储设计 , 有时候只使用一台Redis服务器可能没有办法处理所有请求 。 因此 , 为了扩展Redis的读性能 , 并为Redis提供故障转移(failover)支持 , Redis实现了主从复制特性:执行复制的从服务器会连接上主服务器 , 接收主服务器发送的整个数据库的初始副本(copy);之后主服务器执行的写命令 , 都会被发送给所有连接着的从服务器去执行 , 从而实时地更新从服务器的数据集 。 因为从服务器包含的数据会不断地进行更新 , 所以客户端可以向任意一个从服务器发送读请求 , 以此来避免对主服务器进行集中式的访问 。 我们将在第4章中更加深入地讨论Redis从服务器 。
有memcached使用经验的读者可能知道 , 用户只能用APPEND命令将数据添加到已有字符串的末尾 。 memcached的文档中声明 , 可以用APPEND命令来管理元素列表 。 这很好!用户可以将元素追加到一个字符串的末尾 , 并将那个字符串当作列表来使用 。 但随后如何删除这些元素呢?memcached采用的办法是通过黑名单(blacklist)来隐藏列表里面的元素 , 从而避免对元素执行读取、更新、写入(包括在一次数据库查询之后执行的memcached写入)等操作 。 相反地 , Redis的LIST和SET允许用户直接添加或者删除元素 。
使用Redis而不是memcached来解决问题 , 不仅可以让代码变得更简短、更易懂、更易维护 , 而且还可以使代码的运行速度更快(因为用户不需要通过读取数据库来更新数据) 。 除此之外 , 在其他许多情况下 , Redis的效率和易用性也比关系数据库要好得多 。
【学习Redis从这里开始】数据库的一个常见用法是存储长期的报告数据 , 并将这些报告数据用作固定时间范围内的聚合数据(aggregates) 。 收集聚合数据的常见做法是:先将各个行插入一个报告表里面 , 之后再通过扫描这些行来收集聚合数据 , 并根据收集到的聚合数据来更新聚合表中已有的那些行 。 之所以使用插入行的方式来存储 , 是因为对于大部分数据库来说 , 插入行操作的执行速度非常快(插入行只会在硬盘文件末尾进行写入) 。 不过 , 对表里面的行进行更新却是一个速度相当慢的操作 , 因为这种更新除了会引起一次随机读(randomread)之外 , 还可能会引起一次随机写(randomwrite) 。 而在Redis里面 , 用户可以直接使用原子的(atomic)INCR命令及其变种来计算聚合数据 , 并且因为Redis将数据存储在内存里面2 , 而且发送给Redis的命令请求并不需要经过典型的查询分析器(parser)或者查询优化器(optimizer)进行处理 , 所以对Redis存储的数据执行随机写的速度总是非常迅速的 。


    推荐阅读