Redis 4.x系列(十九):Redis 生产环境部署和优化

  Redis 部署个测试或者测开环境是非常简单的,但如果部署生产环境,则需要考虑更多的因素。

  Redis 生产环境几乎都是部署在基于 Linux 操作系统的服务器上,需要考虑操作系统级别优化、还有客户端连接参数、Redis 安全、内存策略、日志配置、基准测试等。

  这篇文章持续了一个多星期,查了 N 多资料,虽然只是几个优化参数的设置,但有必要深入理解其含义,涉及到对 Linux 底层相关概念的理解,挺费劲的,终于成章。

Linux 优化

基于 Redis 应用场景对 Linux 进行优化可以从以下方面入手。

内存分配策略

修改 Linux 系统内核默认的内存分配策略,将 vm.overcommit_memory 值由默认的 0 改为 1

  1. 查看系统当前内存分配策略:

    #sysctl vm.overcommit_memory
    vm.overcommit_memory = 0
    或者 #cat /proc/sys/vm/overcommit_memory

  2. 查看内存分配百分比(分配策略为 2 时使用),修改此参数与下面操作类似

    #sysctl vm.overcommit_ratio
    vm.overcommit_ratio = 50
    或者 #cat /proc/sys/vm/overcommit_ratio

  3. 修改内存分配策略

    修改方式一:此方式在系统重启后会被恢复为默认值
    sysctl -w vm.overcommit_memory=1
    #echo “vm.overcommit_memory=1” >> /etc/sysctl.conf //将参数持久化到配置文件

    修改方式二:
    #echo 1 > /proc/sys/vm/overcommit_memory

    修改方式三:
    #vim /etc/sysctl.conf 修改或添加 vm.overcommit_memory=1, 保存退出后, 使用 sysctl -p 使配置文件生效。

overcommit_memory:设置内存分配策略,有三个可选值:0、1、2,默认为 0。

  • 0:表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够可使用的内存,则允许应用进程申请内存;否则就拒绝,并把错误返回给应用进程。此方式,内存会尽量减少交换分区(swap)的使用。
  • 1:内核假设总是有足够的内存,直到实际用完为止(允许分配所有的物理内存)。
  • 2:内核不允许申请超过可分配内存上限。

可分配内存上限(CommitLimit) = 总物理内存 x 百分比(默认50,即 50%) + swap。百分比值可通过 overcommit_ratio参数来设置。
可通过cat /proc/meminfo |grep -i commit 来查看系统内存分配上限已分配的内存大小。

1
2
3
4
5
6
7
[root@iZwz94fhuwijhzus6n0xltZ blog]# cat /proc/meminfo |grep -i commit
CommitLimit: 1940804 kB //内存分配上限
Committed_AS: 2394932 kB //已分配的内存
[root@iZwz94fhuwijhzus6n0xltZ blog]# free
total used free shared buff/cache available
Mem: 3881608 1201000 630308 41332 2050300 2328052
Swap: 0 0 0

Redis 在后台持久化是利用了写时复制(Copy-On-Write)的优点,意味着 Redis 不需要申请与数据集大小相同的内存空间。但 Linux 在默认的内存分配策略下,会检查是否有足够的空闲内存来复制父进程的所有内存页;而这可能导致进程由于 OOM(Out of Memory) 而崩溃。出现此问题,会在 Redis 运行日志打印如下错误和警告:

1
2
3
4
55108:M 06 Jan 19:59:27.854 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.

//overcommit_memory=0,当申请的内存不足时报错
01 Jan 17:30:11 # Can't save in background: fork: Cannot allocate memory

交换分区控制

Redis 是内存数据库,但 Linux 内核可能会把部分数据临时保存到在磁盘开辟的交换分区(又称:虚拟内存), Redis 就可能访问位于磁盘的内存页,因磁盘的 I/O 远低于物理内存可能引起阻塞,无法发挥 Redis 高速处理的能力,在 Redis 应用场景下,应尽可能避免使用交换分区。 可设置 Linux 内核的交换分区的的参数(vm.swappiness)来达到目的。

vm.swappiness:该参数用于设置内核使用交换分区的倾向性大小ƒ,取值范围是 0 - 100, 值越大,使用交换分区越激进。

  • 0:最大限度使用物理内存(仅在内存不足的情况下,当剩余空闲内存低于vm.min_free_kbytes limit时,使用交换空间)。
  • 100:内核将积极使用交换分区,将且把内存数据及时搬到交换分区, 该值会严重影响系统性能。

