Troubleshooting - Amazon ElastiCache for Redis
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅中国的 Amazon Web Services 服务入门

Troubleshooting

在排除 ElastiCache 的持续连接问题时,必须核查以下项目:

安全组

安全组是虚拟防火墙,保护您的 ElastiCache 客户端(EC2 实例、Amazon Lambda 函数、Amazon ECS 容器等)和 ElastiCache 集群。安全组是有状态的,也就是说在允许传入或传出流量后,对该流量所做的响应将在该特定安全组的上下文中自动获得授权。

有状态功能要求安全组跟踪所有已授权的连接,而且对跟踪的连接有限制。如果达到该限制,新连接将会失败。有关如何识别是否在客户端或 Elasticache 侧已达到该限制的帮助,请参阅故障排除部分。

您可以将单个安全组同时分配给客户端和 ElastiCache 集群,也可以为每个安全组分配单个安全组。

对于这两种情况,您都需要允许 ElastiCache 端口上来自源的 TCP 出站流量,并允许同一端口上到 ElastiCache 的入站流量。Memcached 的默认端口为 11211,Redis 的默认端口为 6379。默认情况下,安全组允许所有出站流量。在这种情况下,只需要目标安全组中的入站规则。

有关更多信息,请参阅用于访问 Amazon VPC 中 ElastiCache 集群的访问模式

网络 ACL

网络访问控制列表 (ACL) 是无状态规则。必须在入站和出站两个方向上都允许流量,才能成功。网络 ACL 将分配给子网,而不是特定资源。相同的 ACL 可以同时分配给 ElastiCache 和客户端资源,尤其是当它们位于同一子网中时。

默认情况下,网络 ACL 允许所有流量。但可对它们自定义,以拒绝或允许流量。此外,ACL 规则的评估是按顺序进行的,也就是说,匹配流量的编号最小的规则将允许或拒绝该流量。允许 Redis 流量的最低配置为:

客户端网络 ACL:

  • 入站规则:

  • 规则编号:最好低于所有拒绝规则;

  • 类型:自定义 TCP 规则;

  • Protocol:TCP

  • 端口范围:1024 – 65535

  • 来源:0.0.0.0/0(或为 ElastiCache 集群子网创建单独的规则)

  • 允许/拒绝:允许

  • 出站规则:

  • 规则编号:最好低于所有拒绝规则;

  • 类型:自定义 TCP 规则;

  • Protocol:TCP

  • 端口范围:6379

  • 来源:0.0.0.0/0(或 ElastiCache 集群子网。请记住,在发生故障转移或扩缩集群时,使用特定 IP 可能会产生问题)

  • 允许/拒绝:允许

ElastiCache 网络 ACL:

  • 入站规则:

  • 规则编号:最好低于所有拒绝规则;

  • 类型:自定义 TCP 规则;

  • Protocol:TCP

  • 端口范围:6379

  • 来源:0.0.0.0/0(或为 ElastiCache 集群子网创建单独的规则)

  • 允许/拒绝:允许

  • 出站规则:

  • 规则编号:最好低于所有拒绝规则;

  • 类型:自定义 TCP 规则;

  • Protocol:TCP

  • 端口范围:1024 – 65535

  • 来源:0.0.0.0/0(或 ElastiCache 集群子网。请记住,在发生故障转移或扩缩集群时,使用特定 IP 可能会产生问题)

  • 允许/拒绝:允许

有关更多信息,请参阅网络 ACL

路由表

与网络 ACL 类似,每个子网可以具有不同的路由表。如果客户端和 ElastiCache 集群位于不同的子网中,请确保它们的路由表允许它们相互访问。

环境越复杂(涉及多个 VPC、动态路由或网络防火墙),排除问题可能会变得越难。请参阅 网络连接验证 以确认您的网络设置是否合适。

DNS 解析

ElastiCache 提供基于 DNS 名称的服务终端节点。可用的端点包括 ConfigurationPrimaryReaderNode 端点。有关更多信息,请参阅查找连接终端节点

在故障转移或集群修改的情况下,与端点名称关联的地址可能会发生变化,并将自动更新。

自定义 DNS 设置(即不使用 VPC DNS 服务)可能不知道 ElastiCache 提供的 DNS 名称。确保您的系统可以使用类似 dig(如下所示)或 nslookup 的工具来解析 ElastiCache 端点。

$ dig +short example.xxxxxx.ng.0001.use1.cache.amazonaws.com example-001.xxxxxx.0001.use1.cache.amazonaws.com. 1.2.3.4

您还可以通过 VPC DNS 服务强制进行名称解析:

$ dig +short example.xxxxxx.ng.0001.use1.cache.amazonaws.com @169.254.169.253 example-001.tihewd.0001.use1.cache.amazonaws.com. 1.2.3.4

通过服务器端诊断识别问题

