
第1章 简介
Redis 是一个开源的、高性能的、基于键值对的缓存与存储系统,通过提供多种键值数据类型来适应不同场景下的缓存与存储需求。同时Redis的诸多高层级功能使其可以胜任消息队列、任务队列等不同的角色。除此之外,Redis还支持外部模块扩展,使其在某些场景下可以作为主数据库使用。
本章将分别介绍Redis的历史和特性,以使读者能够快速地对Redis有一个全面的了解。
1.1 历史与发展
2008年,意大利的一家创业公司Merzia推出了一款基于MySQL的网站实时统计系统LLOOGG,然而没过多久,该公司的创始人Salvatore Sanfilippo便开始对MySQL的性能感到失望,于是他决定亲自为LLOOGG量身定制一个数据库,并于2009年开发完成,这个数据库就是Redis。不过Salvatore Sanfilippo并不满足将Redis只用于LLOOGG这一款产品,而是希望让更多的人使用它,于是在同一年Salvatore Sanfilippo将Redis开源发布,并开始和Redis的另一名主要的代码贡献者Pieter Noordhuis一起继续着Redis的开发。
Salvatore Sanfilippo自己也没有想到,短短的几年时间,Redis就拥有了庞大的用户群体。截至2021年,在Stack Overflow发布的全球开发者调查报告中,Redis连续4年蝉联“最受开发者喜爱的数据库”以及“亚马逊云使用最广泛的数据库”两项殊荣。Redis的国内用户包括新浪微博、街旁网和知乎等,国外用户包括GitHub、Stack Overflow、Flickr、暴雪和Instagram等。
VMware公司从2010年开始赞助Redis的开发,Salvatore Sanfilippo和Pieter Noordhuis也分别于同年的3月和5月加入VMware,全职开发Redis。
随后在2015年7月15日,Salvatore Sanfilippo加入一家位于美国加利福尼亚州的公司Redis Labs。这家公司专门提供围绕Redis的数据库云服务。从此,Redis Labs正式成为Redis的官方赞助商。
2020年6月30日,Salvatore Sanfilippo决定退居二线,即不再参与Redis的日常维护,而是作为Redis Labs的技术顾问去探索如何让Redis更进一步等更多未知的事情。自此,Redis Labs的首席架构师Yossi Gottlieb和高级软件架构师Oran Arga接替Salvatore Sanfilippo的工作让Redis继续前进。
Redis的代码托管在GitHub上,开发十分活跃。截至本书出版,Redis的最新稳定版本为发布于2020年的Redis 6。本书的内容也是基于此版本编写的。
小背景
2009年2月25日,有人在Hacker News上发布了一个帖子(如图1-1所示),内容就是“Redis”这五个字母,还有到当时Redis的托管商Google Code的链接。Redis的作者在这个贴子下面发表回复:“Redis(与其他竞品相比)的一个重要目标就是让键值能够支持更多高级复杂的数据类型。”实际上一直到十几年后的今天,Redis仍然在朝着这个方向努力。
图1-1 Redis官网提供了详细的命令文档
1.2 特性
作为一款最初由个人开发的系统,Redis究竟有什么魅力经久不衰,吸引了如此多的用户呢?
1.2.1 存储结构
有脚本语言编程经验的读者对字典(或称映射、关联数组)数据结构一定很熟悉,如代码dict["key"] = "value"中dict是一个字典变量,字符串"key"是键名,而"value"是键值,在字典中我们可以获取或设置键名对应的键值,也可以删除一个键。
Redis是REmote DIctionary Server(远程字典服务器)的缩写,它以字典存储数据,并允许其他应用通过TCP读写字典中的内容。同大多数脚本语言中的字典一样,Redis字典中的键值除了可以是字符串,还可以是其他数据类型。到目前为止,Redis支持的键值数据类型如下:
● 字符串类型(其扩展类型还包括HyperLogLog类型);
● 哈希类型;
● 列表类型;
● 集合类型;
● 有序集合类型;
● 流类型。
这种字典形式的存储结构与常见的MySQL等关系数据库的二维表形式的存储结构有很大的差异。举个例子,如下所示,我们在程序中使用post变量存储了一篇文章的数据(包括标题、正文、阅读量和标签):
post["title"] = "Hello World!" post["content"] = "Blablabla..." post["views"] = 0 post["tags"] = ["PHP", "Ruby", "Node.js"]
现在我们希望将这篇文章的数据存储在数据库中,并且要求可以通过标签检索出文章。如果使用关系数据库存储,一般会将其中的标题、正文和阅读量存储在一个表中,而将标签存储在另一个表中,然后使用第三个表连接文章和标签表[1]。需要查询时还得将3个表进行连接,不是很直观。而Redis字典结构的存储方式和对多种键值数据类型的支持使得开发者可以将程序中的数据直接映射到Redis中,数据在Redis中的存储形式和其在程序中的存储方式非常相近。使用Redis的另一个优势是其对不同的数据类型提供了非常方便的操作方式,如使用集合类型存储文章标签,对标签进行如交、并这样的集合运算操作。3.5节会专门介绍如何借助集合运算轻松地实现“找出所有同时属于A标签和B标签且不属于C标签的元素”这样用关系数据库实现起来性能不高且较为烦琐的操作。
1.2.2 内存存储与持久化
Redis数据库中的所有数据都存储在内存中。由于内存的读写速度远快于硬盘,因此Redis在性能上对比其他基于硬盘存储的数据库有非常明显的优势,在一台普通的笔记本计算机上,Redis可以在一秒内读写超过10万个键值。
将数据存储在内存中也有问题,例如程序退出后内存中的数据会丢失。不过Redis提供了对持久化的支持,即可以将内存中的数据异步写入硬盘中,同时不影响继续提供服务。
1.2.3 功能丰富
Redis虽然是作为数据库开发的,但由于其提供了丰富的功能,越来越多的人将其用作缓存、队列系统等。Redis可谓是名副其实的多面手。
Redis可以为每个键设置生存时间(Time To Live,TTL),生存时间到期后键会自动被删除。这一功能配合出色的性能让Redis可以作为缓存系统来使用,而且Redis支持持久化和丰富的数据类型的特性使其成为另一个非常流行的缓存系统Memcached的有力竞争者。
讨论
关于Redis和Memcached优劣的讨论一直是一个热门的话题。在性能上Redis是单线程模型,而Memcached支持多线程,所以在多核服务器上后者的性能理论上相对更高一些。然而,前面已经介绍过,Redis的性能已经足够优异,在绝大部分场景中其性能不会成为瓶颈,所以在使用时更应该关心的是二者在功能上的区别。Redis 3.0的推出标志着Memcached的几乎所有功能已成为Redis的子集。同时,Redis对集群的支持使得Memcached原有的第三方集群工具不再成为优势。因此,在新项目中使用Redis而不是Memcached将会是更好的选择。
作为缓存系统,Redis还可以限定数据占用的最大内存空间,在数据达到空间限制后可以按照一定的规则自动淘汰不需要的键。
除此之外,Redis的列表类型键可以用来实现队列系统,并且支持阻塞式读取,可以很容易地实现一个高性能的优先级队列。同时在更高层面上,Redis还支持“发布/订阅”的消息模式,可以基于此构建聊天室[2]等系统。
更有趣的是,Redis从4.0版本开始提供对模块(module)的支持。借助模块,用户可以高性能地基于Redis本身的核心能力扩展出更广泛的用途。以下是一些常见的模块:
(1)RediSearch模块提供了全文搜索功能;
(2)RedisGraph模块可以把Redis变成一个图数据库;
(3)RedisJSON为Redis增加了JSON数据类型;
(4)rediSQL可以让Redis运行SQL语句。
1.2.4 简单稳定
即使功能再丰富,如果使用起来太复杂也很难吸引人。Redis直观的存储结构使得通过程序与Redis交互十分简单。在Redis中使用命令来读写数据,命令之于Redis就相当于SQL语言之于关系数据库。例如在关系数据库中要获取posts表内id为1的记录的title字段的值,可以使用如下SQL语句实现:
SELECT title FROM posts WHERE id = 1 LIMIT 1
相应地,在Redis中要读取键名为post:1的哈希类型键的title字段的值,可以使用如下命令实现:
HGET post:1 title
其中,HGET就是一条命令。Redis提供了200多条命令(如图1-2所示),听起来很多,但是由于使用场景不同,每个使用场景中需要用到的命令不会很多,而且每条命令都很容易记忆。读完第3章你就会发现Redis的命令比SQL语言要简单很多。
图1-2 Redis官网提供了详细的命令文档
Redis提供了几十种不同编程语言的客户端库,这些库都很好地封装了Redis的命令,使得在程序中与Redis进行交互变得更容易。有些库还提供了可以将编程语言中的数据类型直接以相应的形式存储到Redis中(如将数组以列表类型直接存入Redis)的简单方法,使用起来非常方便。
Redis使用C语言开发,代码量只有几万行。这降低了用户通过修改Redis源代码来使之更适合自己的项目所需要的门槛。对于希望“榨干”数据库性能的开发者,这无疑具有强大的吸引力。
Redis是开源的,所以事实上Redis的开发者并非只有Salvatore Sanfilippo。截至目前,有数百名开发者为Redis贡献了代码。良好的开发氛围和严谨的版本发布机制使得Redis的稳定版本非常可靠,如此多的公司在项目中使用Redis也可以印证这一点。
[1] 这是一种符合第三范式的设计。事实上,还可以使用其他方式来实现标签系统。
[2] Redis的贡献者之一Pieter Noordhuis提供了一个使用该模式开发的聊天室的例子。