Redis相关的问题及应对策略

2016-12-16 Li Shuai 更多博文 » 博客 » GitHub »

Redis

原文链接 https://cyrusin.github.io/2016/12/16/redis-20161216/
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。


1. 连接数过高

Redis连接数过高,且没有修改进程能打开的最大文件数,当达到最大文件数限制时,Redis在accept新连接的时候会立即报错"Max open files",无法成功获取该连接,此时,listen socket是持续可读的状态,事件循环直接把CPU跑满。这种现象还可能与没有配置Redis配置文件中的timeout参数有关,不设timeout参数,Redis不会主动关闭僵尸连接,导致连接数越来越高,容易达到限制。 解决方法:1)config set timeout xx修改keeplive时间;2)client kill ip:port杀死一部分僵尸连接;3)修改系统参数,增加单进程能打开的最大文件数。 这一点Memcache和Redis就不同,当Memcache出现这种情况的时候,会立即调用listen(fd, 0),将accept队列的backlog队列设为0,这样就会直接拒绝掉后续的连接,减轻系统的负载。

2. 慢查询

Redis是单线程的,当有单个操作需要处理较长时间,就会阻塞后续的连接,可能是涉及大容量集合相关的CPU密集的运算也可能是操作的value值较大,Redis本身提供慢查询日志的查看,可以查看慢查询日志来分析是哪些操作占用CPU时间过长。 解决方法:限制key和value的大小,如果可以,不要让Redis执行过于复杂的计算,而是获取数据后,提交给异步执行的服务端计算。

3. Redis持久化机制

Redis在fork出子进程做RDB或者AOF的保存时,如果数据量较大,这个不光是CPU可能飙升,内存使用也会有危险。 解决方法:1)主库可以不做持久化,将持久化交给从库来做;2)主从复制采用链表式结构,而不是在主库上增加较多从库,这样主库的复制压力较大。3)使用RDB快照的方式做持久化的话,Redis的配置一定要设maxmemory,该值不要超过机器内存的1/2,防止出现内存占满,使用swap而性能下降。

4. Redis的键删除机制

Redis的键删除机制主要是两种:1)内存不足时的键删除(释放内存);2)过期键的删除。 1)内存不足时:Redis在执行命令时,都会执行一个freeMmoryIfNeeded的函数,当已用内存超过最大内存时(server.maxmemory),根据配置文件中的以下六种策略之一进行删除:

    volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
    volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
    volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
    allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
    allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
    no-enviction(驱逐):禁止驱逐数据

这就是尽量要设置maxmemory和键删除策略的原因,这种策略是保证Redis不会无限制的使用内存。 2)过期键的清除策略:这种策略主要有两种方法,首先是惰性删除,所有读写数据库的Redis命令在执行之前都会调用expireIfNeeded函数对输入键进行检查。如果过期就删除,如果没过期就正常访问。其次是定期删除,每当周期性函数serverCron执行时,会调用activeExpireCycle进行主动的过期键删除。具体方法是在规定的时间内,多次从expires中随机挑一个键,检查它是否过期,如果过期则删除。