redis灵魂拷问:为什么响应变慢了

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: redis灵魂拷问:为什么响应变慢了

redis是一个内存的数据库,最大的特点就是访问性能快,但是也有很多时候,我们会遇到响应变慢的情况,今天我们就来聊一聊。


redis是一个单线程的模型,如果主线程阻塞了,肯定会造成响应变慢。下面我们先来看一看阻塞主线程的操作。


1.AOF重写和RDB快照


前面已经讲过了,redis在AOF重写时,主线程会fork出一个bgrewriteaof子进程,而主线程会fork出一个bgsave子进程。这2个操作表面上看不阻塞主线程,但fork子进程的这个过程是在主线程完成的。fork子进程时redis需要拷贝内存页表,如果redis实例很大,这个拷贝会耗费大量的CPU资源,阻塞主线程的时间也会变长。


我们再回顾一下fork bgrewriteaof子进程这张图:

微信图片_20221212161541.png

2.内存大页


redis默认支持内存大页是2MB,使用内存大页,一定程度上可以减少redis的内存分配次数,但是对数据持久化会有一定影响。


还是看上面那张图,在AOF重写和RDB快照过程中,不会阻塞主线程,这时候,主线程依然在接收新的写请求。这时就用到了CopyOnWrite。使用了内存大页,即使redis只修改其中一个大小是1kb的key,也需要拷贝1整页的数据,即2MB。在写入量较多时,大量拷贝就会导致redis性能下降。

在操作系统上执行下面命令,可以看到是否开启内存大页:

[root@master ~]# cat /sys/kernel/mm/transparent_hugepage/enabled
[always] madvise never

下面命令可以关闭内存大页,但是重启后会失效,怎么样永久禁用,可以参考网上方法。


echo never > /sys/kernel/mm/transparent_hugepage/enabled

注意:关闭内存大页是一个折中,对子进程的COW有优势,但是会增加redis的内存分配次数。


3.命令复杂度高


这个是非常常见的redis阻塞操作,比如一次查询的数据量太大,再比如对一个set或者list数据类型执行SORT操作,复杂度是O(N+M*log(M)),可以看下面官网描述:

官网:https://redis.io/commands/sort

Time complexity: O(N+M*log(M)) where N is the number of elements in the list or set to sort, and M the number of returned elements. When the elements are not sorted, complexity is currently O(N) as there is a copy step that will be avoided in next releases.


再比如我们对set数据类型执行SMEMBERS命令,复杂度是o(N),可以看下面官网描述:


官网:https://redis.io/commands/smembers

Time complexity: O(N) where N is the set cardinality.

对于复杂度高的命令,我们要慎重使用,一方面用复杂度低的命令替代,比如用SSCAN替代SMEMBERS。另一方面,对于排序/交集/并集等操作我们可以在客户端完成。


官网对每个命令都给出了复杂度说明,可以参考。地址如下:


https://redis.io/commands/sort


4.bigkey删除操作


对bigkey的删除需要释放大量的空间,空闲内存使用一个链表进行管理,释放的过程就是把需要释放的内存块加入这个链表中,如果释放的太多,加入链表的时间会增加,从而影响redis响应性能。


redis4.0以后引入了layfree机制,可以使用子进程异步删除,从而不影响主线程执行。不过lazyfree默认是关闭的,在redis.conf文件,如下:

