关于 Redis 实例的 Linux 配置优化

内存分配控制

vm.overcommit_memory

什么是 overcommit?

Linix 操作系统对大部分申请内存的请求都回复 yes,以便能运行更多的程序。因为申请内存后,不会马上使用内存,这种技术叫做 overcommit。

Redis 启动可能出现这样的日志:**

  1. # 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

如果 Redis 启动是有上面的日志,说明 vm.overcommit_memory=0,Redis 提示将它设置为 1。

**vm.overcommit_memory** 用来设置内存分配策略,有三个可选值:

含义
0 表示内核将检查是否有足够的可用内存。如果有,则通过申请;否则申请失败,并返回错误给应用进程。
1 表示应用允许超量使用内存知道用完
2 表示内核绝不过量(”never overcommit”)使用内存,即:系统整个内存地址空间不能超过 swap+50% 的 RAM 值,50%是 overcommmit_ratio 默认值,此参数同样支持修改。

:::warning 注:**上文描述的可用内存代表物理内存和 swap 之和。日志中的 Background save 代表的是 bgsavebgwriteaof

如果当前可用内存不足,操作系统应该如何处理 fork 操作。如果 vm.overcommit_memory=0,代表如果没有可用内存,就申请失败,对应的 Redis 执行 fork 失败。 :::

Redis 建议将 vm.overcommit_memory 设置为 1,为了让 fork 操作能够在低内存下也能运行成功。

获取和设置


获取**

  1. cat /proc/sys/vm/overcommit_memory
  2. 0


设置**

echo "vm.overcommit_memory=1" >> /etc/sysctl.conf

sysctl vm.overcommit_memory=1

最佳实践

  • Redis 设置合理的 maxmemory,保证机器有 20%~30% 的闲置内存。
  • 集中化管理 AOF 重写和 RDB 的 bgsave
  • 设置 vm.overcommit_memory=1,防止极端情况下会造成 fork 事变。

swappiness

参数说明

什么是 swap?

  • 当物理内存不足时,可以将一部分内存页进行 swap 操作,以解燃眉之急。
  • swap 空间由硬盘提供,对于需要高并发、高吞吐的应用,磁盘 IO 会成为系统瓶颈。

什么是 swappiness?

  • 在 Linux 系统中,并不是要等所有物理内存使用完才使用到 swap,而是会根据 swappiness 决定操作系统使用 swap 的倾向程度。
  • swappiness 的取值范围:0~100。swappiness 越大,表示操作系统可能使用 swap 的概率越大;反之越小,表示系统更倾向使用物理内存。
  • swappiness 的默认值为 60。

swappiness 的重要值策略说明:

策略
0 Linux 3.5 以及以上:宁愿用 OOM Killer 也不用 swap
Linux 3.4 以及更早:宁愿用 swap 也不用 OOM Killer
1 Linux 3.5 以及以上:宁愿用 swap 也不用 OOM Killer
60 默认值
100 操作系统会主动使用 swap

OOM(Out Of Memory)killer 机制是指 Linux 操作系统发现可用内存不足时,强制杀死一些用户进程(非内核进程),来保证系统有足够的可用内存进行分配。

设置方法

swappiness 设置方法如下:

echo {bestvalue} > /proc/sys/vm/swappiness

上述方法在系统重启后会失效,为保证系统重启后立即生效,可操作如下:

# 在 /etc/sysctl.conf 文件追加以下配置

vm.swappiness={bestvalue}

{bestvalue} 为需要配置的最佳值。

如何监控 swap

查看 swap 的总体情况

Linux 提供 free 命令来查询系统的内存使用情况,其中包含 swap 的信息。

如下使用 free -m (以 MB 为单位)命令查看,其中 swap 共 1023 MB,已使用 0 MB,空闲 1023 MB:

root@e1141c7e0abd:/data# free -m
              total        used        free      shared  buff/cache   available
Mem:           1987         317        1302           0         367        1542
Swap:          1023           0        1023

