前言
如果Redis 没有执行大量的慢查询,同时也没有删除大量的过期的keys,那么我们该怎么办呢?那么我们是不是就应该关注影响性能的其他机制了,也就是文件系统和操作系统了。
Redis 会把数据持久化到磁盘,这个过程依赖文件系统来完成,文件系统把数据写回磁盘的机制,会直接影响到Redis 持久化效率。而且,在持久化的过程中,Redis 也还在接收其他请求,持久化的效率高低又会影响到Redis 处理请求的性能。
Redis 是内存数据库,内存操作非常频繁,所以,操作系统的内存机制会直接影响到Redis 的处理效率。比如说,如果Redis 的内存不够用了,操作系统会启动swap 机制,这就拖慢了Redis。
下面我将从这两个方面讲解:
Redis 持久化优化
首先我们要明白文件系统有两种系统调度,是write 和 fsync 。这两种有什么区别:
write: 只要把日志记录写到内核缓冲区,就可以返回了,并不需要等待日志实际写回到磁盘
fsync: 需要把日志记录写回到磁盘后才能返回,时间较长。
在日志文件里,关于aof 的配置项,appendfsync 有 no,everysec,always。no 就是文件系统的write 调度,文件系统周期性把日志写回磁盘。剩下的都是fsync 将日志写回磁盘,everysec、always 不同的是 everysec 是由后台子线程完成fsync的操作,always 使用主线程去操作
aof 日志,避免日志文件增大,会进行aof 重写,缩小aof 日志文件,aof 重写是子线程异步执行的。
如果aof 重写压力大,会对磁盘进行大量IO操作,fsync 需要等到数据落盘才能返回,就会导致fsync 被阻塞。fsnyc 可能由子线程完成,但是主线程会监控fsync 的执行速度。如果上一次fsync 还没有写完,被主线程监控发现了,主线程就会阻塞,redis 会变慢了。
对于这个点的优化,要根据业务考虑,如果只是当缓冲用,数据丢了,可以从数据库获取,那么 appendfsync 可以设置no, everysec
还有一个配置 no-appendfsync-on-rewrite yes 默认是no 如果 yes 就是表示aof 重写,不进行fsync 操作,也就是说Redis 实例把写命令写到内存后,不调用后台线程进行 fsync 操作,就可以直接返回了。当然,如果此时实例发生宕机,就会导致数据丢失。‘
以上根据业务配置,我们也可以升级硬件,比如采用高速的固态硬盘作为aof 日志的写入设备。
操作系统的swap
什么是操作系统的swap?
内存 swap 是操作系统里将内存数据在内存和磁盘间来回换入和换出的机制,涉及到磁盘的读写,所以,一旦触发 swap,无论是被换入数据的进程,还是被换出数据的进程,其性能都会受到慢速磁盘读写的影响。
Redis 高性能的原因就是数据直接写在内存里,如果给Redis 的内存不足,并且操作系统开启了swap。就会把一部分数据写到磁盘,导致Redis性能下降,数据的读写都是在主线程完成的,此时主线程读写磁盘,增加Redis 的响应时间。
这种情况一般是这样产生的:
-
Redis 实例使用了大量的内存,导致物理内存不足
-
Redis 实例和其他进行运行一台机器,其他进行大量文件读写。文件读写本身消耗内存,这会导致分配给 Redis 实例的内存量变少,进而触发 Redis 发生 swap
这种情况,增加内存或者用机器,redis 实例服务器没有其他的外部进程,除了系统内部的进程。
操作系统本身会在后台记录每个进程的 swap 使用情况,可以用下列命令查看进程的swap使用情况:
-
./redis-cli info | grep process_id 查看redis 进程id
-
cd /proc/5332. 进入 Redis 所在机器的 /proc 目录下的该进程目录中:
-
cat smaps | egrep '^(Swap|Size)' 查看该 Redis 进程的使用情况:
$cat smaps | egrep '^(Swap|Size)' Size: 54 kB Swap: 0 kB Size: 10 kB Swap: 10 kB Size: 4 kB Swap: 0 kB Size: 462044 kB Swap: 462008 kB
上面解释如下:
每一行 Size 表示的是 Redis 实例所用的一块内存大小,而 Size 下方的 Swap 和它相对应,表示这块 Size 大小的内存区域有多少已经被换出到磁盘上了。如果这两个值相等,就表示这块内存区域已经完全被换出到磁盘了。
发现 Swap ,就需要增加机器内存。如果是集群,需要增加集群实例。
大内存页的优化
Linux 内核从 2.6.38 开始支持内存大页机制,该机制支持 2MB 大小的内存页分配,而常规的内存页分配是按 4KB 的粒度来执行的。
很多人认为大内存页可以避免内存的分配次数。确实是这样的,但是redis 有个cow 机制,数据修改后,会申请一块新的内存,并对数据拷贝,如果内存大的话需要拷贝大的内存。你可以看到,当客户端请求修改或新写入数据较多时,内存大页机制将导致大量的拷贝,这就会影响 Redis 正常的访存操作,最终导致性能变慢。
那么我们应该关闭内存大页机制
cat /sys/kernel/mm/transparent_hugepage/enabled
如果是always 就是启动了,never 就是没有启动,可以用下面去修改
echo never /sys/kernel/mm/transparent_hugepage/enabled
好了,写到这里已经很多了,这个需要不断总结,需要对redis 进行监控:
监控大量的key 是否集中过期:
./redis-cli info | grep expired_keys 数据很大报警
监控因为超过最大内存,被淘汰的keys
./redis-cli info | grep evicted_keys
解决方案,改变淘汰策略,随机比较快,还有集群扩容,分担压力
./redis-cli info | grep latest_fork_usec 监控这个值,有可能RDB和AOF 重写期间,实例占用内存大,fork拷贝内存页表越久
解决方案:实例小,部署在物理机器上,情况关闭aof,合理配置repl-backlog和slave client-output-buffer-limit,避免主从全量同步
bigkeys 的监控
redis-cli -h $host -p $port --bigkeys 扫描bigkeys
解决方案,bigkeys 分片存储,Redis 4.0+可开启lazy-free机制 可以异步删除