很难大规模操作有状态的分布式系统,redis 也不例外。托管数据库通过承担大部分繁重工作使生活变得更轻松。但是您仍然需要一个健全的架构并在服务器(Redis)和客户端(应用程序)上应用最佳实践。
本博客涵盖了一系列与 Redis 相关的最佳实践、提示和技巧,包括集群可扩展性、客户端配置、集成、指标等。虽然我会不时引用Amazon MemoryDB和ElastiCache for Redis,但大多数(如果不是全部) ) 一般适用于 Redis 集群。
无论如何,这并不意味着是一个详尽的清单。我只是选择了十,因为它是一个不错的、有益健康的数字!
让我们深入了解一下您在扩展 Redis 集群方面有哪些选择。
1. 可扩展性选项
您可以放大或缩小:
扩展(垂直)- 您可以增加单个节点/实例的容量,例如从Amazon EC2db.r6g.xlarge类型升级到db.r6g.2xlarge
Scaling Out (Horizontal) - 您可以向集群添加更多节点
向外扩展的需求可能是由几个原因驱动的。
如果您需要处理读取繁重的工作负载,您可以选择添加更多副本节点。这适用于 Redis 集群设置(如MemoryDB)或非集群主副本模式,例如ElastiCache with cluster mode disabled的情况。
如果您想增加写入容量,您会发现自己受到主副本模式的限制,应该选择基于 Redis 集群的设置。您可以增加集群中的分片数量 - 这是因为只有主节点可以接受写入,并且每个分片只能有一个主节点。
这还具有增加整体高可用性的额外好处。
图 1:Redis(已禁用集群模式)和 Redis(已启用集群模式)集群 – ElastiCache for Redis 文档
2. 扩展集群后,最好使用这些副本!
大多数 Redis 集群客户端(包括 )的默认行为redis-cli是将所有读取重定向到主节点。如果您添加了只读副本来扩展读取流量,它们将处于空闲状态!
您需要切换到READONLY模式以确保副本处理所有读取请求,而不仅仅是被动参与者。确保正确配置您的 Redis 客户端 - 这将因客户端和编程语言而异。
例如,在Go Redis 客户端中,您可以设置ReadOnly为true:
client := redis.NewClusterClient(
&redis.ClusterOptions{
Addrs: []string{clusterEndpoint},
ReadOnly: true,
//..other options
})
为了进一步优化,您还可以使用RouteByLatency或RouteRandomly,这两个都自动开启ReadOnly模式。
您可以参考JAVA 客户端(例如 Lettuce)的工作原理
3. 使用只读副本时要注意一致性特征
您的应用程序有可能从副本中读取过时的数据——这就是最终一致性。由于主副本节点复制是异步的,因此您发送到主节点的写入器可能尚未反映在只读副本中。当您拥有大量只读副本(尤其是跨多个可用区)时,可能会出现这种情况。如果这对您的用例来说是不可接受的,那么您也必须求助于使用主节点进行读取。
MemoryDB 或 ElastiCache for Redis 中的ReplicationLag 指标可用于检查副本在应用来自主节点的更改方面落后多长时间(以秒为单位)。
那么强一致性呢?
在这种情况下MemoryDB,来自主节点的读取是强一致的。这是因为客户端应用程序仅在写入(到主节点)写入持久多可用区事务日志后才会收到成功的写入确认。
4. 请记住,您可以影响密钥在 Redis 集群中的分布方式
Redis 没有使用一致性哈希(像许多其他分布式数据库一样),而是使用哈希槽的概念。总共有16384槽,为集群中的每个主节点分配一定范围的哈希槽,每个键属于特定的哈希槽(从而分配给特定节点)。如果键属于不同的哈希槽,则在 Redis 集群上执行的多键操作将无法进行。
但是,您并非完全受集群的支配!可以通过使用hashtags来影响键的位置。因此,您可以确保特定键具有相同的哈希槽。例如,如果您将客户 ID 的订单存储42在HASHnamedcustomer:42:orders中,并将客户资料信息存储在 中customer:42:profile,您可以使用花括号{}来定义将被散列的特定子字符串。在这种情况下,我们的键是{customer:42}:orders和{customer:42}:profile-{customer:42}现在驱动哈希槽的放置。现在我们可以确信这两个键都在同一个哈希槽中(因此是同一个节点)。
5. 您是否考虑过缩小(后退)?
您的应用程序很成功,它有很多用户和流量。你扩展了集群,事情仍然很顺利。惊人的!
但是,如果您需要缩减规模怎么办?
在执行此操作之前,您需要注意一些事项:
每个节点上是否有足够的可用内存?
这可以在非高峰时段进行吗?
它将如何影响您的客户端应用程序?
在此阶段您可以监控哪些指标?(例如CPUUtilization,CurrConnections等等)
请参阅MemoryDb for Redis 文档中的一些最佳实践,以更好地规划扩展。
6. 当事情出错时......
面对现实吧,失败是令人羡慕的。重要的是你是否为他们做好了准备。对于您的 Redis 集群,需要考虑以下几点:
您是否测试过您的应用程序/服务在遇到故障时的行为?如果没有,请做!借助 MemoryDB 和 ElastiCache for Redis,您可以利用故障转移 API模拟主节点故障并触发故障转移。
你有副本节点吗?如果您只有一个带有单个主节点的分片,那么如果该节点发生故障,您肯定会停机。
你有多个分片吗?如果您只有一个分片(主分片和副本分片),则在该分片的主节点故障的情况下,集群将无法接受任何写入。
您的分片是否跨越多个可用区?如果您有跨多个 AZ 的分片,您将更好地准备应对 AZ 故障。
在所有情况下,MemoryDB确保在节点更换或故障转移期间不会丢失数据
7. 无法连接Redis,求助!
Tl;DR:可能是网络/安全配置
这是一直困扰人们的事情!使用MemoryDB和ElastiCache,您的Redis 节点位于 VPC 中。如果您将客户端应用程序部署到AWS Lambda、EKS、ECS、App Runner等计算服务,则需要确保您拥有正确的配置 - 特别是在 VPC 和安全组方面。
这可能因您使用的计算平台而异。例如,您如何配置 Lambda 函数以访问 VPC 中的资源与 App Runner 的操作方式(通过VPC 连接器)甚至 EKS(尽管从概念上讲,它们是相同的)略有不同。
8. Redis 6 自带访问控制列表 - 使用它们!
没有理由不对 Redis 集群应用身份验证(用户名/密码)和授权(基于 ACL 的权限)。MemoryDB符合 Redis 6 并支持 ACL。但是,为了符合较旧的 Redis 版本,它为每个帐户配置一个默认用户(使用用户名default)和一个名为 的不可变 ACL open-access。如果您创建MemoryDB集群并将其与此 ACL 关联:
客户端无需身份验证即可连接
客户端可以在任何键上执行任何命令(也没有权限或授权)
作为最佳实践:
定义显式 ACL
添加用户(连同密码),以及
根据您的安全要求配置访问字符串。
您应该监控身份验证失败。例如,MemoryDB 中的AuthenticationFAIlures指标为您提供失败的身份验证尝试总数 - 对此设置警报以检测未经授权的访问尝试。
不要忘记周边安全
如果您已经TLS在服务器上进行了配置,请不要忘记在您的客户端中也使用它!例如,使用 Go Redis:
client := redis.NewClusterClient(
&redis.ClusterOptions{
Addrs: []string{clusterEndpoint},
TLSConfig: &tls.Config{MaxVersion: tls.VersionTLS12},
//..other options
})
不使用它可能会给你的错误不够明显(例如泛型i/o timeout)并使事情难以调试 - 这是你需要小心的事情。
9.有些事情你不能做
作为托管数据库服务,MemoryDB或ElastiCache 限制对某些 Redis 命令的访问。例如,您不能使用与CLUSTER相关的命令的子集,因为集群管理(规模、分片等)由服务本身承担。
但是,在某些情况下,您可能会找到替代方案。以监控运行缓慢的查询为例。虽然您无法latency-monitor-threshold使用CONFIG SET进行配置,但您可以slowlog-log-slower-than在参数组中设置设置,然后使用slowlog get它进行比较。
10.使用连接池
您的 Redis 服务器节点(即使是功能强大的节点)资源有限。其中之一是能够支持一定数量的并发连接。大多数 Redis 客户端都提供连接池作为有效管理与 Redis 服务器的连接的一种方式。重用连接不仅有利于您的 Redis 服务器,而且由于开销减少,客户端性能也得到了提高——这在大容量场景中至关重要。
ElastiCache 提供了一些您可以跟踪的指标:
CurrConnections:客户端连接数(不包括只读副本)
NewConnections:特定时间段内服务器接受的连接总数。
11.(奖励)使用适当的连接模式
这一点很明显,但我还是要说出来,因为这是我目睹人们犯的最常见的“入门”错误之一。
您在客户端应用程序中使用的连接模式取决于您是使用独立的 Redis 设置还是 Redis 集群(很可能)。大多数 Redis 客户端对它们进行了明确的区分。例如,如果您使用启用了集群模式的Go Redis 客户端MemoryDB),则Elasticache需要使用NewClusterClient(而不是NewClient):
redis.NewClusterClient(&redis.ClusterOptions{//....})
有趣的是,有一个更加灵活的 UniversalClient 选项(在撰写本文时,这是在 Go Redis v9 中)
如果你没有使用正确的连接模式,你会得到一个错误。但有时,根本原因会隐藏在一般错误消息的后面——因此您需要保持警惕。
结论
您所做的架构选择最终将取决于您的特定需求。我鼓励您浏览以下博客文章,以更深入地了解 MemoryDB 和 ElastiCache for Redis 的性能特征,以及它们如何影响设计解决方案的方式:
优化 Amazon ElastiCache 和 MemoryDB 的 Redis 客户端性能
最佳实践:Redis 客户端和适用于 Redis 的 Amazon ElastiCache
测量 Amazon MemoryDB for Redis 的数据库性能
随意分享您的 Redis 提示、技巧和建议。在那之前,快乐的建筑!