出现问题常见的原因有:
1. 网络问题,比如跨网跨机房
2. 个别慢查询导致的整个redis受阻,记住,redis是单线程
3. 存储不当,比如类型存储错误,该用zset却用list,value值过大
4. aof的增量写入和aof重写带来的瞬间阻塞(单线程,操作同个文件,io阻塞了)
5. rdb fork子线程导致的阻塞问题
关于AOF
redis的持久化有两种方式,一种是RDB,一种是AOF。
先说AOF,AOF说白了就是操作redis的日志文件,与MySQL的binlog有点类似,每个redis实例都有一份AOF文件,写入AOF有以下几种策略:no,always,every seconds。一般建议every seconds,毕竟no和always都太极端了。AOF的目的主要是用来当redis实例出现故障时,比如主机宕机,使用AOF来进行数据的恢复。
随着操作记录的增长,AOF也将随之增大,因此就有了AOF重写的机制,即rewrite,什么叫AOF rewrite,举个例子,假设有个key:value是carCount:1,对carCount设置了3次值,分别为
set carCount=2,set carCount=3,set carCount=4,那么最后carCount的值为4,如果不对AOF进行重写,那么将会有3条针对carCount的操作记录,为了减小AOF的大小,redis server会定期对AOF进行重写,重写之后,刚刚针对carCount的操作将会合并成1条,即set carCount=4(只保留最终的操作结果,省略了中间的过程)
AOF重写有多种策略,这里不做过多介绍。但是关于AOF增量写入和重写还是有坑的,笔者就曾经遇到过,现象就是redis每隔几分钟会出现几秒钟的阻塞,阻塞期间任何的读写操作都将无法正常进行,原因我们当时将AOF设置成每秒更新,即fsync,当fsync与rewrite同时进行的时候(操作同个文件啊),会导致IO性能受损而影响整个redis服务(redis是单线程),后来我们将AOF fsync的机制改成了在rewrite期间不进行fsync,以下是关于aof的相关配置:
############################## APPEND ONLY MODE ###############################
# 是否开启AOF,默认关闭(no)
appendonly yes
# 指定 AOF 文件名
appendfilename appendonly.aof
# Redis支持三种不同的刷写模式:
# appendfsync always #每次收到写命令就立即强制写入磁盘,是最有保证的完全的持久化,但速度也是最慢的,一般不推荐使用。
appendfsync everysec #每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,是受推荐的方式。
# appendfsync no #完全依赖OS的写入,一般为30秒左右一次,性能最好但是持久化最没有保证,不被推荐。
#在日志重写时,不进行命令追加操作,而只是将其放在缓冲区里,避免与命令的追加造成DISK IO上的冲突。
#设置为yes表示rewrite期间对新写操作不fsync,暂时存在内存中,等rewrite完成后再写入,默认为no
no-appendfsync-on-rewrite no
#当前AOF文件大小是上次日志重写得到AOF文件大小的二倍时,自动启动新的日志重写过程。
auto-aof-rewrite-percentage 100
#当前AOF文件启动新的日志重写过程的最小值,避免刚刚启动Reids时由于文件尺寸较小导致频繁的重写。
auto-aof-rewrite-min-size 64mb
AOF还有一些坑,比如说AOF重写,Redis的AOF重写是通过fork出一个Redis进程来实现的,所以在一台服务器上最好要预留一半的内存(防止出现AOF重写集中发生,出现swap和OOM)。
再说说RDB与主从同步
在redis中,为了避免线程阻塞,在持久化RDB,主从同步,AOF重写时,redis会单开一个进程来处理。
对于RDB,当达到一定条件时,redis会进行持久化,此时会fork一个进程出来处理持久化,fork新的子进程时,虽说可以共享父进程的数据,但还是需要复制父进程的内存页表,如果redis的内存很大,那么这个内存页表也将是非常巨大的,可能达到百兆,此时进行复制,也需要时间,也会导致堵塞。
什么情况下会fork子进程
Master首次向slave同步数据,当master收到slave的syn请求,会fork一个子进程,将内存数据存储到文件上,然后再同步到slave。
AOF重写,重写并不会读AOF文件,而是直接使用内存中的数据,并归档日志。
持久化很容易造成阻塞,不管是AOF rewrite,还是RDB bgsave,都有可能会出现阻塞,原因是一般情况下,Redis服务设置了appendfsync everysec, 主进程每秒钟便会调用fsync(), 将数据写到存储硬件里. 但由于服务器正在进行大量IO操作, 导致主进程fsync()/操作被阻塞, 最终导致Redis主进程阻塞.解决方案这里就不多说了,上面已经有提到。