实时查看 swap 的使用

Linux 提供了 vmstat 命令查看系统的相关性能指标,包含负载、CPU、内存、swap、IO的相关属性。

其中与 swap 相关的指标是 siso。分别代表操作系统的 swap inswap out

如下面是执行 vmstat 1 (每隔 1 秒输出)的内容,其中 siso 都为 0,代表当前没有使用 swap。

root@e1141c7e0abd:/data# vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 1  0      0 1333960  11544 364348    0    0     1     0   62   36  0  1 98  0  0
 0  0      0 1333952  11544 364348    0    0     0     0 2313  746  1  1 98  0  0
 1  0      0 1333952  11544 364348    0    0     0     0 2439  787  0  1 99  0  0
 1  0      0 1333952  11544 364348    0    0     0     0 2165  759  1  2 98  0  0
 0  0      0 1333952  11544 364348    0    0     0     0 2076  657  0  2 98  0  0
 0  0      0 1333952  11544 364348    0    0     0     0 1704  570  1  1 98  0  0
 0  0      0 1333952  11544 364348    0    0     0     0 2145  676  1  1 98  0  0
 0  0      0 1333952  11544 364348    0    0     0     0 2167  704  0  1 99  0  0
 0  0      0 1333952  11544 364348    0    0     0     0 2397  729  1  1 99  0  0
 0  0      0 1333952  11544 364348    0    0     0     0 2584  765  0  1 98  0  0
 0  0      0 1333952  11544 364348    0    0     0     0 2999  671  1  1 98  0  0
 0  0      0 1333952  11544 364348    0    0     0     0 2100  650  0  1 98  0  0

查看指定进程的 swap 使用情况

Linux 操作系统中,/proc/{pid} 目录是存储指定进程的相关信息,其中 /proc/{pid}/smaps 记录了当前进程对应的内存映射信息。
**

以下拿 Redis 实例举例,假设进程 ID 为986

通过 cat /proc/986/smaps 可查询 Redis 的 smaps 信息。其中存在多个内存块信息,取其中一块观察:

2aab0a400000-2aab35c00000 rw-p 2aab0a400000 00:00 0
Size: 712704 kB 
Rss: 617872 kB 
Shared_Clean: 0 kB 
Shared_Dirty: 0 kB 
Private_Clean: 15476 kB 
Private_Dirty: 602396 kB 
Swap: 58056 kB 
Pss: 617872 kB

其中 swap 字段代表该内存块 swap 分区的数据大小。可通过 cat /proc/986/smaps | grep swap 查看当前进程使用到的 swap 量。

总结

当 Linux > 3.5 时,vm.swapniess=1,否则 vm.swapniess=0,实现两个目标:

  • 物理内存充足时,是 Redis 足够块
  • 物理内存不足时,避免 Redis 死掉(如果 Redis 高可用,死掉比阻塞更好)


THP

Redis 在启动是可能看到如下日志:

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 re

提示看是建议修改 Transparent Huge Page(THP)的相关配置

Linux kernel 在 2.6.38 内核增加了 THP 特性,支持大内存页(2MB)分配,默认开启。

当开启时,可降低 fork 子进程的速度,但 fork 操作之后,每个内存页会从 4KB 变为 2MB,会大幅增加重写期间父进程的内存消耗。同时每次写命令会引起复制内存页单位放大了 512 倍,会拖慢写操作的执行时间,导致写操作慢查询,如最简单 incr 也会出现在慢查询中。

因此 Redis 日志中建议将此特性禁用,禁用方法如下:

echo never > /sys/kernel/mm/transparent_hugepage/enabled

为使机器重启后 THP 配置依然生效,可在 /etc/rc.local 中追加 echo never > /sys/kernel/mm/transparent_hugepage/enabled

