Redis调优
屏蔽关键命令
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command CONFIG CONFIG-T
rename-command KEYS KEYS-T
设置慢查询列表
redis slowlog通过2个参数配置管理,默认命令耗时超过10毫秒,就会被记录到慢查询日志队列中;队列默认保存最近产生的128个慢查询命令。
slowlog-log-slower-than: 慢查询阀值,单位微秒. 默认100000(10毫秒); 生产环境设置1ms,因为Redis是single thread,如果命令都是1ms以上,则实例的吞吐量只有1000QPS.
slowlog-max-len: 慢查询存储的最大个数,默认128; 生产设置设置大于1024,因为slowlog会省略过多的参数,慢查询不会占用过多的内存; 慢查询队列满后,淘汰最老的慢查询实体。
推荐设置
redis> CONFIG SET slowlog-log-slower-than 1000
redis> CONFIG SET slowlog-max-len 1000
redis> CONFIG GET slowlog-log-slower-than
1) "slowlog-log-slower-than"
2) "1000"
redis> CONFIG GET slowlog-max-len
1) "slowlog-max-len"
2) "1000"
设置连接客户端数
在Linux中, 可以通过ulimit查看和设置系统当前用户进程的资源数。 其中ulimit-a命令包含的open files参数, 是单个用户同时打开的最大文件个数
ulimit -a
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1048576
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 permitt
# 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-74432=4064个, 如果你想设置更高的maxclients, 请使用ulimit-n来设置。从上面的三行日志分析可以看出open files的限制优先级比maxclients
大。
推荐设置
以4核8G服务为例,1台物理机上部署2个redis实例
设置open files
ulimit –Sn 65535
设置maxclients
> config SET maxclients 65535
OK
> config get maxclients
1) "maxclients"
2) "32799"
设置backlog队列
此参数确定了TCP连接中已完成队列(完成三次握手之后)的长度, 当然此值必须不大于Linux系统定义的/proc/sys/net/core/somaxconn值,默认是511,而Linux的默认参数值是128。当系统并发量大并且客户端速度缓慢的时候,可以将这二个参数一起参考设定。
推荐设置
查看linux内核参数
cat /proc/sys/net/core/somaxconn
128
设置:
echo 8000 > /proc/sys/net/core/somaxconn
查看redis
> config get tcp-backlog
1) "tcp-backlog"
2) "511"
需要手动修改配置文件后,重新加载。重新加载会出现短暂阻塞
tcp-backlog 8000
> redis-cli debug reload
内存分配
vm.overcommit_memory
Redis在启动时可能会出现这样的日志:
# 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
在分析这个问题之前, 首先要弄清楚什么是overcommit? Linux操作系统对大部分申请内存的请求都回复yes, 以便能运行更多的程序。 因为申请内存后, 并不会马上使用内存, 这种技术叫做overcommit。 如果Redis在启动时有上面的日志, 说明vm.overcommit_memory=0, Redis提示把它设置为1。
可选值:0、1、2。
0, 表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内 存申请失败,并把错误返回给应用进程。
1, 表示内核允许分配所有的物理内存,而不管当前的内存状态如何。
2, 表示内核允许分配超过所有物理内存和交换空间总和的内存
最佳实践:
- 配置合理maxmemory参数,保证机器有20%-30%的闲置内存
- 配置vm.overcommit_memory=1,防止极端情况下会造成fork失败
- 集中化管理AOF重写和RDB的bgsave
推荐设置
echo "vm.overcommit_memory=1" >>/etc/sysctl.conf
sysctl vm.overcommit_memory=1
获取
# cat /proc/sys/vm/overcommit_memory
0
swappiness
swap对于操作系统来比较重要, 当物理内存不足时, 可以将一部分内存页进行swap操作, 已解燃眉之急。 但世界上没有免费午餐, swap空间由硬盘提供, 对于需要高并发、 高吞吐的应用来说, 磁盘IO通常会成为系统瓶颈。 在Linux中, 并不是要等到所有物理内存都使用完才会使用到swap, 系统参数swppiness会决定操作系统使用swap的倾向程度。 swappiness的取值范围是0~100, swappiness的值越大, 说明操作系统可能使用swap的概率越高, swappiness值越低, 表示操作系统更加倾向于使用物理内存。 swap的默认值是60, 了解这个值的含义后, 有利于Redis的性能优化 。
值 | 策略 |
---|---|
0 | linux3.5以及以上:宁愿用swap也不用OOM Killer linux3.4以及更早:宁愿用swap也不用OOM Killer |
1 | linux3.5以及以上,宁愿用swap也不用OOM Killer |
60 | 默认值 |
100 | 操作系统会主动使用swap |
附:
OOM(Out Of Memory) killer机制是指Linux操作系统发现可用内存不足时, 强制杀死一些用户进程(非内核进程) , 来保证系统有足够的可用内存进行分配。
推荐设置
echo 0 > /proc/sys/vm/swappiness
但是上述方法在系统重启后就会失效, 为了让配置在重启Linux操作系统后立即生效, 只需要在/etc/sysctl.conf追加vm.swappiness={bestvalue}即可 、
echo vm.swappiness=0 >> /etc/sysctl.conf
需要注意/proc/sys/vm/swappiness是设置操作, /etc/sysctl.conf是追加操作。
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 retain the setting after a reboot.
Redis must be restarted after THP is disabled.
从提示看Redis建议修改Transparent Huge Pages(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中追加.
maxmemory
要求主从 maxmemory 配置必须一致, 当配置的maxmemory从节点小于主节点, 如果复制的数据量超过从节点maxmemory时, 它会根据maxmemory-policy策略进行内存溢出控制, 此时从节点数据已经丢失, 但主从复制流程依然正常进行, 复制偏移量也正常,修复这类问题也只能手动进行全量复制。
要求保证机器有20%-30%的闲置内存。
推荐设置
以4核8G服务为例,1台物理机上部署2个redis实例
maxmemory 3072000000
容错
Redis Cluster 可以为每个主节点设置若干个从节点,单主节点故障时,集群会自动将其中某个从节点提升为主节点。如果某个主节点没有从节点或者从节点不可用,那么当它发生故障时,从故障发现到自动完成转移集群将完全处于不可用状态。
执行任何键命令返回:
(error) CLUSTERDOWN The cluster is down
不过 Redis 也提供了一个参数 cluster-require-full-coverage配置为no当主节点故障时只影响它负责槽的相关命令执行, 不会影响其他主节点的可用性。
推荐设置
redis> config set cluster-require-full-coverage no
redis> config get cluster-require-full-coverage
1) "cluster-require-full-coverage"
2) "no"
复制积压缓冲区
这个信息在 Replication 块里,可以通过 info replication 看到
redis-cli -p 6380 info replication |grep backlog
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:15148084
repl_backlog_histlen:1048576
复制积压缓冲区大小非常重要,它严重影响到主从复制的效率。当从库因为网络原因临时断开了主库的复制,然后网络恢复了,又重新连上的时候,这段断开的时间内发生在master 上的修改操作指令都会放在积压缓冲区中,这样从库可以通过积压缓冲区恢复中断的主从同步过程。
积压缓冲区是环形的,后来的指令会覆盖掉前面的内容。如果从库断开的时间过长,或者缓冲区的大小设置的太小,都会导致从库无法快速恢复中断的主从同步过程,因为中间的修改指令被覆盖掉了。这时候从库就会进行全量同步模式,非常耗费 CPU 和网络资源
如果实例的修改指令请求很频繁,那就把积压缓冲区调大一些,几十个MB大小差不多了,如果很闲,那就设置为几个MB
redis-cli -p 6380 info stats |grep sync
sync_full:1
sync_partial_ok:0
sync_partial_err:1
通过查看 sync_partial_err 变量的次数来决定是否需要扩大积压缓冲区,它表示主从半同步复制失败的次数
推荐设置
redis> config set repl-backlog-size 100mb # 读多写少redis可配置小点,如:30mb
RDB fork
fork操作会阻塞redis进程, 对于高流量的Redis实例OPS可达5万以上, 如果fork操作耗时在秒级别将拖慢Redis几万条命令执行, 对线上应用延迟影响非常明显。 正常情况下fork耗时应该是每GB消耗20毫秒左右。 可以在info stats统计中查latest_fork_usec指标获取最近一次fork操作耗时, 单位微秒
推荐设置
改善fork操作耗时
- 优先使用物理机或者高效支持fork操作的虚拟化技术, 避免使用Xen
- 控制Redis实例最大可用内存, fork耗时跟内存量成正比, 线上建议每个Redis实例内存控制在10GB以内
- 合理配置Linux内存分配策略, 避免物理内存不足导致fork失败
- 降低fork操作的频率, 如适度放宽AOF自动触发时机, 避免不必要的全量复制等
主从节点
主节点会为每个从节点单独建立一条连接用于命令复制,默认配置是: client-output-buffer-limit slave256mb64mb60。 当主从节点之间网络延迟较高或主节点挂载大量从节点时这部分内存消耗将占用很大一部分,主从节点不要部署在较差的网络环境下, 如异地跨机房环境, 防止复制客户端连接缓慢造成溢出
推荐设置
主节点挂载的从节点不要多于2个
servercron
在 Redis 中, 常规操作由 redis.c/serverCron
实现, 它主要执行以下操作
- 更新服务器的各类统计信息,比如时间、内存占用、数据库占用情况等
- 清理数据库中的过期键值对
- 对不合理的数据库进行大小调整
- 关闭和清理连接失效的客户端
- 尝试进行 AOF 或 RDB 持久化操作
- 如果服务器是主节点的话,对附属节点进行定期同步
- 如果处于集群模式的话,对集群进行定期同步和连接测试
Redis 将 serverCron
作为时间事件来运行, 从而确保它每隔一段时间就会自动运行一次, 又因为 serverCron
需要在 Redis 服务器运行期间一直定期运行, 所以它是一个循环时间事件: serverCron
会一直定期执行,直到服务器关闭为止。
在 Redis 2.6 版本中, 程序规定 serverCron
每秒运行 10
次, 平均每 100
毫秒运行一次。 从 Redis 2.8 开始, 用户可以通过修改 hz
选项来调整 serverCron
的每秒执行次数, 具体信息请参考 redis.conf
文件中关于 hz
选项的说明,也叫定时删除,这里的“定期”指的是Redis定期触发的清理策略,由位于src/redis.c的activeExpireCycle(void)函数来完成。
推荐设置
hz 10
客户端输出缓冲区
通过info命令的info clients模块, 找到输出缓冲区列表最大对象数, 例如:
> info clients
# Clients
client_longest_output_list:4869
如果输出缓冲区较大
·限制普通客户端输出缓冲区的, 把错误扼杀在摇篮中, 例如可以进行如下设置:
client-output-buffer-limit normal 20mb 10mb 120
·适当增大slave的输出缓冲区的, 如果master节点写入较大, slave客户端的输出缓冲区可能会比较大, 一旦slave客户端连接因为输出缓冲区溢出被kill, 会造成复制重连。
·限制容易让输出缓冲区增大的命令, 例如, 高并发下的monitor命令就是一个危险的命令。
·及时监控内存, 一旦发现内存抖动频繁, 可能就是输出缓冲区过大
客户端存活状态
client list中的age和idle分别代表当前客户端已经连接的时间和最近一次的空闲时间 .
> client list
id=2232080 addr=10.16.xx.55:32886 fd=946 name= age=603382 idle=331060 flags=N db=0
sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=get
例如上面这条记录代表当期客户端连接Redis的时间为603382秒, 其中空闲了331060秒 。如果age等于idle,说明连接一直处于空闲状态.
如果使用jedis pool连接redis,需要关注minIdle,如以下配置
redis.minIdle=20 # 最小空闲连接数,保持连接池最少存在对象数
redis.maxIdle=300 # 最大空闲连接数,如果请求大于最大空闲连接,当使用完后会直接销毁掉连接对象
redis.maxTotal=1000 # 最大连接数,redis请求不能超过最大连接数
redis.minEvictableIdleTimeMillis=60000 # 单位:60s.对象空闲多久后,从连接池移除,如果移除后池中空闲连接数小于最小空闲连接则再重新创建对象放入池中
redis.timeBetweenEvictionRunsMillis=30000 # 单位30s.从连接池移除扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程.