Linux 默认 vm.swappiness 的值是 60,表示物理内存使用率超过 60% 时开始使用交换空间。

Redis 应用场景应设置成 0 以最大限度的使用物理内存,即使没有足够的内存,宁愿让 Redis 进程被杀死,也不希望被交换分区拖慢,使 Redis 能够做到快速失败(fail-fast),那么 HA 或集群机制就能够妥善处理崩溃的情况。

  1. 查看 vm.swappiness

    #sysctl vm.swappiness
    vm.swappiness = 60
    或者 #cat /proc/sys/vm/swappiness

  2. 修改交换分区使用倾向率

    #sysctl -w vm.swappiness=0
    vm.swappiness = 0
    或者
    #echo 0 > /proc/sys/vm/swappiness //重启会被恢复

    将当前值持久化到配置文件
    #echo vm.swappiness=0 >> /etc/sysctl.conf
    或在此配置文件底下增加或修改该配置项的值,sysctl -p激活配置。

禁用透明大页

为了提高对现代计算机大内存的管理性能,Linux 引入了大页内存(Huge page)技术(又称:标准大页管理), 而 Huge Page 难以手动管理,甚至需要修改代码进行重要更改才能使用其生效认,因此 RHEL 6 开始引入了 **Transparent Huge Pages(THP)**, THP 是一个抽象层,能够自动创建、管理和使用标统大页。

这两者的分配机制存在区别:标准大页管理是预分配的方式,而透明大页管理是内核动态分配方式,对用户是透明的。

为了有效地使用大页面,内核必须找到大到足以满足请求的物理连续内存区域,并且还要正确对齐。为此,添加了 khugepaged 内核线程。该线程有时会尝试用较大的页面分配替换当前使用的较小页面,从而最大化 THP 使用。

在 Redis 应用环境,Linux 默认开启的透明大页功能,可能会导致持久化时子进程创建缓慢,官方建关闭该功能;否则在实例启动时,Redis 运行日志会收到如下警告消息:

1
2
3
4
5
55108:M 06 Jan 19:59:27.855 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. 
This will create latency and memory usage issues with Redis.
To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root,
and add it to your /etc/rc.local in order to retain the setting after a reboot.
Redis must be restarted after THP is disabled.

其它应用也会报类似的警告,如 MongoDB。Oracle 官方也是建议禁用 THP,否则会引起一起异常。

  1. 查看是否启用透明大页

    #cat /sys/kernel/mm/redhat_transparent_hugepage/enabled //适用于 redhat Linux
    #cat /sys/kernel/mm/transparent_hugepage/enabled //适用于其它Linux系统
    [always] madvise never
    [always] 表示透明大页开启,[never]表示透明大页禁用,[madvise]表示只在 MADV_HUGEPAGE 标志的 VMA 中使用 THP。
    #cat /sys/kernel/mm/transparent_hugepage/defrag //透明大页碎片整理

  2. 禁用透明大页>

    #echo never > /sys/kernel/mm/transparent_hugepage/enabled //禁用透明大页
    #echo never > /sys/kernel/mm/transparent_hugepage/defrag //禁用透明大页碎片整理

  3. 将禁用透明大页配置持久化到配置文件

    #echo “echo never > /sys/kernel/mm/transparent_hugepage/enabled” >> /etc/rc.d/rc.local
    #echo “echo never > /sys/kernel/mm/transparent_hugepage/defrag” >> /etc/rc.d/rc.local

    或手动编辑 /etc/rc.d/rc.local 文件,在底部添加禁用透明大页配置,重启系统生效
    echo never > /sys/kernel/mm/transparent_hugepage/enabled

  4. 查看透明大页信息

    #grep Huge /proc/meminfo
    AnonHugePages: 956416 kB
    HugePages_Total: 0
    HugePages_Free: 0
    HugePages_Rsvd: 0
    HugePages_Surp: 0
    Hugepagesize: 2048 kB

内存是以块(页)的方式进行管理的,当前大部分系统默认的页大小为 4096 bytes 即 4K 。 1MB 内存等于 256 页; 1GB 内存等于 256000 页。
Huge Pages 有两种格式大小:2MB 和 1GB,2MB 页块大小适合用于 GB 大小的内存, 1GB 页块大小适合用于 TB 级别的内存; 2MB 是默认的页大小。

网络连接优化