注意:有些 Linux 发行版没有将 THP 放到 /sys/kernel/mm/transparent_hugepage/enabled 中,如 Red Hat6 以上的 THP 放置到 /sys/kernel/mm/redhat_transparent_hugepage/enabled 中。而 Redis 源码中检查 THP 是将 THP 的路径写死成:/sys/kernel/mm/transparent_hugepage/enabled,导致部分发行版无法生效,若需生效,可自定义 /sys/kernel/mm/transparent_hugepage/enabled文件配置即可。

OOM killer

OOM killer 会在可用内存不足时选择性的杀掉用户进程。

运行规则是如何,选择哪些进程下手?

  • OOM killer 进程会为每个用户进程设置一个权值,权值越高,被杀掉的概率越高;
  • 每个权值放在 /proc/{pid}/oom_score 中,这个值受 /proc/{pid}/oom_adj 控制;
  • oom_adj 设置为最小值时,该进程不会被 OOM killer 杀掉

oom_adj 在不同 Linux 版本中最小值不同,可参考 Linux 源码的 oom.h 文件

oom_adj 设置方法如下:

echo {value} > /proc/{pid}/oom_adj

对于 Redis 服务器,将所有 Redis 的 oom_adj 设置为最低值或稍小的值,可降低 OOM killer 杀掉的概率。

运维提示:
**

有关 OOM killer 的详细细节,可参考 Linux 源码 mm/oom_kill.coom_badness 函数。

  • 笔者认为 oom_adj 参数只是起到辅助作用,合理规划内存更为重要;
  • 通常在高可用的情况下,杀掉比将死更好,因此无需过多依赖 oom_adj 配置

**

使用 NTP

NTP(Network Time Protocol,网络时间协议)是保证不同机器时间一致性的服务。

Redis Sentinel 与 Redis Cluster 需要多 Redis 节点。Redis 服务本身对服务器时钟没有过多的严格要求。但若节点时钟不一致,可能对排查异常日志带来困难。

为此,我们可以设定每天定时去同步一次系统时间。

image.png

例如每小时同步一次 NTP 服务:

0 * * * * /user/sbin/ntpdate ntp.xx.com > /dev/null 2>&1

ulimit

Linux 系统中,ulimit 可查看和设置系统当前用户进程的资源数。

其中 ulimit -a 命令包含的 open files 参数,是单个用户同时打开的最大文件个数:

ulimit -a

core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 7932
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1048576
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) unlimited
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

Redis 允许同时多个客户端通过网络进行连接,可通过配置 maxclients 来限制最大客户端连接数。对 Linux 系统来说,这些网络连接都是文件句柄。假设当前 open files 是4096,那么启动 Redis 会看到如下日志:

# You requested maxclients of 10000 requiring at least 10032 max file descriptors.
# Redis can’t set maximum open files to 10032 because of OS error: Operation not permitted. 
# Current maximum open files is 4096. Maxclients has been reduced to 4064 to compensate for low ulimit. If you need higher maxclients increase ‘ulimit –n’.

日志解释:

  • 第一行:Redis 建议把 open files 至少设置成 10032,那 10032 如何而来?因为 maxclients 默认是 10000,这些用来处理客户端连接;除此之外, Redis 内部会最多使用 32 个文件描述符。所以 10032 = 10000 + 32。
  • 第二行:Redis 不能将 open files 设置成 10032,因为它没有权限设置。
  • 第三行:当前系统的 open files 是 4096,所以将 maxclients 设置成 4096-32=4064 个,如果你想要设置更高的 maxclients,请使用 ulimit -n 设置。

说明:日志分析可看出 open files 的限制优先级比 maxclients 大。

如何设置 open files:**

ulimit -Sn {max-open-files}

TCP backlog

Redis 默认的 tcp-backlog 值是 511。可以通过修改调整。

如果 Linux 的 tcp-backlog 小于 Redis 设置的 tcp-backlog,那么 Redis 启动时候,会看到如下日志:

# WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/ net/core/somaxconn is set to the lower value of 128.

查看方法:

cat /proc/sys/net/core/somaxconn

4096

修改方法:

echo 511 > /proc/sys/net/somaxconn