ElastiCache 引擎中的 CloudWatch 指标和运行时信息是识别连接问题潜在来源的常见来源或信息。良好的分析通常从以下项目开始:

  • CPU 使用率:Redis 是一个多线程应用程序。但是,每个命令的执行都发生在一个(主)线程中。为此,ElastiCache 提供了指标 CPUUtilizationEngineCPUUtilizationEngineCPUUtilization 提供专用于 Redis 进程的 CPU 使用率,而 CPUUtilization 提供所有 vCPU 的使用率。具有多个 vCPU 的节点通常具有不同的 CPUUtilizationEngineCPUUtilization 值,第二个值通常更高。高 EngineCPUUtilization 可能是由于请求数量增加或需要大量 CPU 时间才能完成的复杂操作引起的。您可以通过以下方式标识两者:

    • 增加的请求数:检查与 EngineCPUUtilization 模式匹配的其他指标的增加。有用的指标包括:

      • CacheHitsCacheMisses:成功的请求数量或在缓存中找不到有效项目的请求的数量。如果未命中与命中之比较高,则应用程序会因无效的请求浪费时间和资源。

      • SetTypeCmdsGetTypeCmds:这些与 EngineCPUUtilization 相关的指标可以帮助理解写入请求(由 SetTypeCmds 衡量)还是读取请求(由 GetTypeCmds 衡量)的负载明显更高。如果负载主要是读取,则使用多个只读副本可以在多个节点之间平衡请求,并将主节点留出用于写入。在已禁用集群模式的集群中,使用只读副本可以通过使用 ElastiCache 读取器终端节点在应用程序中创建额外连接配置来完成。有关更多信息,请参阅查找连接终端节点。读取操作必须提交到此额外连接。写入操作将通过常规主端点完成。在已启用集群模式的情况下,建议使用支持本机只读副本的库。使用正确的标记,库将能够自动发现集群拓扑、副本节点,通过 READONLY Redis 命令启用读取操作,然后将读取请求提交到副本。

    • 增加的连接数:

      • CurrConnectionsNewConnectionsCurrConnection 是数据点集合时已建立的连接数,而 NewConnections 显示的是在期间内创建的连接数。

        创建和处理连接意味着大量的 CPU 开销。此外,创建新连接所需的 TCP 三向握手会对整体响应时间产生负面影响。

        每分钟有数千个 NewConnections 的 ElastiCache 节点表明连接仅由几个命令创建和使用,这不是最佳做法。最佳做法是保持已建立连接并将其重复用于新操作。当客户端应用程序支持并正确实现连接池或持久连接时,可采用此最佳做法。使用连接池时,currConnections 数量没有很大的变化,NewConnections 应该尽可能的低。Redis 通过少量的当前连接提供最佳性能。将当前连接保持为数十或数百个的顺序,可最大限度地减少支持单独连接(如客户端缓冲区和 CPU 周期)的资源使用,以便为连接提供服务。

    • 网络吞吐量:

      • 确定带宽:ElastiCache 节点的网络带宽与节点大小是成比例的。由于应用程序具有不同的特征,因此结果可能会因工作负载而异。例如,小请求比率较高的应用程序对 CPU 使用率的影响往往大于网络吞吐量,而较大的密钥则会导致更高的网络利用率。因此,建议使用实际工作负载测试节点,以便更好地了解限制。

        模拟应用程序的负载可以提供更准确的结果。但是,通过基准工具可以很好地了解限制。

      • 对于主要是读取请求的情况,使用副本进行读取操作将减轻主节点上的负载。如果使用场景主要是写入,则使用许多副本将增加网络使用率。对于写入主节点的所有字节,有 N 个字节将被发送到副本,N 为副本数。对于写入密集型工作负载,最佳做法是在启用了集群模式的情况下使用 ElastiCache of Redis,这样写入可以跨多个分区得到平衡,或者纵向扩展到具有更多网络功能的节点类型。

      • CloudWatch 指标 NetworkBytesInNetworkBytesOut 分别提供进入或离开节点的数据量。ReplicationBytes 是专用于数据复制的流量。

      有关更多信息,请参阅 网络相关限制

    • 复杂命令:Redis 命令在单个线程上提供,这意味着按顺序处理请求。单个慢速命令可能会影响其他请求和连接,最终导致超时。对多个值、密钥或数据类型进行操作的命令的使用必须仔细完成。根据参数数量或其输入或输出值的大小,可以阻止或终止连接。

      一个显著例子就是 KEYS 命令。此命令扫描整个密钥空间来搜索给定模式,并在其执行过程中阻止其他命令的执行。Redis 使用“Big O”符号来描述其命令的复杂性。

      密钥命令具有 O (N) 时间复杂性,N 表示数据库中的密钥数。因此,密钥数越大,命令的速度就越慢。KEYS 可能会以不同的方式制造麻烦:如果未使用搜索模式,则该命令会返回所有可用的密钥名称。在含有数千或百万个项目的数据库中,这将会创建大量输出并充满网络缓冲区。

      如果使用了搜索模式,则只有匹配该模式的密钥才会返回到客户端。但是,引擎仍然会扫描整个密钥空间来搜索它,并且完成命令的时间将是相同的。

      KEYS 命令的一个替代选择是 SCAN 命令。此命令会遍历密钥空间并限制特定数量项目中的迭代,避免引擎上的长时间阻塞。

      扫描具有 COUNT 参数,用于设置迭代块的大小。默认值为 10(每次迭代 10 个项目)。

      取决于数据库中的项目数,较小的 COUNT 值数据块将需要更多的迭代才能完成全面扫描,而且较大的值将使引擎在每次迭代中处于繁忙状态。虽然小计数值将使 SCAN 在大数据库变慢,较大的值可能会导致出现 KEYS 中提及的相同问题。

      例如,运行 SCAN 命令(计数值为 10)将需要在具有 100 万个密钥的数据库上进行 10 万次重复操作。如果平均网络往返时间为 0.5 毫秒,则传输请求将耗时大约 5 万毫秒(50 秒)。

      另一方面,如果计数值为 100,0000,则需要一次迭代,并且传输它只需要 0.5 毫秒。但是,在命令完成扫描所有密钥空间之前,该引擎将完全阻止其他操作。

      除了 KEYS 之外,其他几个命令如果使用不当也可能会有害。要查看所有命令及其各自的时间复杂度的列表,请转到 https://redis.io/commands

      潜在问题的示例:

      • Lua 脚本:Redis 提供了嵌入式 Lua 解释器,允许在服务器端执行脚本。Redis 上的 Lua 脚本在引擎级别执行,而且根据定义,其具有原子性,这意味着在脚本执行过程中不允许运行其他命令或脚本。Lua 脚本提供了直接在 Redis 引擎上运行多个命令、决策算法、数据解析和其他操作的可能性。虽然脚本的原子性和分载应用程序的可能性很诱人,但必须小心使用脚本,并且仅用于小型操作。在 ElastiCache 上,Lua 脚本的执行时间限制为 5 秒。未写入密钥空间的脚本将在 5 秒后自动终止。为了避免数据损坏和不一致,如果脚本执行在 5 秒内未完成并且在执行过程中有任何写入,则节点将进行故障转移。事务是保证 Redis 中多个相关密钥修改的一致性的替代方案。事务允许执行一个命令块,监视现有密钥以进行修改。如果任何受监视的密钥在事务完成之前发生了更改,则会放弃所有修改。

      • 批量删除项目:DEL 命令接受多个参数,这些参数是要删除的密钥名称。删除操作是同步的,如果参数列表很大,或者包含大列表、集合、排序集或哈希(包含多个子项的数据结构),则需要大量 CPU 时间。换句话说,如果具有许多元素,那么即使删除单个密钥也可能需要相当长的时间。DEL 的替代项选择为 UNLINK,这是自 Redis 4 以来可用的异步命令。UNLINK 必须尽可能优先于 DEL。从 ElastiCache for Redis 6x 开始,lazyfree-lazy-user-del 参数使 DEL 命令在启用时的行为类似于 UNLINK。有关更多信息,请参阅 Redis 6.x 参数更改

      • 对多个密钥进行操作的命令:DEL 在先前是作为接受多个实际参数的命令提起,其执行时间直接与之成正比。但是,Redis 提供了更多工作原理类似的命令。例如,MSETMGET 允许一次插入或检索多个字符串键。使用它们可能有助于降低多个单独的 SETGET 命令固有的网络延迟。但是,参数列表过大会影响 CPU 使用率。

        虽然仅仅 CPU 使用率并不是导致连接问题的原因,但是通过多个密钥花费过多时间处理单个或少量命令可能会导致其他请求失败,并增加总体 CPU 使用率。

        密钥的数量及其大小将影响命令的复杂性,从而影响完成时间。

        其他可对多个密钥进行操作的命令示例:HMGETHMSETMSETNXPFCOUNTPFMERGESDIFFSDIFFSTORESINTERSINTERSTORESUNIONSUNIONSTORETOUCHZDIFFZDIFFSTOREZINTERZINTERSTORE

      • 对多种数据类型进行操作的命令:Redis 还提供针对一个或多个密钥执行操作的命令,无论其数据类型如何。ElastiCache for Redis 提供了指标 KeyBasedCmds 来监控此类命令。此指标汇总了以下命令在所选时间段内的执行情况:

        • O(N) 复杂性:

          • KEYS

        • O(1)

          • EXISTS

          • OBJECT

          • PTTL

          • RANDOMKEY

          • TTL

          • TYPE

          • EXPIRE

          • EXPIREAT

          • MOVE

          • PERSIST

          • PEXPIRE

          • PEXPIREAT

          • UNLINK (O(N) 来回收内存。但是,内存回收任务发生在一个单独的线程中,并且不会阻塞引擎

        • 根据数据类型不同的复杂性时间:

          • DEL

          • DUMP

          • RENAME 被认为是一个具有 O(1) 复杂性的命令,但在内部执行 DEL。执行时间将根据重命名密钥的大小而变。

          • RENAMENX

          • RESTORE

          • SORT

        • 大哈希:哈希是一种数据类型,允许单个密钥和多个键值子项目。每个哈希可以存储 4,294,967,295 个项目,并且对大哈希执行的操作可能会变得昂贵。类似于 KEYS,哈希具有 HKEYS 命令,该命令具有 O(N) 时间复杂度,N 表示哈希中的项目数。HSCAN 必须优先于 HKEYS 来避免长时间运行的命令。HDELHGETALLHMGETHMSETHVALS 是应谨慎用于大哈希的命令。

      • 其他大数据结构:除了哈希之外,其他数据结构可能是 CPU 密集型的。集、列表、排序集和 Hyperloglog 也可能需要相当长的时间来处理,具体取决于它们的大小和使用的命令。有关这些命令的更多信息,请参阅 https://redis.io/commands

网络连接验证

查看与 DNS 解析、安全组、网络 ACL 和路由表相关的网络配置后,可以使用 VPC Reachability Analyzer 和系统工具验证连接性。

Reachability Analyzer 将测试网络连接并确认是否满足所有要求和权限。对于以下测试,您需要 VPC 中可用的 ElastiCache 节点之一的 ENI ID(弹性网络接口标识)。您可以执行以下操作来查找:

  1. 转到 https://console.aws.amazon.com/ec2/v2/home?#NIC:

  2. 按照您的 Elasticache 集群名称或之前从 DNS 验证获得的 IP 地址筛选接口列表。

  3. 记下或以其他方式保存 ENI ID。如果显示了多个接口,请查看说明以确认它们属于正确的 ElastiCache 集群,然后选择其中一个接口。

  4. 继续执行下一步骤。

  5. 访问网址 https://console.aws.amazon.com/vpc/home?#ReachabilityAnalyzer 创建分析路径,然后选择以下选项:

    • Source Type(源类型):如果您的 ElastiCache 客户端在 Amazon EC2 实例上运行,选择 instance(实例);如果使用的是其他服务(例如具有 awsvpc 网络的 Amazon Fargate Amazon ECS、Amazon Lambda 等),以及相应的资源 ID(EC2 实例或 ENI ID),选择 Network Interface(网络接口)

    • Destination Type(目的地类型):选择 Network Interface(网络接口),然后在列表中选择 ElastiCache ENI

    • Destination port(目的地端口):为 ElastiCache for Redis 指定 6379,或为 ElastiCache for Memcached 指定 11211。这些端口是使用默认配置定义的端口,本示例假定它们未更改。

    • Protocol:TCP

创建分析路径并等待结果。如果状态为无法访问,请打开分析详细信息并查看 Analysis Explorer,了解请求被阻止的详细信息。

如果可到达性测试通过,请继续进行系统级别的验证操作。

要验证 ElastiCache 服务端口上的 TCP 连接,请执行以下操作:在 Amazon Linux 上,Nping 在软件包 nmap 中可用,并可以测试 ElastiCache 端口上的 TCP 连接,以及提供网络往返时间来建立连接。以此来验证 ElastiCache 集群的网络连接和当前延迟,如下所示:

$ sudo nping --tcp -p 6379 example.xxxxxx.ng.0001.use1.cache.amazonaws.com Starting Nping 0.6.40 ( http://nmap.org/nping ) at 2020-12-30 16:48 UTC SENT (0.0495s) TCP ... (Output suppressed ) Max rtt: 0.937ms | Min rtt: 0.318ms | Avg rtt: 0.449ms Raw packets sent: 5 (200B) | Rcvd: 5 (220B) | Lost: 0 (0.00%) Nping done: 1 IP address pinged in 4.08 seconds

默认情况下,nping 会发送 5 个探测器,探测器之间的延迟为 1 秒。您可以使用选项“-c”来增加探测器的数量,并使用“--delay”来更改发送新测试的时间。

如果带有 nping 的测试失败而 VPC Reachability Analyzer 测试通过,请您的系统管理员查看可能基于主机的防火墙规则、非对称路由规则或操作系统级别的任何其他可能的限制。

在 ElastiCache 控制台上,在 ElastiCache 集群详细信息中检查是否已启用 Encryption in-transit(传输中加密)。如果传输中加密已启用,请使用以下命令确认是否可以建立 TLS 会话:

openssl s_client -connect example.xxxxxx.use1.cache.amazonaws.com:6379

如果连接和 TLS 协商成功,则预计会有大量输出。检查最后一行中可用的返回代码,该值必须为 0 (ok)。如果 openssl 返回不同的内容,请在 https://www.openssl.org/docs/man1.0.2/man1/verify.html#DIAGNOSTICS 中检查错误原因。

如果所有基础设施和操作系统测试都通过,但您的应用程序仍然无法连接到 ElastiCache,请检查应用程序配置是否符合 ElastiCache 设置。常见的错误有:

  • 您的应用程序不支持 ElastiCache 集群模式,而 ElastiCache 启用了集群模式;

  • 您的应用程序不支持 TLS/SSL,而 ElastiCache 启用了传输中加密功能;

  • 应用程序支持 TLS/SSL,但没有正确的配置标记或受信任的证书颁发机构;

网络相关限制

  • 最大连接数:同时连接的数量有硬限制。每个 ElastiCache 节点允许跨所有客户端同时进行 65000 个连接。可以通过 CloudWatch 上的 CurrConnections 指标监控此限制。但是,客户端也有出站连接限制。在 Linux 上,使用以下命令检查允许的临时端口范围:

    # sysctl net.ipv4.ip_local_port_range net.ipv4.ip_local_port_range = 32768 60999

    在前面的示例中,将允许 28231 个从同一个源连接到同一目的地 IP(ElastiCache 节点)和端口的连接。以下命令展示了特定 ElastiCache 节点 (IP 1.2.3.4) 具有的连接数量:

    ss --numeric --tcp state connected "dst 1.2.3.4 and dport == 6379" | grep -vE '^State' | wc -l

    如果数量过高,您的系统可能会因尝试处理连接请求而变得过载。建议考虑实施连接池或持久连接等技术,以更好地处理连接。尽可能配置连接池以将最大连接数限制为几百个。此外,建议采用退避逻辑来处理超时或其他连接异常,以避免在出现问题时出现连接损失。

  • 网络流量限制:检查以下适用于 Redis 的 CloudWatch 指标来确定 ElastiCache 节点上可能达到的网络限制:

    • NetworkBandwidthInAllowanceExceeded/NetworkBandwidthOutAllowanceExceeded:由于吞吐量超过了聚合带宽限制而形成的网络数据包。

      请注意,写入主节点的每个字节都将被复制到 N 个副本,N 代表副本的数量。具有小节点类型、多个副本和密集型写入请求的集群可能无法应对复制积压。对于这种情况,最佳做法是纵向扩展(更改节点类型)、横向扩展(在已启用集群模式的集群中添加分区)、减少副本数量或最大程度减少写入次数。

    • NetworkConntrackAllowanceExceeded:由于超过了分配给节点的、跨所有安全组跟踪的连接最大数量而形成的数据包。在此期间,新连接可能会失败。

    • NetworkPackets PerSecondAllowanceExceeded:超过每秒最大数据包数。基于高比率小请求的工作负载可能会在达到最大带宽之前达到此限制。

    以上指标是确认节点达到网络限制的理想方法。但是,通过网络指标的高原也可以确认限制。

    如果平稳状态持续了很长时间,则它们之后可能会出现复制滞后、用于缓存的字节增加、可用内存减少、高交换和 CPU 使用率。Amazon EC2 实例同样具有网络限制,这些限制可通过 ENA 驱动程序指标跟踪。具有增强联网支持和 ENA 驱动程序 2.2.10 或更高版本的 Linux 实例可以使用以下命令查看限制计数器:

    # ethtool -S eth0 | grep "allowance_exceeded"

CPU 使用率

CPU 使用率指标是调查的起点,以下项目可以帮助缩小 ElastiCache 端可能出现的问题的范围:

  • Redis SlowLogs:ElastiCache 默认配置保留最近 128 个需要超过 10 毫秒才能完成的命令。慢速命令的历史记录会在引擎运行时保留,如果发生故障或重启,则会丢失。如果列表达到 128 个条目,旧事件将被删除来为新事件预留空间。慢速事件列表的大小和被视为慢的执行时间可以通过自定义参数组中的参数 slowlog-max-lenslowlog-log-slower-than 进行修改。慢速日志列表可以通过在引擎上运行 SLOWLOG GET 128 来进行检索,128 代表报告的最近 128 个慢速命令。每个条目都包含以下字段:

    1) 1) (integer) 1 -----------> Sequential ID 2) (integer) 1609010767 --> Timestamp (Unix epoch time)of the Event 3) (integer) 4823378 -----> Time in microseconds to complete the command. 4) 1) "keys" -------------> Command 2) "*" ----------------> Arguments 5) "1.2.3.4:57004"-> Source

    上述事件发生在 12 月 26 日,UTC 19:26:07,耗时 4.8 秒(4823 毫秒)完成,该事件由从客户端 1.2.3.4 请求的 KEYS 命令导致。

    在 Linux 上,时间戳可以使用命令日期进行转换:

    $ date --date='@1609010767' Sat Dec 26 19:26:07 UTC 2020

    使用 Python:

    >>> from datetime import datetime >>> datetime.fromtimestamp(1609010767) datetime.datetime(2020, 12, 26, 19, 26, 7)

    或者在 Windows 上使用 PowerShell:

    PS D:\Users\user> [datetimeoffset]::FromUnixTimeSeconds('1609010767') DateTime : 12/26/2020 7:26:07 PM UtcDateTime : 12/26/2020 7:26:07 PM LocalDateTime : 12/26/2020 2:26:07 PM Date : 12/26/2020 12:00:00 AM Day : 26 DayOfWeek : Saturday DayOfYear : 361 Hour : 19 Millisecond : 0 Minute : 26 Month : 12 Offset : 00:00:00Ticks : 637446075670000000 UtcTicks : 637446075670000000 TimeOfDay : 19:26:07 Year : 2020

    如果短时间内(一分钟内或更短时间)有许多慢速命令,则需要引起关注。查看命令的性质以及如何对其进行优化(请参阅前面的示例)。如果经常报告 O(1) 时间复杂度的命令,请检查前面提到的 CPU 使用率高的其他因素。

  • 延迟指标:ElastiCache for Redis 提供了 CloudWatch 指标来监控不同类别命令的平均延迟。数据点的计算方法是将类别中命令的执行总数除以期间内的总执行时间。了解延迟指标结果是多个命令的聚合,这一点非常重要。单个命令可能会导致意外结果(如超时),不会对指标产生重大影响。对于这种情况,慢日志事件将是一个更准确的信息来源。以下列表包含可用的延迟指标以及影响它们的相应命令。

    • EvalBasedCmdsLatency:与 Lua 脚本相关的命令、evalevalsha

    • GeoSpatialBasedCmdsLatency:geodistgeohashgeoposgeoradiusgeoradiusbymembergeoadd

    • GetTypeCmdsLatency:读取命令(无论数据类型如何);

    • HashBasedCmdsLatency:hexistshgethgetallhkeyshlenhmgethvalshstrlenhdelhincrbyhincrbyfloathmsethsethsetnx

    • HyperLogLogBasedCmdsLatency:pfselftestpfcountpfdebugpfaddpfmerge

    • KeyBasedCmdsLatency:可以对不同数据类型进行操作的命令:dumpexistskeysobjectpttlrandomkeyttltypedelexpireexpireatmovepersistpexpirepexpireatrenamerenamenxrestoreKsortunlink

    • ListBasedCmdsLatency:lindex、llen、lrange、blpop、brpop、brpoplpush、linsert、lpop、lpush、lpushx、lrem、lset、ltrim、rpop、rpoplpush、rpush、rpushx;

    • PubSubBasedCmdsLatency:psubscribe、publish、pubsub、punsubscribe、subscribe、unsubscribe;

    • SetBasedCmdsLatency:scardsdiffsintersismembersmemberssrandmembersunionsaddsdiffstoresinterstoresmovespopsremsunionstore

    • SetTypeCmdsLatency:写入命令(无论数据类型如何);

    • SortedSetBasedCmdsLatency:zcardzcountzrangezrangebyscorezrankzrevrangezrevrangebyscorezrevrankzscorezrangebylexzrevrangebylexzlexcountzaddzincrbyzinterstorezremzremrangebyrankzremrangebyscorezunionstorezremrangebylexzpopmaxzpopminbzpopminbzpopmax

    • StringBasedCmdsLatency:bitcountgetgetbitgetrangemgetstrlensubstrbitposappendbitopbitfielddecrdecrbygetsetincrincrbyincrbyfloatmsetmsetnxpsetexsetsetbitsetexsetnxsetrange

    • StreamBasedCmdsLatency:xrangexrevrangexlenxreadxpendingxinfoxaddxgroupreadgroupxackxclaimxdelxtrimxsetid

  • Redis 运行时命令:

    • info commandstats:提供自 Redis 引擎启动以来执行的命令列表、其累积执行数、总执行时间以及每个命令的平均执行时间;

    • 客户端列表:提供当前已连接的客户端列表以及相关信息,如缓冲区使用情况、最后执行的命令等;

  • 备份和复制:版本 2.8.22 之前的 ElastiCache for Redis 使用分支过程来创建备份并处理与副本的完全同步。对于写入密集型使用案例,此方法可能会产生大量内存开销。

    从 ElastiCache Redis 2.8.22 开始,Amazon 推出了一种无分支备份和复制方法。新方法可能会延迟写入,以防止出现故障。这两种方法都可能产生较高的 CPU 使用率,导致更长的响应时间,从而导致客户端在执行过程中出现超时。始终检查客户端故障是否发生在备份窗口或在该期间内 SaveInProgress 指标是否为 1。建议将备份窗口安排在使用率低的时间段,以最大限度地减少客户端出现问题或备份失败的可能性。