通过设置端口监听队列参数来优化 TCP 连接。在高负载情况下,连接监听队列限制太小的话,可能会导致链接超时触发重传,大的监听队列对防止拒绝服务(DoS)攻击也是有帮助的。

  1. Linux 系统 TCP 连接队列长度,有两个参数可设置:somaxconntcp_max_syn_backlog
    • net.core.somaxconn:指定建立完整连接的接收(Accept)队列的长度(上限),该值被传递给 listen 函数的 backlog参数,默认值是 128(定义端口最大的监听队列的长度)。对于高负载应用来说,这个默认值是不够的,一般会把该值增加到 1024 或者更大。
    • net.ipv4.tcp_max_syn_backlog:指挂起连接的最大队列(SYN 队列:不完整队列,又称半连接队列)长度(上限),默认值是 128
    net.ipv4.tcp_max_syn_backlog是个用户态的参数,net.core.somaxconn是内核态参数,当出现 tcp_max_syn_backlog 大于 somaxconn时,因内核态参数优先级高于用户态,tcp_max_syn_backlog队列长度会被截断至somaxconn长度,即能够建立完整连接数的上限是somaxconn
  2. 查看 somaxconn 和 tcp_max_syn_backlog 的值

    #sysctl net.core.somaxconn net.ipv4.tcp_max_syn_backlog
    net.core.somaxconn = 128
    net.ipv4.tcp_max_syn_backlog = 128

    或者 #cat /proc/sys/net/core/somaxconn
    128
    或者#cat /proc/sys/net/ipv4/tcp_max_syn_backlog
    128

  3. 设置 somaxconn 和 tcp_max_syn_backlog 值

    #sysctl -w net.core.somaxconn=65535
    net.core.somaxconn = 65535
    #sysctl -w net.ipv4.tcp_max_syn_backlog=65535
    net.ipv4.tcp_max_syn_backlog = 65535

    或者
    #echo 65535 > /proc/sys/net/core/somaxconn //重启会被恢复
    #echo 65535 > /proc/sys/net/ipv4/tcp_max_syn_backlog //重启会被恢复

    持久化当前设置到配置文件
    #echo “net.core.somaxconn = 65535” >> /etc/sysctl.conf
    #echo “net.ipv4.tcp_max_syn_backlog=65535” >> /etc/sysctl.conf

    或者编辑该配置文件修改这两项参数值,若不存在测添加这两个配置项
    #vim /etc/sysctl.conf
    net.core.somaxconn = 65535
    net.ipv4.tcp_max_syn_backlog = 65535
    保存退出,执行:#sysctl -p 使配置生效

  4. 应用层,Redis 提供了 TCP 连接监听队列长度的配置
    在 Redis 配置文件 redis.conf 中,默认配置了 tcp-backlog 511511 是默认长度。特别注意:Linux 内核将以静默方式将其截断为 /proc/sys/net/core/somaxconn 的值,因此确保提高 Linux 内核 somaxconntcp_max_syn_backlog的值。
    如果 somaxconn 值小于应用配置的 tcp-backlog 的值,Redis 实例在启动时,就会报如下警告:
1
2
55108:M 06 Jan 19:59:27.854 # WARNING: The TCP backlog setting of 511 cannot be enforced 
because /proc/sys/net/core/somaxconn is set to the lower value of 128.
  1. 对 Linux 系统 TCP 连接的优化是系统级的,对高负载应用都是有效的,如Redis、Nginx、Apache等。

打开文件数

Linux 下,一切皆文件,Linux 中所有对象如普通文件、目录、字符设备、块设备、套接字、进程、线程、管道等都被视为文件。详指在文件系统成面,都被抽象成了文件,通过 VFS(Virtual Filesystem,虚拟文件系统)提供的通用的接口,供各种文件系统调用。

每个 Socket 连接相当于打开了一个文件(文件句柄),而 Linux 对每个进程所能的打开的文件是有限制的,可通过ulimit -n 查看,默认是 1024,在高负载应用环境下,这个值是不够的,通常会增大到应用服务同时能接收处理的最大连接数。

Redis 的配置文件 redis.conf 可设置同时连接的最大客户端数,参数是maxclients,默认值是 10000,而 Linux 默认的进程所能打开的文件数是** 1024**,所以需要提高操作系统这个参数的值高于 Redis 中的 maxclients 选项。

ulimit 命令查看帮助
#help -m ulimit

查看一个进程能打开的最大文件数
#ulimit -n

设置一个进程能打开的最大文件数,切换到启动 Redis 进程的用户
#ulimit -n 28800
重启系统或重启下 ssh 服务:service sshd restart。
需要注意的是 ulimit 提供的是对特定 shell 可利用的资源的控制,而 shell 是与具体用户相关的, 因此 ulimit 提供的是对单个用户的限制。

