性能调优最佳实践

最后更新于 2024 年 4 月 18 日

问题

在本文中,您可以找到操作 Redis Enterprise 数据库的最佳实践。其中一些建议也适用于 Redis Community 部署,特别是在处理合适的数据类型和命令时,以及关注命令复杂度。

Redis 命令

  • 检查慢日志,查找 EVALSHAHGETALLHMGETMGET 以及所有类型的 SCAN 命令。降低慢日志阈值以捕获更多慢命令。您可以使用 CONFIG SET slowlog-log-slower-than <THRESHOLD_MICROSECONDS> 配置阈值。
  • 避免使用 KEYS 命令,该命令会扫描整个键空间。
  • 计算实际的 ops/s 时,请考虑 MGET 命令。例如,以 32k ops/s 执行 10 个键的 MGET 相当于 320k ops/s。

热点键和超大键

  • 使用 redis-cli --bigkeysredis-cli --hotkeys(仅适用于非 CRDB 数据库)验证键的大小。由于对超大键执行操作(例如扫描)导致 CPU 使用率过高时,分片将无济于事。可以将大键拆分成小键以减轻负载。
  • 另一个建议是限制范围大小,任何命令(ZREVRANGEZRANGEZREVRANGEBYSCORE 等)都可以,只是不要执行全范围扫描(0 -1)。范围限制得越多越好。然后,对于大型集合,我们建议使用带有定义范围的 ZRANGE(或其他范围扫描命令),而不是全扫描。
  • 如果可能,避免无限制的 LRANGE 调用:它们可能导致高延迟。请使用范围。如果数据是时间序列,可以在不同的数据结构中更有效地处理。

删除

使用异步的 UNLINK 删除超大键,而不是同步的 DEL

要 在不影响性能的情况下执行 Redis 中的大规模键删除,请使用带有批量操作的 redis-cli 或 Redis Insight。使用 redis-cli,您可以指定一个模式,并确保您

  • 使用 -i 选项,这样您就不会阻塞分片的执行
  • 使用 UNLINK,以便在后台执行任务
  -i <interval>      
    When -r is used, waits <interval> seconds per command.                     
    It is possible to specify sub-second times like -i 0.1.

因此,一个使用该命令的例子是

redis-cli -p <PORT> --scan --pattern city:* -i 0.01 | xargs redis-cli -p <PORT> unlink

或者,可以使用带有 -L 选项(最大行数)的 xargs 来减少阻塞服务执行其他命令的可能性。

man xargs

[...]

-L number   Call utility for every number non-empty lines read.

Redis Insight 有一个“批量操作 (BULK ACTIONS)”选项卡,其中包含“删除键 (Delete Keys)”的选项。

  • 在“查找键 (Find keys)”表单字段中,可以提供一个模式
  • 点击“试运行 (DRY RUN)”按钮
  • 预览窗格将返回一个将要执行 UNLINK 的键列表
  • 点击“执行 (EXECUTE)”按钮以执行键的删除

持久化和备份

非复制数据库在执行备份或启用持久化(AOF/快照)时可能会导致延迟。复制数据库解决了这种副作用,备份在副本分片上收集。同样的机制也适用于持久化。

LUA 脚本

验证 LUA 脚本不会因为长时间执行而占用状态机。检查指标“Other cmds”,它表示除命令以外的任何操作,例如正在进行的非读写命令。例如,auth/ping/eval/evalsha/type 命令。

可伸缩性和分片放置

  • 纵向扩展(Scale-up)。对数据库进行重新分片,以利用多核优势。如果分片在同一个节点中,请保持 single 代理策略。
  • 横向扩展(Scale-out)。如果 CPU 占用率过高,请将分片重新分布到各个节点(如果数据库未集群,请确保操作是集群安全的)。
  • 使用相应的 REST API 端点优化分片放置,并评估给定数据库的建议。
  • DMC 代理扩展。如果一个数据库在多个节点上拥有分片,请考虑更改代理策略(all-master-shards),以便 DMC 代理也可以进行扩展。
  • 为 DMC 代理配置不同数量的线程(max_threads)并进行基准测试,以确保代理不会成为瓶颈。
  • 检查分片是否均衡:不均衡可能是由节点具有不同特性引起的。使用哈希策略或超大键也可能导致不均衡。