从服务器端终止的连接

ElastiCache for Redis 的默认配置会无限期将客户端连接保持为已建立状态。但是,在某些情况下,可能需要终止连接。例如:

  • 客户端应用程序中的漏洞可能会导致连接被遗忘并在空闲状态仍保持为已建立状态。这被称为“连接泄漏”,其结果是在 CurrConnections 指标上观察到的已建立连接数量的稳步上升。此行为可能会导致客户端或 ElastiCache 端出现饱和。当客户端无法立即进行修复时,某些管理员会在其 ElastiCache 参数组中设置“超时”值。超时是允许空闲连接保留的时间(以秒为单位)。如果客户端在此期间内未提交任何请求,则 Redis 引擎将在连接达到超时值后立即终止连接。较小的超时值可能会导致不必要的断开连接,客户端需要正确处理它们并重新连接,因此会导致延迟。

  • 用于存储密钥的内存与客户端缓冲区共享。具有较大请求或响应的速度较慢的客户端可能需要大量内存来处理其缓冲区。ElastiCache for Redis 的默认配置不会限制常规客户端输出缓冲区的大小。如果达到 maxmemory 限制,引擎将尝试移出项目以满足缓冲区使用情况。在内存极低的情况下,ElastiCache for Redis 可能会选择断开消耗大量客户端输出缓冲区的客户端,以释放内存并保持集群的运行状况。

    可以通过自定义配置来限制客户端缓冲区的大小,达到限制的客户端将断开连接。但客户端应能够处理意外断开的连接。用于处理常规客户端缓冲区大小的参数如下:

    • client-query-buffer-limit:单个输入请求的最大大小;

    • client-output-buffer-limit-normal-soft-limit:针对客户端连接的软限制。如果保持在软限制以上的时间超过 client-output-buffer-limit-normal-soft-seconds 定义的时间(以秒为单位),或者如果达到硬限制,则连接将终止;

    • client-output-buffer-limit-normal-soft-seconds:超出 client-output-buffer-limit-normal-soft-limit 的连接允许的时间;

    • client-output-buffer-limit-normal-hard-limit:达到此限制的连接将立即终止。

    除了常规客户端缓冲区之外,以下选项控制副本节点和 Pub/Sub(发布/订阅)客户端的缓冲区:

    • client-output-buffer-limit-replica-hard-limit;

    • client-output-buffer-limit-replica-soft-seconds;

    • client-output-buffer-limit-replica-hard-limit;

    • client-output-buffer-limit-pubsub-soft-limit;

    • client-output-buffer-limit-pubsub-soft-seconds;

    • client-output-buffer-limit-pubsub-hard-limit;