查看进程打开的句柄数,第一列是文件句柄数,第二列是进程ID
#lsof -n|awk ‘{print $2}’|sort|uniq -c|sort -nr|more |grep pid

Redis 安全配置

Redis 官方说明:如果 Redis 服务直接暴露在公网上,没绑定受信任的访问IP,是非常危险的。

Redis 实例部署在线上环境,必须配置 Redis 绑定受信任的访问 IP 地址;或者如阿里云,支持配置部署在与应用系统同一个内网,外网不可访问。

安全配置

编辑 Redis 配置文件vim redis.conf,找到配置项:

  1. 绑定受信任的访问IP

    bind 192.168.1.100 10.0.0.1
    bind 127.0.0.1 ::1

  2. 绑定端口, Redis 默认端口:6379

    port 6379

    记得更新主从复制和 Sentinel 配置中的相应的参数

  3. 设置访问密码

    requirepass foobared
    使用密码访问
    ./redis-cli -a foobared

    或者先连接,再授权
    ./redis-cli
    127.0.0.1:6379> auth foobared

  4. 记得把主实例的访问密码添加到从实例的配置文件

sentinel auth-pass [master-name] [password]
5. 禁用或重命名某些危险命令,让其几乎不可执行
在 redis.conf 配置文件中,增加 rename-command 参数
重命名
rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52
重命名为空字符,则表示禁用
rename-command CONFIG “”

保护模式

Redis 提供了保护模式(Protected mode), 默认开启,该模式是一层安全保护。

当服务器未使用 bind 指令显式绑定一组 IP 地址,也未配置任何身份验证(没有配置密码),Redis 服务器仅接受来自本地 IPv4 和 IPv6 的回环地址 127.0.0.1 和 :: 1 以及 Unix 域套接字的客户端的连接;其它连接会被拒绝。

客户端连接

在 Redis 配置文件 redis.conf 中可配置客户端的网络参数

  1. 客户端连接空闲 N 秒后关闭连接,单位,默认 0 表示不关闭

timeout 0
2. 挂起套接字请求队列的大小

tcp-backlog 511
3. 配置最大客户端连接数
maxclients 10000
该参数非常有用,特别是在一些有访问需要经过防火墙的环境,有的防火墙会把空闲一定时间的连接关闭,但不会告知客户端和服务器。
4. 指定时间间隔发送 TCP ACK 检测客户端连接是否活的,单位

tcp-keepalive 300
5. 配置客户端输出缓冲区大小,有三种类型客户端
client-output-buffer-limit [class] [hard limit] [soft limit] [soft seconds]
client-output-buffer-limit normal 0 0 0 //普通客户端
client-output-buffer-limit slave 256mb 64mb 60 //从实例客户端
client-output-buffer-limit pubsub 32mb 8mb 60 //发布订阅客户端
达到硬限制大小,立即断开;达到软限制大小并达到持续时间,立即断开。都设置为 0,表示禁用。

默认情况下,普通客户端不受限制,因为它们不会在没有询问的情况下(以推送方式)接收数据,而是在请求之后,因此只有异步客户端可能会创建一个请求数据的速度超过其读取速度的方案(发布/订阅)。

配置内存策略

Redis 是内存数据库,内存不像硬盘样有那么大的存储量,生产环境需要对 Redis 占用的内存空间精细规划。

Redis 经常被用做缓存,通常会设置超时时间使键自动失效,还需要考虑缓存满时的淘汰策略。

  1. 查看当前内存使用情况

    127.0.0.1:6379> info memory
    used_memory:847592 //已使用
    maxmemory:536870912 //分配的最大内存,默认 0 表示没有限制
    …..
    maxmemory_policy:noeviction //淘汰策略

    注意:used_memory 值中计入了客户端缓中区占用的内存空间,也就是占用内存空间的不完全是数据对象;在计算 maxmemory 的上限时,从实例输出客户端缓冲区和 AOF 缓冲区的大小并没有被计入。

  2. 内存淘汰策略
    maxmemory_policy 指达到内存空间限制时的淘汰策略,默认值是noeviction,即不淘汰键,但新的请求内存空间达到 maxmemory 限制时,Redis 会拒绝键的创建并返回一个错误。

