我在年纪尚轻经验尚浅的时候,接到过一个任务。有个功能需要测试性能,设计上大量使用 Redis,我需要搭建一个 Redis 的性能测试环境,来找出最佳配置。对于当时只会用 API 实现需求的我来说,这显然不是个简单的任务。我面对的是许多 Redis 配置选项的作用和原理,以及这些配置选项背后的操作系统功能。一连好几天,我迷失在各种晦涩的英文文档和大量可疑的中文博客里,如同依靠一块磁铁就想在亚马逊丛林里找出一条可以复用的路线。那个任务我完成得并不好,最后是靠着一步一步测试摸索出一个似乎还行的组合。我念了一串不明所以的咒语,得到了一只长得像鸽子的白色鸟类。
时至今日,我又经历了许多次丛林历险。虽然丛林仍是丛林,没有变成灌木丛,但我多少掌握了一些「野外生存技巧」。我总结了一个 Checklist,记录了关于 Redis 运维配置的一些思路,作为曾经丛林历险生活的纪念品。
配置 checklist
Linux 环境调整
-  允许内核超量使用内存 vm.overcommit_memory=1。
-  降低内存 swap 概率 swappiness。
- 禁用内存透明大页 THP。
- 增大进程同时打开文件数量。
- 增大 TCP 全连接队列长度。
- 配置网络防火墙。
- 使用非 root 用户启动。
- (可选)定时备份数据。
Redis 服务配置
- 设置最大内存。
- 开启持久化。
- 设置慢日志阈值。
- 重命名高危命令防止误操作。
- 修改默认端口防止扫描。
- (可选)调整 hash、zset 等数据类型的 listpack (ziplist)使用条件。
- 监控 bigkey。
- 监控热点 Key。
- 启用延迟监控功能。
- (可选)监控内存碎片化。
高并发场景
- 增加最大连接客户端数量。
- 增加 tcp-backlog。
- 读写分离。
- (可选)增加 I/O 子线程。
- (可选)Redis 绑定 CPU 核心,提高缓存命中率。
Linux 环境配置
内存设置
- 允许内核超量使用内存 - vm.overcommit_memory=1。- RDB 持久化和 AOF 重写需要 fork 子进程,当可用内存(物理+swap)不足时,fork 会失败。调整为 1 后,允许内存不足时 fork。 - 1 
 2
 3
 4
 5
 6- # 查看 
 cat /proc/sys/vm/overcommit_memory
 # 临时修改,重启后还原
 sysctl vm.overcommit_memory=1
 # 持久修改,重启后生效
 echo "vm.overcommit_memory=1" >> /etc/sysctl.conf- 💡 /proc 不是一个真实的目录,因此对其的修改不持久,系统重启后会还原。类似 - sysctl vm.overcommit_memory=1的效果。- 💡 /etc/sysctl.conf 文件用于设置 Linux 内核参数,系统重启后才生效,可以用 - sysctl -p命令加载而无须重启。参考How-to-modify-sysctl-settings- 重定向提示权限不够时,用 - tee命令。- 1 - echo "vm.overcommit_memory=1" | sudo tee -a /etc/sysctl.conf > /dev/null - 配合 - maxmemory使用,设置 70%~80% 物理内存。
- 降低内存 swap 概率 - swappiness。- swap 影响 Redis 命令性能。 - swappiness越大,swap 概率越高。最小值为 0,但 Linux 3.5+ 时- swappiness=0代表内存不足时会杀死用户进程(包括 Redis) 回收内存,应该避免。取值策略:尽量小,宁愿用 swap 也不能杀死 redis 进程。- ≤ Linux 3.4,swappiness取 0。
- Linux 3.5+,swappiness取 1。
 - 1 
 2
 3
 4
 5
 6- # 临时修改,重启后还原 
 echo 1 > /proc/sys/vm/swappiness
 # 临时修改,重启后还原
 sysctl vm.swappiness=1
 # 持久修改,重启后生效
 echo "vm.swappiness=1" >> /etc/sysctl.conf- 相关命令 - 1 
 2
 3
 4
 5
 6- uname -a # 查看内核版本 
 cat /proc/version # 查看内核版本
 free -h # 查看系统 swap
 vmstat 1 # 每隔 1s 输出一次,关注 si so 两列
 cat /proc/{pid}」/smaps | grep -i swap # 查看 pid 进程的 swap 使用
 sysctl -p # 立即加载 /etc/sysctl.conf 文件