资源

  • 检查从属分片日志中的 AOF 错误:这表明磁盘速度较慢。管理 UI 也会在节点摘要页面上报告 "Unable to write to disk due to reaching disk I/O limit"(由于达到磁盘 I/O 限制而无法写入磁盘)。该警报是关于磁盘 I/O 限制,而不是存储限制。磁盘可能未满,但无法保持其速度。例如,如果节点中的所有分片都启用了通过 AOF 进行持久化,就可能发生这种情况。这与底层硬件有关。
  • logtop 与慢日志分析结合使用,以捕获由任何重量级命令引起的尖峰
  • 确保交换已禁用。Linux 系统即使在内存充足的情况下也可能进行内存交换(尤其是当 swappiness 值较高时)
  • 低内存也可能导致代价高昂的驱逐。大规模的键驱逐会导致延迟尖峰,因为数据库必须释放内存。如果集群内存不足且驱逐策略配置为 volatile-lru,或者仅仅因为大量键设置了 EXPIRE,就可能发生这种情况。
  • Redis Enterprise 主-主 (Active-Active) 数据库从版本 6.2.18 开始支持 MEMORY USAGE 命令,这简化了故障排除,并允许应用程序检测异常行为和危险趋势。

网络延迟

  • 检查主分片和端点是否位于不同的节点上,这可能会增加延迟(最好是所有主分片都靠近代理)
  • 客户端和数据库是在同一个 VPC 中还是在不同的 VPC 中进行对等连接?两者在同一个 VPC 中运行有助于成倍提高每秒数据包的数量。

连接池

始终建议使用连接池。否则,每个请求都会打开一个新的连接。这会暴露许多可能阻止连接打开的瞬时问题。此外,打开和关闭连接会产生额外的开销。

使用连接池,少数连接将处理所有请求,并且不会在每个请求后关闭。这消除了上述问题,并将确保更好的性能,因为不会浪费时间在打开和关闭连接上。

Redis Enterprise 会永久保持连接打开,除非客户端关闭它们或不回复 TCP keep-alive 消息。对于连续 5 分钟未回复 keep-alive 消息的空闲连接,Redis Enterprise 会关闭它们:Redis Enterprise 假定客户端已关闭其连接。可以使用 CLIENT LIST 命令验证活动连接数。

如果使用连接池,您可能需要增加池中的最小连接数,以便新的线程创建不会延迟客户端上的操作。如果未使用连接池,请考虑使用它。

支持连接池的客户端示例

并非所有客户端都提供连接池功能:例如,StackExchange 通过多路复用单个连接来实现。

管道(Pipelining)

使用客户端管道,可以通过批量打包多个命令来节省往返时间。除了将多个命令的累积延迟减少到批次的延迟之外,读取传入消息所需的套接字读取操作也更少,从而节省了系统调用并降低了总体延迟。在文档中阅读更多信息。

不同分片上的负载不均衡

为数据库增加更多分片以利用多核提供的并行性是可能的,但这并非总是有效。分片负载过高的原因可能有几个。其中一个原因是在该分片上存在超大键或多个超大键。要找到它们,您可以使用带有 --bigkeys 标志的 redis-cli 命令。

redis-cli -h <hostname> -p <port> -a <password> --bigkeys

您也可能有一个热点键。要识别它,可以在流量较低的时段运行 MONITOR 命令进行非常短时间的监视(几秒钟)。请注意,此命令具有危险性,并且可能影响延迟。请在非常短的时间内运行它,并首先在开发数据库或其他流量较低的非生产数据库上进行测试。阅读有关 MONITOR 命令的更多信息。