注册
登录
新闻动态
其他科技
返回
每个开发人员都应该避免的 Redis 反模式
作者:
糖果
发布时间:
2024-03-27 06:10:19 (1天前)
来源:
redis.com/howtos/antipatterns/
开发人员不仅使用 Redis,他们还喜欢它。Stack Overflow 的 2021 年年度开发人员调查已连续第五年将 Redis 列为最受欢迎的数据库平台!但同样重要的是要了解 Redis 默认设置并不适合所有人。由于 Redis 的速度和性能,数以百万计的开发人员使用它,但是确保正确使用它很重要。 “反模式”基本上是指那些最初看起来很合适的实践和解决方案,但是当涉及到实现阶段时,它会使您的代码变得更加复杂。让我们看看要避免的主要 Redis 反模式: ##### 1. 在单个分片/Redis 实例上运行的大型数据库# 对于在单个分片/Redis 实例上运行的大型数据库,故障转移、备份和恢复可能需要更长的时间。因此,始终建议将分片保持在推荐大小。一般保守的经验法则是 25Gb 或 25K Ops/Second。 > 重要的 如果您有超过 25 GB 的数据和大量操作,Redis Enterprise 建议您进行分片。另一方面是如果你每秒有超过 25,000 次操作,那么分片可以提高性能。每秒操作次数更少,它也可以处理高达 50GB 的数据。 ##### 2. 每个请求一个连接# 每个操作打开一个连接都需要大量开销来建立和拆除 TCP 连接。作为开发人员,您有时可能会创建连接、运行命令并关闭连接。虽然每个命令打开和关闭连接在技术上是可行的,但它远非最佳且不必要地降低了 Redis 的整体性能。因此,建议使用连接池 (Jedis) 或具有响应式设计的 Redis 客户端 (Lettuce)。 使用 OSS Cluster API,与节点的连接由客户端根据需要维护,因此您可以在任何给定时间向不同节点打开多个连接。 > 重要的 使用 Redis Enterprise,连接实际上是到一个代理,它负责处理集群级别连接的复杂性。使用 Redis Enterprise,连接实际上是到一个代理,它负责处理集群级别连接的复杂性。 ###### 示例 #1 - redis-py # 让我们看一下使用连接池来管理到 Redis 服务器的连接的 redis-py。默认情况下,您创建的每个 Redis 实例将依次创建自己的连接池。您可以通过将已创建的连接池实例传递给 Redis 类的 connection_pool 参数来覆盖此行为并使用现有连接池。您可以选择这样做以实现客户端分片或对连接的管理方式进行细粒度控制。 >> >池= redis 。连接池(主机= 'localhost' ,端口= 6379 ,数据库= 0 ) >> > r = Redis 。Redis ( connection_pool =池) 了解有关 redis-py 的更多信息 ###### 示例 #2 - 生菜# Lettuce 提供通用连接池支持。Lettuce 连接被设计为线程安全的,因此一个连接可以在多个线程之间共享,并且默认情况下 Lettuce 连接会自动重新连接。虽然在大多数情况下连接池不是必需的,但它在某些用例中可能会有所帮助。Lettuce 提供通用的连接池支持。 RedisClient 客户端 = RedisClient.create(RedisURI.create(host, port)); GenericObjectPool
> pool = ConnectionPoolSupport .createGenericObjectPool(() -> client.connect(), new GenericObjectPoolConfig()); // 执行工作 尝试 (StatefulRedisConnection
connection = pool.borrowObject()) { RedisCommands
commands = connection.sync(); 命令.multi(); commands.set("key", "value"); 命令.set("key2", "value2"); 命令.exec(); } // 终止 池。关闭(); 客户端关闭(); 了解有关生菜的更多信息 ##### 3. 直接连接Redis实例# 对于大量客户端,重新连接洪水将能够简单地淹没单线程 Redis 进程并强制进行故障转移。因此,建议您使用正确的工具来减少与 Redis 服务器的打开连接数。 > 重要的 Redis Enterprise DMC 代理允许您通过充当代理来减少与缓存服务器的连接数。 还有其他 3rd 方工具,如Twemproxy。它是一个快速且轻量级的代理服务器,可让您减少与 Redis 服务器的打开连接数。它的构建主要是为了减少与后端缓存服务器的连接数。这与协议流水线和分片一起使您能够水平扩展分布式缓存架构。 ##### 4. 多个二级分片(Redis OSS)# Redis OSS 使用基于分片的仲裁。建议至少使用 3 个数据副本(每个主分片 2 个副本分片),以防止出现裂脑情况。简而言之,Redis OSS 通过拥有奇数个分片(主 + 2 个副本)解决了仲裁挑战。 > 重要的 Redis Enterprise 解决了奇数节点的仲裁问题。Redis Enterprise 避免了只有 2 个数据副本的裂脑情况,更具成本效益。此外,如果额外的、不必要的数据节点过于昂贵,所谓的“仅仲裁节点”可用于将集群增加到奇数个节点。Redis Enterprise 解决了奇数节点的仲裁问题。 ##### 5. 单机操作# 连续执行多个操作会增加连接开销。相反,请使用Redis Pipelining。流水线是在管道中发送多条消息而不等待每个消息的回复的过程 - 并且(通常)在回复进来时处理回复。 流水线完全是客户端实现。它旨在解决高网络延迟环境中的响应延迟问题。因此,通过网络发送命令和读取响应所花费的时间越少越好。这可以通过缓冲有效地实现。在将命令发送到服务器之前,客户端可能(或可能不)在 TCP 堆栈中缓冲命令(如其他答案中所述)。一旦它们被发送到服务器,服务器就会执行它们并在服务器端缓冲它们。流水线的好处是大大提高了协议性能。通过流水线获得的加速范围从连接到本地主机的五倍到最慢互联网连接的至少一百倍。 ##### 6. 缓存没有 TTL 的密钥# Redis 主要用作键值存储。可以在这些键上设置超时值。也就是说,超时到期会自动删除密钥。此外,当我们使用删除或覆盖密钥内容的命令时,它会清除超时。Redis TTL 命令用于以秒为单位获取密钥到期的剩余时间。TTL 返回具有超时的密钥的剩余生存时间。这种内省功能允许 Redis 客户端检查给定的键将继续成为数据集的一部分的秒数。键将累积并最终被驱逐。因此,建议在所有缓存键上设置 TTL。 ##### 7. 无尽的Redis复制循环# 当尝试通过慢速或饱和链接复制非常大的活动数据库时,由于持续更新,复制永远不会完成。因此,建议调整从属和客户端缓冲区以允许较慢的复制。查看这个详细的博客。 ##### 8. 热键# Redis 很容易成为您应用运行数据的核心,保存有价值且经常访问的信息。但是,如果您将访问集中到少数经常访问的数据,则会产生所谓的热键问题。在 Redis 集群中,密钥实际上决定了数据在集群中的存储位置。根据对该键的散列,数据存储在一个单一的主要位置。因此,当您一遍又一遍地访问单个密钥时,实际上是在一遍又一遍地访问单个节点/分片。换一种说法——如果你有一个由 99 个节点组成的集群,并且你有一个键可以在一秒钟内收到一百万个请求,那么所有这些百万请求都将发送到一个节点,而不是分散到其他 98 个节点上。 Redis 甚至提供工具来查找您的热键所在的位置。将 redis-cli 与 –hotkeys 参数与您需要连接的任何其他参数一起使用: $ redis-cli --hotkeys 如果可能,最好的防御是避免造成这种情况的发展模式。将数据写入驻留在不同分片中的多个键将允许您更频繁地访问相同的数据。 简而言之,具有每个客户端操作都可以访问的特定键。因此,建议使用散列算法对热键进行分片。您可以将策略设置为 LFU 并运行 redis-cli --hotkeys 来确定。 ##### 9. 使用按键命令# 在 Redis 中,KEYS 命令可用于对所有存储的键执行详尽的模式匹配。这是不可取的,因为在具有大量键的实例上运行它可能需要很长时间才能完成,并且会在此过程中减慢 Redis 实例的速度。在关系世界中,这相当于运行一个未绑定的查询(没有 WHERE 子句的 SELECT...FROM)。请谨慎执行此类操作,并采取必要措施确保您的租户不会在其应用程序代码中执行 KEYS 操作。使用 SCAN,它将迭代分散到许多调用中,而不是一次占用整个服务器。 > 重要的 按键名扫描键空间是一个非常慢的操作,运行时间复杂度为 O(N),其中 N 是键的数量。推荐使用RediSearch根据数据内容返回信息,而不是遍历key空间。 FT.SEARCH 命令“@make: Ford @model: explorer” 2SQL: SELECT * FROM orders WHERE make = Ford AND model = explorer" ##### 10. 运行 Ephemeral Redis 作为主数据库# Redis 通常用作应用程序的主要存储引擎。与使用 Redis 作为缓存不同,使用 Redis 作为主数据库需要两个额外的功能才能有效。任何主数据库都应该真正具有高可用性。如果缓存出现故障,那么您的应用程序通常处于电量不足状态。如果主数据库出现故障,您的应用程序也会出现故障。类似地,如果缓存出现故障并且您将其重新启动为空,那也没什么大不了的。但是,对于主数据库来说,这是一个大问题。Redis 可以轻松处理这些情况,但它们通常需要与作为缓存运行不同的配置。Redis 作为主数据库很棒,但您必须通过打开正确的功能来支持它。 > 重要的 使用 Redis 开源,您需要设置 Redis Sentinel 以实现高可用性。在 Redis Enterprise 中,这是一项核心功能,您只需在创建数据库时打开即可。至于持久性,Redis Enterprise 和开源 Redis 都通过 AOF 或快照提供持久性,以便您的实例以您离开它们的方式重新启动。 ##### 11. 将 JSON blob 存储在字符串中# 用多种语言编写的微服务可能不会以一致的方式编组/解组 JSON。将需要应用程序逻辑来锁定/监视原子更新的密钥。JSON 操作通常是一项计算成本非常高的操作。因此,建议使用 HASH 数据结构以及 RedisJSON 模块。 ##### 12. 在不考虑查询模式的情况下将表或 JSON 转换为 HASH # 唯一的查询机制是 SCAN,它需要读取数据结构并将过滤限制为 MATCH 指令。建议将表或 JSON 存储为字符串。使用 SET 或 SORTED SET 将索引分解为反向索引并指向字符串的键。在一个 Redis 实例中使用 SELECT 命令和多个数据库 Salvatore(Redis 的创建者)提到在一个 Redis 实例中使用 SELECT 和多个数据库是一种反模式。建议针对每个数据库需求使用专用的 Redis 实例。这在微服务架构中尤其如此,其中客户端应用程序可能会相互干扰(嘈杂的邻居、数据库设置/拆卸影响、维护、升级……) RedisTimeSeries 模块提供了与时间序列数据库的直接竞争。但是如果唯一的查询是基于排序的,那就是不必要的复杂性。因此,建议对每个值使用得分为 0 的 SORTED SET。附加值。或者使用时间戳作为基于时间的简单查询的分数
收藏
举报
1 条回复
动动手指,沙发就是你的了!
登录
后才能参与评论