堵塞--redis的噩耗

由于redis的单线程模型,如果在主线程中执行了耗时的操作,那么之后的所有命令将会被堵塞,响应时间将会增加,整个系统的吞吐量会降低。为此我们需要避免导致redis堵塞的操作,那么redis都有哪些堵塞操作呢?我将redis的堵塞分为内在原因与外在原因。

内在原因

1、时间复杂度高的操作,如keys、集合的一些聚合操作等。我们可以通过redis的慢查询日志,找到执行慢的日志。

2、bigkey的删除操作,如下表所示
在这里插入图片描述

可以看出删除一个百万级别的bigkey都达到秒级别了。为此删除bigkey需要使用异步删除的unlink命令,但是如果你的redis版本不支持异步删除的unlink命令,那么可以使用scan命令一次删除一部分。

3、大量key同时过期,它会导致大量的请求穿透到数据库,同时还可能会导致redis堵塞。redis的过期key的删除是在redis。Redis 键值对的 key 可以设置过期时间。默认情况下,Redis 每 100 毫秒会删除一些过期 key,具体的算法如下:

采样 ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP 个数的 key,并将其中过期的 key 全部删除;
如果超过 25% 的 key 过期了,则重复删除的过程,直到过期 key 的比例降至 25% 以下。

ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP 是 Redis 的一个参数,默认是 20。这一策略对清除过期 key、释放内存空间很有帮助。但是,如果触发了上面这个算法的第二条,Redis 就会一直删除以释放内存空间。注意,删除操作是阻塞的(Redis 4.0 后可以用异步线程机制来减少阻塞影响)。所以,一旦该条件触发,Redis 的线程就会一直执行删除,这样一来,就没办法正常服务其他的键值操作了,就会进一步引起其他键值操作的延迟增加,Redis 就会变慢。
我们可以通过给key设置过期时间时,额外添加一个比较短的过期时间,比如1分钟以内的随机值,避免大量key同时过期。

4、大实例redis,fork创建子进程,虽然redis生成RDB快照与AOF的重写是通过fork子进程来实现的,并且利用了Linux的写时复制技术,但是进程的一些基本信息与虚拟内存到物理内存的映射页表还是需要复制的。这个复制的过程会堵塞主线程,如果redis实例非常大的话,如几十GB,那么fork过程将会达到秒级别,从而长时间堵塞主线程。为此我们需要避免大redis实例,一般redis的合理大小是2到4GB。如果你的数据量真的很大的话,应该使用切片集群,将数据分摊到不同的实例。
另外有一点需要注意的,Linux的写时复制技术是以页为单位复制的,也就是如果主线程只修改了一个页的一个字节,那么修改前也会复制一整页的数据。为此我们需要关闭Linux的大页机制,避免fork子进程执行期间,主进程修改数据时,需要拷贝大量的内存数据,减小对主线程的影响。

5、打开AOF且将策略设置为always,那么redis每收到一条更新命令,就会同步写盘,一次写盘操作耗时通常是毫秒级别的,明显的会影响redis的吞吐量。

6、主从同步中,redis从机清空数据库与加载RDB快照时,如果你使用的是redis4.0及以上的版本,那么清空数据库的时候,是用的异步线程实现的。但是加载RDB快照是同步完成的,在加载完成之前redis就会堵塞住。为此我们一般通过调大replic_backlog缓冲区的大小,避免因为网络闪断导致的全量复制。

7、淘汰策略,当redis内存达到上限后,将会触发内存淘汰,当一次写入大量的数据时,需要预先淘汰数据。

8、上文中提到的,AOF日志的每秒刷盘,如果AOF的刷盘策略配置成everysec时,当redis主线程收到命令时,会将其写入AOF的操作系统缓冲区,后台线程每秒调用fsync刷新一次到磁盘。但是需要注意的是如果由于磁盘IO繁忙,异步刷盘时,fsync超过一秒还没有执行成功,那么主线程将停止服务。

9、管道与事务过大。

外在原因

CPU

1、与其他占用CPU的服务混合部署,其他服务占用大量的CPU资源。redis是一个CPU密集型的应用,不建议混合部署
2、绑定CPU优化,由于redis的生成RDB快照与AOF重写是通过fork子进程完成的,同时redis内部还有一些后台子线程。绑定CPU不合理也会导致他们互相争抢资源。关于CPU的优化在我的博文Redis 多核CPU与NUMA架构优化中已经有详细的描述,这里不再赘述。

网络

1、大量的并发请求,当存在大量的并发请求时,虽然使用了IO多路复用技术,但是IO的读写都是在主线程中执行,当有大量的并发请求时,单线程的IO读写可能存在性能瓶颈,从而导致请求堵塞。
2、大量的网络连接,这样有如下的后果:

  1. redis连接拒绝,redis提供了maxclients参数用于控制网络连接的最大个数,当超过了redis会拒绝连接
  2. Linux系统资源瓶颈,达到进程可以打开的文件描述符个数限制:ulimit -n 65535。linux半连接与全连接队列溢出,可以通过netstat -s命令查看连接队列是否有溢出。
    尽量使用长连接代替短连接,设置使用连接池,关于连接池的介绍见我的博文池化技术–如何减少不断创建数据库连接的性能损耗

内存

主机内存不足,发生swap。那么如果读取或者更新的命令的数据被交换到了磁盘,那么需要将数据所在的页交换回内存,将会显著的降低redis的性能。为此我们一般需要关闭linux的swap功能,并且设置redis的最大内存空间,同时还要预留一定的内存空间给操作系统等使用。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值