- ≤ Linux 3.4,
- 禁用内存透明大页 THP。 - 开启 THP 能提升内存访问效率,但会降低内存时复制的性能。根据 Redis 作者这篇 Linux 内核如何影响 Redis 性能的文章,Redis 依靠子进程实现 RDB 持久化和 AOF 重写,开启 THP 会增加重写期间父进程的内存消耗,也会拖慢重写期间命令执行速度。 - 1 
 2
 3
 4
 5- # 检查 THP 
 cat /sys/kernel/mm/transparent_hugepage/enabled
 # 临时禁止 THP
 echo never > /sys/kernel/mm/transparent_hugepage/enabled
 echo never > /sys/kernel/mm/transparent_hugepage/defrag- 持久修改 THP 需要根据 Linux 发行版采取不同设置。 - 关于 THP 的详细接收,可以参考下面两篇文章。 - 6.3. 禁用 Transparent Huge Pages 功能 | Red Hat Product Documentation - 我们为什么要禁用 THP 丨TiDB 应用实践 | PingCAP - 💡 常用数据库 MySQL、MongoDB 都会建议关闭 THP。 
网络设置
- 增大进程同时打开文件数量。 - Linux 秉持「一切皆文件」的思想,将网络连接(套接字)也对应为一个文件。同时,Linux 限制每个进程能同时打开的文件描述符数量,也就限制了进程的最大连接数。一旦超过上限,会报 error: too many open files 错误。 - 通过 - ulimit -n可以查看进程的文件描述符限制,通常为 1024。通过- ulimit命令设置阈值,有两种阈值。- soft 代表警告阈值,可以超过这个值,超出会警告。
- hard 代表严格阈值,超出就报错。
 - 1 
 2
 3
 4
 5- ulimit -Sn # 查看警告阈值 
 ulimit -Hn # 查看严格阈值
 ulimit -Hn # 设置警告阈值
 ulimit -Sn 10032 # 设置警告阈值- 通过 ulimit 设置的阈值只在当前会话内有效。想要持久设置,需要修改 /etc/security/limits.conf 文件。 - 1 
 2
 3
 4- # /etc/security/limits.conf 
 root hard nofile 10032
 root soft nofile 10032
 # root 为进程用户,nofile 代表文件数量。- Redis - maxclients决定了最大客户端连接,默认 10000。如果同时打开文件数量小于这个数,会建议设置为 maxclient + 32,32 是 Redis 自身需要使用的数量。- Redis 客户端文档 中提议设置 - sysctl -w fs.file-max=100000,根据Linux 内核参数文档,这里设置的是内核最大连接数。可以通过- cat /proc/sys/fs/file-max查看当前值,如果较小,可以增大。- 关于 ulimit 的设置,可以参考 how-to-set-ulimit-max-number-open-files。 
- 增大 TCP 全连接队列长度。 - Redis 默认的 tcp-backlog 值为 511,如果 Linux 系统的全连接队列长度小于 Redis 的值,启动时会警告。Linux 系统相关配置为 - /proc/sys/net/core/somaxconn,实际采用 max(somaxconn, backlog) 作为应用全连接队列长度。- 1 
 2
 3
 4- # 查看系统默认值 
 cat /proc/sys/net/core/somaxconn
 # 查看 6379 端口实际值,Send-Q 列
 ss -lnt | grep 6379- 设置时将 somaxconn 增加到 Redis tcp-backlog 一致即可。高并发时可以统一为 65536。Redis 需设置 tcp-backlog 65536。 - 1 
 2
 3
 4
 5
 6- # 临时设置 
 echo 65536 > /proc/sys/net/core/somaxconn
 echo 65536 > /proc/sys/net/ipv4/tcp_max_syn_backlog
 # 持久设置
 echo "net.core.somaxconn=65536" >> sysctl.conf
 echo "net.ipv4.tcp_max_syn_backlog=65536" >> sysctl.conf- Docker 在 run 命令添加相关配置。 - 1 - docker run ... --sysctl net.core.somaxconn=511 ... - Docker compose - 1 
 2- sysctls: 
 net.core.somaxconn: '511'
