单击此处开始使用 Redis Enterprise。 Redis Enterprise 允许您在各地以任意规模使用任何实时数据。
你们的科学家只顾着考虑能否做到,却没有停下来思考是否应该这样做。——伊恩·马尔科姆博士,《侏罗纪公园
“最佳实践”在技术领域已成为一种惯用模式。当然,你可以使用给定的工具完成某件事,但这样做真的是一个好主意吗?关于此主题的一再出现表面了我们工具的灵活性。最佳实践非常适合初学者从一开始就学会正确的方法。问题在于,有时我们软件工程师对这些最佳实践的记忆不全面。其他时候,我们通过不阅读手册,而只是将方榫钉入圆孔中来完成我们需要做的工作,却未意识到我们无意中将游戏变为了困难模式。
因此,让我们采用另一种方法:不要关注最佳实践,而是关注最差实践。我们已经看到客户、开源用户乃至工具实现了一些模式,这些模式呼吁我们摇头表示不赞同。诚然,我们以前尚未集中整理过这类智慧,因此现在就让我们从七条 Redis“最差实践”开始。
根据我在网上看到的代码示例数量(实际上,几年前甚至可能包括我自己的某些示例),许多人懒得为其 Redis 实例设置密码。对于 Redis 当前版本,这种情况要想成为真正的最差实践,你必须在 redis.conf 中非常努力才能向整个互联网开放一个无密码的 Redis 实例。然而,较旧版本确实允许这样做。为什么不使用密码是一个糟糕的主意?没有密码,你的服务器会被找到。 一旦被找到,将可能发生各种恶作剧,从清除数据库到运行高复杂性命令暂停 Redis 进程,甚至修改文件(通过CONFIG SET/GET)。
TL;DR: 如果没有密码,你将被 h4x0r3d。
最佳实践替代:设置一个密码并使用 AUTH。
奇怪,KEYS是人们在 Redis 中学习到的第一批命令之一,尽管使用它(在生产环境中)很可怕。对于那些开悟到不了解 KEYS 的人来说,它会对给定数据库中的所有键(或模式)进行完整迭代。当然,这非常有用,尤其是对于调试来说,如果你只有少数几个键,它也不是什么大问题。但是,随着扩展,KEYS 会是一个隐藏的杀手。请考虑以下四点事实:
因此,在您有几十个键时,编写依赖于 KEYS 的应用程序是可以的——但此操作会随着越来越多的键而花费越来越长的时间。在此时,Redis 将忙于在数据库中遍历键。想象一下必须处理 4,294,967,295 个任何东西,您就会明白为什么它不会很快。最后,KEYS 是一个同步命令,因此生成所有这些键的响应(尤其是如果它们是大键)将需要一段时间,更不用说发送它的时间了。
TL;DR:Redis 会比您想象的更大,并且 KEYS 会长时间堵塞您的 Redis 服务器。
最佳实践备选方案:使用 SCAN,它会将迭代分散到多个调用中,不会一次性占用整个服务器。
Redis 作者 Salvatore Sanfilippo 曾称编号数据库是他设计 Redis 时犯下的最严重的错误。此设计选择是一个警示故事,说明创建看起来执行一项操作但实际上执行了另一项操作的内容时应该注意什么。值得庆幸的是,虽然在现实中这种情况变得越来越少见,但 Redis 仍然具备通过SELECT命令切换不同“数据库”的能力。每个数据库从密钥的角度都是孤立的。因此,数据库 0 上的密钥foo:bar可以与数据库 9 中的foo:bar完全不同。听起来不错,对吗?问题在于这些数据库没有任何别的隔离方式。在数据库 0 上运行键将仍然冻结数据库 9。实际上,这看起来就像你可以在每个数据库上运行独立的工作负载,但实际上它们根本就不独立。
倒霉,但这并不是最糟糕的做法,对吧?嗯,问题在于整个生态系统都不支持编号数据库。编号数据库棺材上的第一根钉子,而且可能是最致命的一根钉子是,任何集群系统(开源或 Redis Enterprise 集群)都不支持它们。实际上,你将永远无法离开 Redis 的单个节点。此外,一些模块也不支持编号数据库。
TL;DR:编号数据库并没有达到你的预期效果——而且会限制你的扩展能力。
最佳实践替代方案: 运行 Redis 的隔离实例——其开销很低,所以为什么不呢?如果你正在运行 Redis Enterprise,则默认情况下数据库是隔离的/多租户的。
这些命令属于一个有趣的类别:在很多时候都是无害且有用的,但在其他时候又成为令人痛恨的恶魔。Redis 中的哈希数据结构允许你在一个键下设置一系列字段/值对——HGETALL 是一个简单的命令,它让你一次检索哈希中的所有内容。这很好,因为在大多数情况下你可能最多只处理三位数的字段。同键一样,你可以有 232 个字段 和 每个哈希的值。 在大多数情况下,你远不需要这么多的字段,但在某些情况下,由于代码的性质(或逻辑错误),你可以累积大量字段和值,随着时间的推移不断增加字段的数量。然后你运行 HGETALL 并接收到数千个字段和值,每个字段和值可能高达 512MB,这意味着你遇到的问题实际上与 KEYS 相似。
事情在 LRANGE 中可能更糟。LRANGE 从给定范围内的列表中获取项目;要获取所有项目,LRANGE 0 -1 可以做到这一点。Redis 中的列表实际上是链接列表,这意味着每个元素都必须按顺序访问(以获取指向下一个元素的指针)。到目前为止,你可能已经猜到 232 个元素(每个最多 512MB)是最大值,并且你可以累积非常多的元素。如果你将列表用作队列,只需要一个工作线程离线几分钟,就可以使列表的大小迅速增加。
有序集合和集合也是大同小异。它们可以存储大量的的块数据,每个块都可能很大。当你请求所有数据块时,这可能需要一些时间。
也就是说:Redis 可以存储非常大的数据结构。除非你知道数量,否则预计结果数量为 232。
最佳实践替代方案:运行一个命令来检查数据结构的大小(HLEN用于哈希,LLEN用于列表,SCARD用于集合,以及ZCARD用于排序的集合)。
许多数据库使用 REST 的概念作为主接口——将普通的 HTTP 请求发送到一个包含以 POST 形式编码的参数的终结点。数据库获取信息并将其作为带有状态代码的响应返回,然后关闭连接。Redis 应以不同方式使用——连接应是持久的,您应该根据需要向长期连接发送请求。然而,善意的开发人员有时会创建一个连接,运行一个命令,然后关闭连接。虽然对每个命令打开和关闭连接在技术上可行,但它远非最佳,并且会不必要地降低 Redis 整体的性能。
使用 OSS 集群 API,可以根据需要由客户端维护与节点的连接,因此您会在任何给定的时间将多个连接打开到不同的节点。借助 Redis Enterprise,连接实际上是到一个代理,该代理负责处理集群级的连接的复杂性。
TL;DR:Redis 连接在无数次操作中都保持打开状态。
最佳实践替代方案:在多个命令期间保持您的连接打开。
Redis 可以轻松成为应用程序运营数据的核心,保存有价值且经常访问的信息。但是,如果您将访问集中到一些不断访问的数据上,就会产生所谓热键问题。在 Redis 集群中,键实际上决定了数据在集群中的存储位置。基于键的哈希,数据存储在一个唯一的首选位置。因此,当您一次又一次地访问单个键时,实际上是一次又一次地访问单个节点/分片。让我们换个说法——如果您有一个包含 99 个节点的集群,并且有一个每秒获得一百万个请求的键,那么这一百万个请求都会发到一个节点,而不会分布到其他 98 个节点。
Redis 甚至提供了查找热键所在位置的工具。将 redis-cli 与 –hotkeys 参数以及连接所需的任何其他参数一同使用
$ redis-cli --hotkeys
TL;DR:不要创建少量经常访问的键。
最佳实践选择:在可能的情况下,最好的防御措施就是避免造成此情况的开发模式。将数据写入多个位于不同分片中的密钥,这将允许您更频繁地访问相同的数据。
Redis 通常用作应用程序的主存储引擎。与将 Redis 用作缓存不同,将 Redis 用作主数据库需要两个额外功能才有效。任何主数据库都应真正具有高可用性。如果缓存宕机,则在通常情况下您的应用程序将处于停摆状态。如果主数据库宕机,您的应用程序也将宕机。同样,如果缓存宕机,并且您重启它时处于空状态,那也不是什么大问题。但是,对于主数据库而言,这将是一个大问题。Redis 可以轻松处理这些情况,但这通常需要不同于缓存运行的配置。
TL;DR:Redis 用作主数据库非常棒,但您必须通过启用正确功能来支持它。
最佳实践选择:对于开源 Redis,您需要设置 Redis Sentinel 以实现高可用性。在 Redis Enterprise 中,这是一个核心功能,您只需在创建数据库时将其启用即可。关于耐用性,Redis Enterprise 和开源 Redis 都通过 AOF 或快照提供耐用性,因此您的实例会按照您离开它们时的状态重新启动。
您已经有了它—— Redis 的七项最差实践。我们覆盖了现有的所有不良做法了吗?当然没有。关注我们的博客或 注册 Redis Watch 时事通讯以发现您绝对、肯定不想在 Redis 中执行的更多事情。
这篇文章让您汗流浃背,是因为您可能犯了这些最差实践中的一个(或七个)吗?在社交媒体上告诉我们。一如既往,我们乐于在 Twitter @Redis 上征求反馈。
观看我们最近关于购买与构建的 Tech Talk:Redis 开源与 Redis Enterprise 中的集群和预置!