性能调优最佳实践
最后更新于 2024 年 4 月 18 日
问题
在本文中,您可以找到操作 Redis Enterprise 数据库的最佳实践。其中一些建议也适用于 Redis Community 部署,特别是在处理合适的数据类型和命令时,以及关注命令复杂度。
Redis 命令
- 检查慢日志,查找
EVALSHA
、HGETALL
、HMGET
、MGET
以及所有类型的SCAN
命令。降低慢日志阈值以捕获更多慢命令。您可以使用CONFIG SET slowlog-log-slower-than <THRESHOLD_MICROSECONDS>
配置阈值。 - 避免使用
KEYS
命令,该命令会扫描整个键空间。 - 计算实际的 ops/s 时,请考虑
MGET
命令。例如,以 32k ops/s 执行 10 个键的MGET
相当于 320k ops/s。
热点键和超大键
- 使用
redis-cli --bigkeys
和redis-cli --hotkeys
(仅适用于非 CRDB 数据库)验证键的大小。由于对超大键执行操作(例如扫描)导致 CPU 使用率过高时,分片将无济于事。可以将大键拆分成小键以减轻负载。 - 另一个建议是限制范围大小,任何命令(
ZREVRANGE
、ZRANGE
、ZREVRANGEBYSCORE
等)都可以,只是不要执行全范围扫描(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 命令的更多信息。