dot Redis 8 已发布,并且它是开源的

了解更多

7 个 Redis 最差实践

点击此处开始使用 Redis Enterprise。 Redis Enterprise 让您能够在任何规模、任何地点处理任何实时数据。

你的科学家们如此专注于他们能否做到,以至于他们没有停下来思考他们是否应该这样做。——伊恩·马尔科姆博士,《侏罗纪公园

“最佳实践”已成为技术领域的常用说法。诚然,你可以用一个给定的工具做某件事,但这样做真的是个好主意吗?这个话题屡次出现,正说明了我们工具的灵活性。最佳实践对于初学者来说是学习正确做法的绝佳途径。问题在于,有时作为软件工程师,我们对这些最佳实践的记忆并不完美。还有些时候,我们通过不阅读手册,只是硬生生将方钉砸进圆孔,完成了我们需要做的事情,却没有意识到我们无意中在玩一个困难模式的游戏。 

所以让我们换一种方法:与其关注最佳实践,不如看看最差实践。我们见过客户、开源用户甚至工具实现了某些模式,这些模式简直让人想摇头表示不赞同。诚然,我们之前没有系统地总结过这类经验,所以现在就从 Redis 的七个“最差实践”开始吧。

1. 不设置密码

基于我在网上看到的大量代码示例(确实,可能甚至包括我几年前写的一些示例),很多人懒得为他们的 Redis 实例设置密码。要在当前版本的 Redis 中,这算得上一个真正的最差实践,你必须在 redis.conf 中非常努力地才能将一个无密码的 Redis 实例暴露给整个互联网。然而,较旧的版本确实允许这种做法。为什么放弃密码是个坏主意?没有密码,你的服务器会被发现。一旦被发现,各种恶意行为都可能发生,从清空数据库到运行高复杂度命令导致 Redis 停顿,甚至通过CONFIG SET/GET修改文件。 

太长不看:没有密码,你的服务器会被黑。

最佳实践替代方案:设置密码并使用AUTH。 

2. KEYS 命令

奇怪的是,KEYS是人们在 Redis 中最早学习的命令之一,尽管在生产环境中使用它非常糟糕。对于那些足够明智,甚至不知道 KEYS 的人来说,它会对给定数据库中的所有键(或模式)进行完整迭代。诚然,这在调试时可能很有用,而且如果只有少量键,问题也不大。然而,随着规模的增长,KEYS 是一个潜在的杀手。考虑四个事实:

  1. Redis(实际上)是单线程的 
  2. Redis 可以容纳 232 个键
  3. Redis Enterprise 限制键大小为 64KB,而 Redis 开源版本的限制为 512MB
  4. KEYS 是一个 O(n) 操作。

因此,当你只有几十个键时,编写依赖 KEYS 的应用程序是可以接受的——但随着键越来越多,这个操作所需的时间也会越来越长。在此期间,Redis 除了遍历数据库中的键外,什么也做不了。想象一下需要执行 4,294,967,295 次任何操作,你就能明白为什么它不会很快。最后,KEYS 是一个同步命令,因此构建所有这些键的响应——特别是如果它们是很大的键——将需要一段时间,更不用说通过网络发送所需的时间了。

太长不看:Redis 会比你预期的更大,KEYS 命令可能会长时间阻塞你的 Redis 服务器。

最佳实践替代方案:使用SCAN,它将迭代分散到多次调用中,而不会一次性占用整个服务器。

3. 编号数据库/SELECT 命令 

Redis 的作者 Salvatore Sanfilippo 曾称编号数据库是他设计 Redis 时犯下的最糟糕的错误。这个设计选择是一个警示故事,讲述了如何构建一个看起来做某事但实际上做另一件事的东西。幸运的是,虽然这种情况在实践中越来越少见,但 Redis 仍然提供了使用SELECT命令在不同“数据库”之间切换的能力。从键的角度来看,每个数据库是隔离的。因此,数据库 0 中的键foo:bar可以与数据库 9 中的foo:bar完全不同。这听起来相当不错,对吧?问题是这些数据库在其他方面并不隔离。在数据库 0 上运行 KEYS 仍然会冻结数据库 9。实际上,看起来你可以在每个数据库上运行独立的工作负载,但实际上,它们根本不独立。

这令人沮丧,但这还不算真正的最差实践,对吧?嗯,问题在于编号数据库在整个生态系统中并未得到很好的支持。编号数据库的第一个也是可能最致命的缺点是,它不受任何集群系统(无论是开源的还是 Redis Enterprise 集群)的支持。实际上,你将永远无法脱离单个 Redis 节点。此外,一些模块也不支持编号数据库。 

太长不看:编号数据库不像你想象的那样工作——而且会把你逼入扩展的死胡同。

最佳实践替代方案:运行独立的 Redis 实例——它的开销很低,何乐而不为?如果你使用 Redis Enterprise,数据库默认就是隔离/多租户的。

4. 使用 HGETALL, LRANGE, SMEMBERS, ZRANGE 获取无限制结果