安全
- 配置网络防火墙。 - 仅限内网访问,禁止外网流量。 - 如果是云主机,设置 IP 白名单仅限指定主机访问。 
- 使用非 root 用户启动。 - 比较简单的做法是用一个非 root 用户安装并配置 Redis。高级一点的做法是专门创建一个用户,nologin 选项禁止登录,并赋予 Redis 相关权限。Docker 默认是以非 root 用户启动。 
- (可选)定时备份数据。 - 取决于 Redis 数据是否重要。如果是纯粹的缓存服务器,且加载成本不高,可以不备份。 
Redis 服务配置
调整配置
- 设置最大内存。 - 设置为 70%~80% 物理内存。 - 1 
 2
 3- # redis.conf 
 # 2GB
 maxmemory 2147483648
- 开启持久化。 - 对于数据记录场景,数据持久化是基本要求。 - 对于缓存场景,开启 Redis 持久化可以加快重启速度。 - 建议采取混合持久化方案。 - 1 
 2- # redis.conf 
 aof-use-rdb-preamble yes
- 设置客户端连接超时。 - 超时客户端连接会被自动释放。使用连接池时没设置 idle 检测,以此兜底。 - 1 
 2
 3- # redis.conf 
 # 秒
 timeout 300
- 设置慢日志阈值。 - 放宽慢日志记录条件,增加慢日志队列长度。 - 1 
 2
 3
 4- # 命令执行耗时超过 1 毫秒,记录慢日志 
 CONFIG SET slowlog-log-slower-than 1000
 # 保留最近 1000 条慢日志
 CONFIG SET slowlog-max-len 1000
- 限制高危命令防止误操作。 - 部分 Redis 命令的影响很大,比如 - flushdb/flushall,误操作会导致数据丢失,需要限制使用。- 高危命令: - flushall/flushdb
- shutdown
- config
- debug
 - 可能导致性能问题的命令: - keys
- save
 - Redis 6.0 之前可以通过 - rename-command重命名来隐藏命令。- 1 
 2- # 将 flushall 重命名为指定的随机字符串 
 rename-command flusall {random_str}- 重命名命令也有缺陷: - 客户端不支持,如果要在客户端使用重命名过的命令,需要修改代码。
- AOF、RDB 兼容问题。
- Redis 源码内部调用的命令还是原来的名称,主要是 config命令。
- 主从复制时需要手动配置从节点,不支持自动设置。
 - Redis 6.0 开始,支持 ACL 功能,能更完善地控制权限。ACL 功能支持创建不同用户,并从命令和键名两个维护控制用户权限。 
 ACL 功能文档
 ACL 命令文档- ACL 为所有命令添加了不同标签(虽然叫 category 但允许重叠更像是 tag),@dangerous 标签下包含了所有的高危命令,可以通过 - ACL CAT dangerous查看。- 1 
 2
 3
 4- # 创建一个叫 application 的用户,密码 42a979... 
 # 允许 @dangerous 外的所有命令
 # 支持访问所有的键
 ACL SETUSER application on +@all -@dangerous >42a979... ~*
- 修改默认端口防止扫描。 - 1 
 2- # redis.conf 
 port 9736