选项 动作 备注
noeviction 不淘汰,在写入操作时返回错误
(DEL和一些不需要更多内存空间的命令例外)
allkeys-lru 使用 最近最小使用(LRU-less recently used)算法来淘态键
volatile-lru 使用 LRU 算法淘汰设置了过期时间的键
allkeys-random 随机删除键,可能是任意的键
volatile-random 随机删除设置了过期时间的键
volatile-ttl 删除设置了过期时间,并生存时间(TTL)较短的键

在生产环境,特别是一台服务器部署多个实例情况下,合理分配内存,设置maxmemory,使每个实例在内存使用上独立,而不受其它实例的影响。
如果 Redis 作为缓存服务器,建议将淘态策略设置为noeviction之外的策略。但也应该避免频繁的淘汰键,可能会严重影响服务器性能。

Redis 官方文档:Using Redis as an LRU cache

Redis 日志配置

系统日志是非常重要的,虽然只返映了过去的状态,但当发生故障时,通过查看和分析日志来定位问题是第一选择。

Redis 服务也记录运行日志,也可对日志级别和日志存储路径进行配置。

  1. Redis 运行日志有四个级别。
  • debug:调试,包含大量的信息,在开发和调试时非常有用。
  • verbose:详细,包含很多信息,但不像调试级别那么混乱。
  • notice:通知,输出中等量信息,生产中适用,Redis 默认日志级别。
  • warning:警告,只记录非常重要/关键的信息。
    debug 级别最低,**warning **级别最高, 日志只会记录当前级别和更高级别的信息。
  1. 修改日志级别

    编辑 Redis 配置文件 redis.conf,找到 loglevel 配置项
    loglevel notice

    或者在运行时设置
    127.0.0.1:6379> config set loglevel debug

  2. 修改日志存放目录

    编辑 Redis 配置文件 redis.conf,找到 logfile 配置项,设置存放 log 路径和文件名
    logfile ./log
    或运行时设置
    127.0.0.1:6379> config set logfile “/usr/local/redis/logs/6379.log”

    如果 logfile 是空字符串,则日志使用标准输出(console),若同时启用了守护进程模式,则日志会被发送到/dev/null被丢弃。

  3. 日志格式如下
    pid:role timestamp loglevel message
    pid 是进程 PID, role 是 Redis 实例角色,timestamp 是日志事件时间戳,loglevel 是日志级别。
    role 角色:

    X Sentinel
    C RDB/AOF writing child
    S slave
    M master

    loglevel 日志级别:

    1
    2
    3
    4
    . degut
    - verbose
    * notice
    # warning

    示例:

    1
    2
    3
    4
    5
    6
    44035:M 28 Jan 14:22:49.368 * Background saving started by pid 44064
    44064:C 28 Jan 14:22:49.558 * DB saved on disk
    44064:C 28 Jan 14:22:49.559 * RDB: 0 MB of memory used by copy-on-write
    44035:M 28 Jan 14:22:49.607 * Background saving terminated with success
    44035:M 28 Jan 14:22:49.607 * Synchronization with slave 10.0.3.4:26380 succeeded
    44035:M 28 Jan 14:22:50.309 # Cluster state changed: ok

其它参考

  1. Performance tips for Redis Cache Server
  2. 写时拷贝技术
  3. centos中设置swap交换空间的大小设置和swappiness的比例设置
  4. 有关linux下redis overcommit_memory的问题
  5. Linux Swap交换分区介绍总结
  6. Why is swappiness set to 60 by default?
  7. 关于Transparent Hugepage
  8. Linux 关于Transparent Hugepages的介绍
  9. 理解 Linux 中的大页内存(huge page)
  10. Linux 大页面使用与实现简介
  11. Linux HugePage 特性
  12. Linux 下配置 HugePages
  13. Linux之关闭透明大页
  14. How to use, monitor, and disable transparent hugepages in Red Hat Enterprise Linux 6 and 7
  15. How TCP backlog works in Linux
  16. Linux服务器调优
  17. Performance tips for Redis Cache Server
  18. Linux Kernel Tuning
  19. linux下修改内核参数进行Tcp性能调优
  20. 文件句柄 & 文件描述符
  21. Redis 官网:Redis Clients Handling
  22. TCP Keepalive HOWTO
  23. Linux networking socket
  24. getsockopt, setsockopt - get and set options on sockets

Redis 4.x系列(十九):Redis 生产环境部署和优化

http://blog.gxitsky.com/2018/11/25/Redis-19-prod-env-deploy/

作者

光星

发布于

2018-11-25

更新于

2022-08-14

许可协议

评论