这些命令属于一个有趣的类别:大多数时候有用且无害,但有时却是惹祸的魔鬼。Redis 中的 Hash 数据结构允许你在一个键下设置一系列的字段/值对——HGETALL是一个简单的命令,可以让你一次性检索 Hash 中的所有内容。这通常没问题,因为大多数时候你处理的字段数量最多只有几百个。像键一样,每个 Hash 可以有 232 个字段值。在大多数情况下,你不会有接近这个数量,但在某些情况下,由于你的代码特性(或逻辑错误),字段和值的数量会累积增加。然后你运行 HGETALL,收到成千上万个字段和值,每个可能高达 512MB,这意味着你遇到的问题与使用 KEYS 几乎相同。

使用LRANGE时情况可能更糟。LRANGE 按给定范围获取列表中的元素;要获取所有元素,LRANGE 0 -1即可做到。Redis 中的 List 实际上是链表,这意味着必须按顺序访问每个元素(以获取指向下一个元素的指针)。现在你可能已经猜到,最大元素数量是 232(每个元素最大 512MB),并且你可能会累积非常多的元素。如果你将 List 用作队列,即使只是让一个工作进程离线几分钟,也可能导致 List 的大小迅速增长。 

Sorted Sets 和 Sets 的情况也差不多。它们可以存储大量数据,每个数据项都可能非常大。当你请求获取所有数据时,这可能会花费大量时间。

太长不看:Redis 可以存储非常大的数据结构。除非你知道数量,否则请预期结果数量可能高达 232

最佳实践替代方案:运行一个命令来检查数据结构的大小(HLEN用于 Hash,LLEN用于 List,SCARD用于 Set,ZCARD用于 Sorted Set)。

5. 每个连接只发送一个请求

许多数据库使用 REST 作为主要接口的概念——向带有编码为 POST 参数的端点发送一个普通的 HTTP 请求。数据库获取信息并返回带有状态码的响应,然后关闭连接。Redis 的用法应该不同——连接应该是持久的,你应该根据需要向一个长期存活的连接发送请求。然而,善意的开发者有时会创建一个连接,运行一个命令,然后关闭连接。虽然为每个命令打开和关闭连接技术上可行,但这远非最佳实践,并且不必要地损害了 Redis 的整体性能。  

使用OSS Cluster API时,客户端会按需维护与节点的连接,因此在任何给定时间,你都会打开与不同节点的多个连接。对于 Redis Enterprise,连接实际上是连接到一个代理,该代理负责处理集群级别的连接复杂性。 

太长不看:Redis 连接设计用于在无数操作中保持开启。 

最佳实践替代方案:在多个命令之间保持连接开启。

6. 热键 (Hot keys)

Redis 很容易成为应用操作数据的核心,存储着有价值且频繁访问的信息。然而,如果你将访问集中在少数几个持续被访问的数据项上,就会产生所谓的“热键”问题。在 Redis 集群中,键实际上决定了数据在集群中的存储位置。数据会根据该键的哈希值存储在一个主要的单一位置。因此,当你反复访问同一个键时,实际上是在反复访问同一个节点/分片。换句话说——如果你有一个包含 99 个节点的集群,并且你有一个键在一秒钟内收到一百万个请求,所有这一百万个请求都将发送到同一个节点,而不会分散到其他 98 个节点。 

Redis 甚至提供了工具来查找热键的位置。使用 redis-cli 并带有 –hotkeys 参数,同时带上连接所需的其他参数

$ redis-cli --hotkeys

太长不看:不要创建少量频繁访问的键。 

最佳实践替代方案:如果可能,最好的防御是避免导致这种情况的开发模式。将数据写入驻留在不同分片中的多个键,将允许你更频繁地访问相同的数据。

7. 将短暂的 Redis 作为主数据库运行 

Redis 经常被用作应用的主要存储引擎。与将 Redis 用作缓存不同,将 Redis 用作主数据库需要两个额外的功能才能有效。任何主数据库都应该具有高可用性。如果缓存崩溃,通常只会导致你的应用处于“部分瘫痪”状态。但如果主数据库崩溃,你的应用也会随之崩溃。类似地,如果缓存崩溃并清空重启,这不是什么大问题。但对于主数据库来说,这是个大问题。Redis 可以轻松处理这些情况,但通常需要与作为缓存运行时不同的配置。 

太长不看:将 Redis 作为主数据库非常棒,但你必须通过启用正确的功能来支持它。

最佳实践替代方案:对于 Redis 开源版本,你需要设置 Redis Sentinel 来实现高可用性。在 Redis Enterprise 中,这是核心功能,你只需要在创建数据库时启用它即可。关于持久性,Redis Enterprise 和 Redis 开源版本都通过 AOF 或快照提供持久性,以便你的实例重启后恢复到你离开时的状态。

以上就是 Redis 的七个最差实践。我们是否涵盖了所有糟糕的实践?当然没有。请继续关注我们的博客或订阅 Redis Watch 时事通讯,了解更多你在 Redis 中绝对不应该做的事情。 

这篇文章是否让你感到冷汗直流,因为你可能犯了其中一个(或七个)最差实践?请在社交媒体上告诉我们。一如既往,我们喜欢在 Twitter 上接收反馈 @Redis

想了解更多?

观看我们最近关于“购买还是自建:Redis 开源版本与 Redis Enterprise 中的集群与配置”的技术演讲! 

Security and Asset Management Tech Talk