- (可选)调整 hash、zset 等数据类型的 listpack (ziplist)使用条件。 - listpack 是紧凑结构,内存占用比 hashtable 和 skiplist 更低。如果瓶颈在内存,可以提高使用 listpack 的阈值。缺点是命令执行性能可能降低。配置如 - hash-max-listpack-entries- hash-max-listpack-value。
增加监控
- 监控 bigkey。 - 降低来自 bigkey 的危害。一般是在开发阶段就要注意。 - 可以通过 - redis-cli —bigkeys统计分布。也可以通过- scan+- debug object key查询实际大小。还可以 Redis RDB Tools 工具,离线分析 rdb 文件。- 重点是建立自己的 bigkey 标准。 - 可以在从节点上执行分析,降低对业务的影响。 - 删除时,通过 unlink 异步删除,对性能影响小,缺点是内存回收不及时。 
- 监控热点 Key。 - 可以从客户端角度监控。 - 使用代理集群方案时,可以再代理端监控热点。 - 可以在 Redis Server 上利用 monitor 命令统计热点。 - 在网络层抓包分析。 
- 启用延迟监控功能。 - 设置一个延迟阈值,超过阈值的事件被记录为延迟峰值。Redis 服务会监控记录命令、fork、rdb、aof、过期、淘汰等多种事件的耗时。 - Redis 官方称延迟监控的实际成本接近于零,可以放心使用。 - 1 
 2
 3
 4- # 阈值设置为 100 毫秒 
 CONFIG SET latency-monitor-threshold 100
 # 使用 latency 命令查询
 LATENCY latest
- (可选)监控内存碎片化。 - used_memory_rss代表操作系统分配给 Redis 进程的内存。- used_memory代表 Redis 分配出去的内存,包括 swap 内存。- mem_fragmentation_ratio代表内存碎片率,- used_memory_rss/- used_memory。> 1 表示内存碎片严重。< 1 代表存在 swap。- 建议以 used_memory ≥ 500M && mem_fragmentation_ratio > 1.5 作为一个监控指标。 
追求高并发性能
- 增加最大连接客户端。 - 1 
 2- # redis.conf 
 maxclients 20000
- 增加 tcp-backlog。 - 1 
 2- # redis.conf 
 tcp-backlog 65536
- 读写分离。 - 需要在客户端配置。 - 1 
 2
 3
 4- # 从节点与主节点断开连接后也能对外提供服务,保证基本可用。 
 slave-serve-stale-data yes
 # 从节点仅处理读请求
 slave-read-only yes
- (可选)增加 I/O 子线程提升网络性能。 - Redis 默认关闭。官方建议确实遇到网络瓶颈再开启,且至少 4 核 CPU 时才开启,注意为主线程预留 1~2 核心。 - 1 
 2
 3
 4- # 开启多线程处理接口写入 
 io-threads-do-reads yes
 # 配置 I/O 线程数
 io-threads 2
- (可选)Redis 绑定 CPU 核心,降低上下文切换开销,提高缓存命中率。 - 缺点是 fork 子进程会与主进程共用一个 CPU,造成竞争,因此不建议在主节点使用。 - 1 
 2
 3
 4- # 在 CPU 0-1 上启用 RPS 
 echo '3' > /sys/class/net/eth1/queues/rx-0/rps_cpus
 # redis 的 CPU 亲和性设置为 CPU 2-8
 taskset -pc 2-8 `cat /var/run/redis.pid`
高延迟问题排查
判断是否是 Redis 问题
- 运行基准测试,得到基准性能数据。与正常实例比较。
确认了是 Redis 性能问题后
- 检查慢日志,slowlog get {n},排查慢速命令。应该从命令时间复杂度和数据量两方面分析。
- 排查 bigkey,redis-cli —bigkeys。
- 检查持久化方案,AOF + always fsync 很慢。
- 检查持久化状态,fork 执行时间过长会导致主线程阻塞。info state 查看 latest_fork_usec 指标。
- 检查内存用量,查看 Redis 进程用量和 swap 情况。
- 检查系统 THP 是否禁用。
- 检查 CPU 负载,top 命令查看 Redis 进程 CPU 使用率。
- 检查服务器网络环境,查看 ulimit fd 数量、backlog 队列长度。
-  测试网络延迟,在另一台服务器执行 redis-cli --latency -h host -p port。
- 检查 Redis 连接数是否超过 maxclients。
Redis 基准测试命令
测试固有延迟,必须在 Redis 同台机器上测试。
| 1 | # 100s 内的最大响应延迟 | 
水平有限,欢迎指正。