Amazon EC2 实例的客户端问题排除

客户端的负载和响应能力也会影响对 ElastiCache 的请求。在排除间歇性连接或超时问题时,需要仔细检查 EC2 实例和操作系统限制。需要注意的一些关键点:

  • CPU:

    • EC2 实例 CPU 使用率:确保 CPU 未饱和或接近 100%。历史分析可以通过 CloudWatch 完成,但请记住,数据点粒度为 1 分钟(启用详细监控)或 5 分钟;

    • 使用可突增 EC2 实例时,请确保他们的 CPU 积分余额没有被耗尽。该信息在 CPUCreditBalance CloudWatch 指标上可用。

    • 短期的 CPU 使用率可能导致超时,而不会影响 CloudWatch 上的 100% 使用率。这种情况需要使用操作系统工具(如 toppsmpstat)进行实时监控。

  • Network

    • 根据实例功能,检查网络吞吐量是否在可接受的值之内。有关更多信息,请参阅 Amazon EC2 实例类型

    • 在具有 ena 增强型网络驱动程序的实例上,请检查 ENA 统计数据查看超时或超出限制情况。以下统计数据对于确认网络限制饱和度很有用:

      • bw_in_allowance_exceeded/bw_out_allowance_exceeded:由于入站或出站吞吐量过高而形成的数据包数量;

      • conntrack_allowance_exceeded:由于安全组连接跟踪限制而丢弃的数据包数。当此限制饱和时,新连接将失败;

      • linklocal_allowance_exceeded:由于通过 VPC DNS 对实例元数据的请求过多而丢弃的数据包数。对所有服务的限制是每秒 1024 个数据包;

      • pps_allowance_exceeded:由于每秒数据包过多而丢弃的数据包数。当网络流量由每秒数千个或数百万个非常小的请求组成时,可能会达到 PPS 限制。ElastiCache 流量可以进行优化,以便通过管道或命令(一次性执行多个操作,如 MGET 而不是 GET)更好地利用网络数据包。