# DEL, UNLINK and ASYNC option of FLUSHALL and FLUSHDB are user-controlled.
# It's up to the design of the application to understand when it is a good
# idea to use one or the other. However the Redis server sometimes has to
# delete keys or flush the whole database as a side effect of other operations.
# Specifically Redis deletes objects independently of a user call in the
# following scenarios:
#
# 1) On eviction, because of the maxmemory and maxmemory policy configurations,
#    in order to make room for new data, without going over the specified
#    memory limit. (内存达到maxmemory,需要使用淘汰策略释放内存)
# 2) Because of expire: when a key with an associated time to live (see the
#    EXPIRE command) must be deleted from memory. (key过期了需要删除,这个设置为yes可以防止大量key同时过期)
# 3) Because of a side effect of a command that stores data on a key that may
#    already exist. For example the RENAME command may delete the old key
#    content when it is replaced with another one. Similarly SUNIONSTORE
#    or SORT with STORE option may delete existing keys. The SET command
#    itself removes any old content of the specified key in order to replace
#    it with the specified string.(删除旧的key)
# 4) During replication, when a replica performs a full resynchronization with
#    its master, the content of the whole database is removed in order to
#    load the RDB file just transferred.(主从全量同步,从库需要清空数据加载RDB文件)
#
#(默认是使用阻塞方式删除的,但是把下面的配置改为yes,这样就能像使用UNLINK命令一样使用费阻塞方式来释放内存了)
# In all the above cases the default is to delete objects in a blocking way,
# like if DEL was called. However you can configure each case specifically
# in order to instead release memory in a non-blocking way like if UNLINK
# was called, using the following configuration directives.  
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
replica-lazy-flush no
# It is also possible, for the case when to replace the user code DEL calls
# with UNLINK calls is not easy, to modify the default behavior of the DEL
# command to act exactly like UNLINK, using the following configuration 
# directive:
#把用户所有的DEL操作都使用UNLINK替代,即使用layfree,这里不建议打开,因为不是bigkey的情况删除效率是非常高的
lazyfree-lazy-user-del no

我把注解一块贴了出来,加了一下自己的翻译。如果lazyfree-lazy-user-del不设置为yes,redis是否采用异步删除,还是要看删除的时间的,对于底层采用整数数组和压缩列表的情况,redis是不会采用异步删除的,还就就是String类型,所以尽量不要存String类型的bigkey。


5.从节点全量同步


从节点全量同步过程中,需要先清除内存中的数据,然后再加载RDB文件,这个过程中是阻塞的,如果有读请求到来,只能等到加载RDB文件完成后才能处理请求,所以响应会很慢。


另外,如果redis实例很大,也会造成RDB文件太大,从库加载时间长。所以尽量保持redis实例不要太大,如果超过4G,建议采用切片集群。


6.AOF同步写盘


之前在讲《redis灵魂拷问:聊一聊AOF日志重写》中,讲到appendfsync策略有3种。如果采用always,每个命令都会同步写盘,这个过程是阻塞的,等写盘成功后才能处理下一条命令。


所以是严格不能丢数据的场景,我们尽量还是采用everysec这种策略,当然如果对丢失数据不敏感,那也可以采用no。


7.内存达到maxmemory


上面也提到过,内存达到maxmemory,需要使用淘汰策略来淘汰key,可以采用lazyfree异步删除。但是选择key的过程是阻塞的,这时候就需要选择较快的淘汰策略,比如用随机淘汰来替换LRU和LFU算法淘汰。


这时候也可以扩大切片数量来减轻淘汰key的时间消耗。


阻塞主线程的操作会让redis响应变慢,我们尽量避免使用,当然还有好多可能阻塞主线程的操作,欢迎大家交流。下面我们看一下硬件资源对redis的影响


1.使用了swap


使用swap的原因是操作系统不能给redis分配足够大的内存,一旦开启了swap,内存数据就需要不停地跟swap换入和换出,对性能影响非常大。


开启swap的原因无非2个,一个是redis需要的内存太大,操作系统没有能力分配,另一个是机器上其他进程使用了大量的内存。


2.网络问题


如果网卡负载很大,对redis性能影响会很大。这一方面有可能redis的访问量确实很高,另一方面也可能是有其他流量大的程序占用了带宽。


这个最好从运维层面进行监控。


3.线程上下文切换


