使用Redis的理由
Redis是一个远程内存数据库,它不仅性能强劲,而且还具有复制特性以及为解决问题而生的独一无二的数据模型。Redis提供了5种不同类型的数据结构,各式各样的问题都可以很自然地映射到这些数据结构上:Redis的数据结构致力于帮助用户解决问题,而不会像其他数据库那样,要求用户扭曲问题来适应数据库。除此之外,通过复制、持久化(persistence)和客户端分片(client-side sharding)等特性,用户可以很方便地将Redis扩展成一个能够包含数百GB数据、每秒处理上百万次请求的系统。
笔者第一次使用Redis是在一家公司里面,这家公司需要对一个保存了6万个客户联系方式的关系数据库进行搜索,搜索可以根据名字、邮件地址、所在地和电话号码来进行,每次搜索需要花费10~15秒的时间。在花了一周时间学习Redis的基础知识之后,我使用Redis重写了一个新的搜索引擎,然后又花费了数周时间来仔细测试这个新系统,使它达到生产级别,最终这个新的搜索系统不仅可以根据名字、邮件地址、所在地和电话号码等信息来过滤和排序客户联系方式,并且每次操作都可以在50毫秒之内完成,这比原来的搜索系统足足快了 200 倍。
1、Redis简介
前面对于Redis数据库的描述只说出了一部分真相。Redis是一个速度非常快的非关系数据库(non-relational database),它可以存储键(key)与5种不同类型的值(value)之间的映射(mapping),可以将存储在内存的键值对数据持久化到硬盘,可以使用复制特性来扩展读性能,还可以使用客户端分片1来扩展写性能。
2、使用Redis的理由
有memcached使用经验的读者可能知道,用户只能用APPEND命令将数据添加到已有字符串的末尾。memcached的文档中声明,可以用APPEND命令来管理元素列表。这很好!用户可以将元素追加到一个字符串的末尾,并将那个字符串当作列表来使用。但随后如何删除这些元素呢?memcached采用的办法是通过黑名单(blacklist)来隐藏列表里面的元素,从而避免对元素执行读取、更新、写入(包括在一次数据库查询之后执行的memcached写入)等操作。相反地,Redis的LIST和SET允许用户直接添加或者删除元素。
使用Redis而不是memcached来解决问题,不仅可以让代码变得更简短、更易懂、更易维护,而且还可以使代码的运行速度更快(因为用户不需要通过读取数据库来更新数据)。除此之外,在其他许多情况下,Redis的效率和易用性也比关系数据库要好得多。
数据库的一个常见用法是存储长期的报告数据,并将这些报告数据用作固定时间范围内的聚合数据(aggregates)。收集聚合数据的常见做法是:先将各个行插入一个报告表里面,之后再通过扫描这些行来收集聚合数据,并根据收集到的聚合数据来更新聚合表中已有的那些行。之所以使用插入行的方式来存储,是因为对于大部分数据库来说,插入行操作的执行速度非常快(插入行只会在硬盘文件末尾进行写入)。不过,对表里面的行进行更新却是一个速度相当慢的操作,因为这种更新除了会引起一次随机读(random read)之外,还可能会引起一次随机写(random write)。而在Redis里面,用户可以直接使用原子的(atomic)INCR命令及其变种来计算聚合数据,并且因为Redis将数据存储在内存里面2,而且发送给Redis的命令请求并不需要经过典型的查询分析器(parser)或者查询优化器(optimizer)进行处理,所以对Redis存储的数据执行随机写的速度总是非常迅速的。
使用 Redis 而不是关系数据库或者其他硬盘存储数据库,可以避免写入不必要的临时数据,也免去了对临时数据进行扫描或者删除的麻烦,并最终改善程序的性能。虽然上面列举的都是一些简单的例子,但它们很好地证明了“工具会极大地改变人们解决问题的方式”这一点。
3、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用作主存储还是辅助存储,以及如何通过复制、持久化和事务等手段保证数据的完整性。展示了一部分在功能上与Redis有重叠的数据库服务器和缓存服务器,从以下表中可以看出Redis与这些数据库及软件之间的区别。
一些数据库和缓存服务器的特性与功能
名称 |
类型 |
数据存储选项 |
查询类型 |
附加功能 |
---|---|---|---|---|
Redis |
使用内存存储(in-memory)的非关系数据库 |
字符串、列表、集合、散列表、有序集合 |
每种数据类型都有自己的专属命令,另外还有批量操作(bulk operation)和不完全(partial)的事务支持 |
发布与订阅,主从复制(master/slave replication),持久化,脚本(存储过程,stored procedure) |
memcached |
使用内存存储的键值缓存 |
键值之间的映射 |
创建命令、读取命令、更新命令、删除命令以及其他几个命令 |
为提升性能而设的多线程服务器 |
MySQL |
关系数据库 |
每个数据库可以包含多个表,每个表可以包含多个行;可以处理多个表的视图(view);支持空间(spatial)和第三方扩展 |
SELECT、INSERT、UPDATE、DELETE、函数、存储过程 |
支持ACID性质(需要使用InnoDB),主从复制和主主复制 (master/master replication) |
PostgreSQL |
关系数据库 |
每个数据库可以包含多个表,每个表可以包含多个行;可以处理多个表的视图;支持空间和第三方扩展;支持可定制类型 |
SELECT、INSERT、UPDATE、DELETE、内置函数、自定义的存储过程 |
支持ACID性质,主从复制,由第三方支持的多主复制(multi-master replication) |
MongoDB |
使用硬盘存储(on-disk)的非关系文档存储 |
每个数据库可以包含多个表,每个表可以包含多个无schema(schema-less)的BSON文档 |
创建命令、读取命令、更新命令、删除命令、条件查询命令等 |
支持map-reduce操作,主从复制,分片,空间索引(spatial index) |
4、Redis附加特性
在使用类似Redis这样的内存数据库时,一个首先要考虑的问题就是“当服务器被关闭时,服务器存储的数据将何去何从呢?”Redis拥有两种不同形式的持久化方法,它们都可以用小而紧凑的格式将存储在内存中的数据写入硬盘:第一种持久化方法为时间点转储(point-in-time dump),转储操作既可以在“指定时间段内有指定数量的写操作执行”这一条件被满足时执行,又可以通过调用两条转储到硬盘(dump-to-disk)命令中的任何一条来执行;第二种持久化方法将所有修改了数据库的命令都写入一个只追加(append-only)文件里面,用户可以根据数据的重要程度,将只追加写入设置为从不同步(sync)、每秒同步一次或者每写入一个命令就同步一次。我们将在第4章中更加深入地讨论这些持久化选项。
另外,尽管Redis的性能很好,但受限于Redis的内存存储设计,有时候只使用一台Redis服务器可能没有办法处理所有请求。因此,为了扩展Redis的读性能,并为Redis提供故障转移(failover)支持,Redis实现了主从复制特性:执行复制的从服务器会连接上主服务器,接收主服务器发送的整个数据库的初始副本(copy);之后主服务器执行的写命令,都会被发送给所有连接着的从服务器去执行,从而实时地更新从服务器的数据集。因为从服务器包含的数据会不断地进行更新,所以客户端可以向任意一个从服务器发送读请求,以此来避免对主服务器进行集中式的访问。我们将在第4章中更加深入地讨论Redis从服务器。
链接:http://www.epubit.com.cn/article/200