解剖完成单个请求所花费的时间

  • 在网络上:TcpdumpWireshark(命令行上的 tshark)是方便的工具,用于了解请求在网络上传输、到达 ElastiCache 引擎并获得返回所花费的时间。以下示例突出显示使用以下命令创建的单个请求:

    $ echo ping | nc example.xxxxxx.ng.0001.use1.cache.amazonaws.com 6379 +PONG

    与上面的命令并行,tcpdump 经过执行并返回:

    $ sudo tcpdump -i any -nn port 6379 -tt tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes 1609428918.917869 IP 172.31.11.142.40966 > 172.31.11.247.6379: Flags [S], seq 177032944, win 26883, options [mss 8961,sackOK,TS val 27819440 ecr 0,nop,wscale 7], length 0 1609428918.918071 IP 172.31.11.247.6379 > 172.31.11.142.40966: Flags [S.], seq 53962565, ack 177032945, win 28960, options [mss 1460,sackOK,TS val 3788576332 ecr 27819440,nop,wscale 7], length 0 1609428918.918091 IP 172.31.11.142.40966 > 172.31.11.247.6379: Flags [.], ack 1, win 211, options [nop,nop,TS val 27819440 ecr 3788576332], length 0 1609428918.918122 IP 172.31.11.142.40966 > 172.31.11.247.6379: Flags [P.], seq 1:6, ack 1, win 211, options [nop,nop,TS val 27819440 ecr 3788576332], length 5: RESP "ping" 1609428918.918132 IP 172.31.11.142.40966 > 172.31.11.247.6379: Flags [F.], seq 6, ack 1, win 211, options [nop,nop,TS val 27819440 ecr 3788576332], length 0 1609428918.918240 IP 172.31.11.247.6379 > 172.31.11.142.40966: Flags [.], ack 6, win 227, options [nop,nop,TS val 3788576332 ecr 27819440], length 0 1609428918.918295 IP 172.31.11.247.6379 > 172.31.11.142.40966: Flags [P.], seq 1:8, ack 7, win 227, options [nop,nop,TS val 3788576332 ecr 27819440], length 7: RESP "PONG" 1609428918.918300 IP 172.31.11.142.40966 > 172.31.11.247.6379: Flags [.], ack 8, win 211, options [nop,nop,TS val 27819441 ecr 3788576332], length 0 1609428918.918302 IP 172.31.11.247.6379 > 172.31.11.142.40966: Flags [F.], seq 8, ack 7, win 227, options [nop,nop,TS val 3788576332 ecr 27819440], length 0 1609428918.918307 IP 172.31.11.142.40966 > 172.31.11.247.6379: Flags [.], ack 9, win 211, options [nop,nop,TS val 27819441 ecr 3788576332], length 0 ^C 10 packets captured 10 packets received by filter 0 packets dropped by kernel

    从上面的输出中,我们可以确认 TCP 三向握手是在 222 微秒(918091 – 917869)内完成的,并且在 173 微秒(918295 – 918122)内提交并返回了 ping 命令。

    请求关闭连接耗费了 438 微秒(918307 – 917869)。这些结果将确认网络和引擎响应时间良好,调查可以集中在其他组件上。

  • 在操作系统上:Strace 可以帮助识别操作系统级别的时间差。对实际应用程序的分析将更加广泛,建议使用专门的应用程序分析器或调试器。以下示例仅显示操作系统基本组件是否按预期工作,其他内容可能需要进一步调查。通过 strace 使用相同的 Redis PING 命令,我们得到:

    $ echo ping | strace -f -tttt -r -e trace=execve,socket,open,recvfrom,sendto nc example.xxxxxx.ng.0001.use1.cache.amazonaws.com (http://example.xxxxxx.ng.0001.use1.cache.amazonaws.com/) 6379 1609430221.697712 (+ 0.000000) execve("/usr/bin/nc", ["nc", "example.xxxxxx.ng.0001.use"..., "6379"], 0x7fffede7cc38 /* 22 vars */) = 0 1609430221.708955 (+ 0.011231) socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3 1609430221.709084 (+ 0.000124) socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3 1609430221.709258 (+ 0.000173) open("/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 3 1609430221.709637 (+ 0.000378) open("/etc/host.conf", O_RDONLY|O_CLOEXEC) = 3 1609430221.709923 (+ 0.000286) open("/etc/resolv.conf", O_RDONLY|O_CLOEXEC) = 3 1609430221.711365 (+ 0.001443) open("/etc/hosts", O_RDONLY|O_CLOEXEC) = 3 1609430221.713293 (+ 0.001928) socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 3 1609430221.717419 (+ 0.004126) recvfrom(3, "\362|\201\200\0\1\0\2\0\0\0\0\rnotls20201224\6tihew"..., 2048, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("172.31.0.2")}, [28->16]) = 155 1609430221.717890 (+ 0.000469) recvfrom(3, "\204\207\201\200\0\1\0\1\0\0\0\0\rnotls20201224\6tihew"..., 65536, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("172.31.0.2")}, [28->16]) = 139 1609430221.745659 (+ 0.027772) socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 3 1609430221.747548 (+ 0.001887) recvfrom(0, 0x7ffcf2f2ca50, 8192, 0, 0x7ffcf2f2c9d0, [128]) = -1 ENOTSOCK (Socket operation on non-socket) 1609430221.747858 (+ 0.000308) sendto(3, "ping\n", 5, 0, NULL, 0) = 5 1609430221.748048 (+ 0.000188) recvfrom(0, 0x7ffcf2f2ca50, 8192, 0, 0x7ffcf2f2c9d0, [128]) = -1 ENOTSOCK (Socket operation on non-socket) 1609430221.748330 (+ 0.000282) recvfrom(3, "+PONG\r\n", 8192, 0, 0x7ffcf2f2c9d0, [128->0]) = 7 +PONG 1609430221.748543 (+ 0.000213) recvfrom(3, "", 8192, 0, 0x7ffcf2f2c9d0, [128->0]) = 0 1609430221.752110 (+ 0.003569) +++ exited with 0 +++

    在上面的示例中,完成该命令所耗费的时间稍多于 54 毫秒(752110 – 697712 = 54398 微秒)。

    实例化 NC 并执行名称解析耗费了大量的时间(从 697712 到 717890,大约 20 毫秒),之后创建 TCP 套接字需要 2 毫秒(745659 到 747858),提交和接收请求的响应需要 0.4 毫秒(747858 到 748330)。