redis虽然是单线程的,但是在多核cpu的情况下,也会发生上下文切换。上下文切换的带来的影响是不能使用一级缓存和二级缓存。我们可以使用下面的命令把redis绑定到一个CPU核上面:


taskset -c 0,6 ./redis-server

这里0,6是同一个物理核的2个逻辑核。这里一定要注意,不能把redis实例绑定到一个逻辑核上面,redis虽然是单线程的,但是如果只用一个逻辑核,fork出的子进程操作会跟主线程争抢CPU资源。


4.磁盘性能低


对于AOF同步写盘的使用场景,如果磁盘性能低,也会影响redis的响应。我们可以优先采用性能更好的SSD硬盘。


redis响应慢的原因有很多,有的是因为阻塞主线程操作引起的,有的是因为硬件资源问题,这跟内存、CPU、网络、磁盘都有一定关系。



本文主要从阻塞主线程的操作和硬件资源方面介绍了redis影响变慢的一些场景。对于redis响应变慢这个问题,原因还要很多,欢迎大家交流。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
2月前
|
缓存 NoSQL Redis
Redis AOF重写问题之正常响应用户命令如何解决
Redis AOF重写问题之正常响应用户命令如何解决
|
运维 监控 NoSQL
Redis变慢?深入浅出Redis性能诊断系列文章(一)
本文基于多年使用和运维管理Redis的经验,详细梳理了可能引起Redis性能问题的原因并剖析对应的解决方案,也希望这一系列的文章能帮助大家更加合理的使用 Redis ,快速的定位并解决问题。
Redis变慢?深入浅出Redis性能诊断系列文章(一)
|
存储 缓存 运维
Redis变慢了,你会怎么排查
Redis变慢了,你会怎么排查
153 0
|
消息中间件 存储 NoSQL
面试:Redis为什么快呢?查询为何会变慢呢?
在实际开发,Redis使用会频繁,那么在使用过程中我们该如何正确抉择数据类型呢?哪些场景下适用哪些数据类型。而且在面试中也很常会被面试官问到Redis数据结构方面的问题:
194 0
面试:Redis为什么快呢?查询为何会变慢呢?
|
缓存 运维 监控
Redis 忽然变慢了如何排查并解决?
Redis 通常是我们业务系统中一个重要的组件,比如:缓存、账号登录信息、排行榜等。 一旦 Redis 请求延迟增加,可能就会导致业务系统“雪崩”。
462 0
Redis 忽然变慢了如何排查并解决?
|
存储 缓存 运维
Redis为什么变慢了?常见延迟问题定位与分析
Redis为什么变慢了?常见延迟问题定位与分析
324 0
|
存储 运维 监控
天啊,为什么我的 Redis 变慢了。。
Redis作为内存数据库,拥有非常高的性能,单个实例的QPS能够达到10W左右。但我们在使用Redis时,经常时不时会出现访问延迟很大的情况,如果你不知道Redis的内部实现原理,在排查问题时就会一头雾水。
265 0
天啊,为什么我的 Redis 变慢了。。
|
监控 NoSQL 网络协议
好烦,一封报警邮件,大量服务节点 redis 响应超时,又得要捉“虫”!
大量TimeoutException,说明当前redis服务节点上已经堆积了大量的连接查询,超出redis服务能力,再次尝试连接的客户端,redis 服务节点直接拒绝,抛出错误。
|
27天前
|
canal 缓存 NoSQL
Redis缓存与数据库如何保证一致性?同步删除+延时双删+异步监听+多重保障方案
根据对一致性的要求程度,提出多种解决方案:同步删除、同步删除+可靠消息、延时双删、异步监听+可靠消息、多重保障方案
Redis缓存与数据库如何保证一致性?同步删除+延时双删+异步监听+多重保障方案
|
1天前
|
存储 缓存 NoSQL
数据的存储--Redis缓存存储(二)
数据的存储--Redis缓存存储(二)
10 2
数据的存储--Redis缓存存储(二)