Redis变慢?深入浅出Redis性能诊断系列文章(二)

(本文首发于”数据库架构师”公号,订阅”数据库架构师”公号,一起学习数据库技术)

本篇为Redis性能问题诊断系列的第二篇,本文主要从应用发起的典型命令使用上进行讲解,由于Redis为单线程服务架构,对于一些命令如果使用不当会极大的影响Redis的性能表现,这里也会对不合理的使用方式给出优化解决方案。

一、Redis慢日志功能

分析Redis访问变慢,其中有个最基础的方法就是先去看Redis是否有慢日志【就像MySQL的慢SQL一样】。Redis提供了一个简单的慢命令统计记录功能,它会记录有哪些命令在执行时耗时较长。Redis慢日志功能由两个核心参数控制:

slowlog-log-slower-than 1000

慢日志命令执行阈值,这里指超过1ms就会被记录【单位为微秒】

slowlog-max-len 4096

保留慢日志命令的个数,类似一个先进先出的队列,超过4096个最早的就会被清理

Redis的这个慢日志功能比较粗糙简单,有个严重的不足:没有持久化记录能力。

由于Redis的慢日志记录都在内存中,不像MySQL会持久化到文件里,那么如果慢日志产生较快,即使设置的slowlog-max-len比较大也会很快被填满,诊断问题时也就不能统计到那个时间段产生的所有慢命令详情。

为了避免产生的慢日志被清理,目前一个折中的解决方案是写一个收集程序周期性的将新增慢命令查出并记录到MySQL或者本地文件中,以备事后分析。但是这个频率一般都是分钟级,Redis处理的吞吐能力又太大,在慢命令较多的情况下往往也不能全部记录下来。

配置好慢日志相关阈值后,可以执行以下命令查询最近的慢日志记录了:

127.0.0.1:6379> SLOWLOG get 5

1) 1) (integer) 42343

2) (integer) 1653659194 #慢日志产生的时间戳

3) (integer) 73536 #慢日志执行的耗时

4) 1) “KEYS” #慢日志命令详情

2) “permission::userMenuList:*”

5) “192.168.1.11:20504” #慢日志命令发起来源IP【4.0及以后版本支持】

6) “”2) 1) (integer) 42342

2) (integer) 1653659194

3) (integer) 73650

4) 1) “KEYS”

2) “userPermission:*”

5) “192.168.1.10:20362”

6) “”

3) 1) (integer) 42341

2) (integer) 1653659193

3) (integer) 81505

4) 1) “KEYS”

2) “userRole:*”

5) “192.168.1.13:19926”

6) “”

二、几种典型导致Redis变慢的不合理使用方式

1.使用keys命令进行正则匹配

Keys的正则匹配是阻塞式的、全量扫描过滤,这对于单线程服务的Redis来说是致命的,仅仅几十万个Key的匹配查询在高并发访问下就有可能将Redis打崩溃!这其实就像MySQL的无索引查询大表数据,全表扫描状态下几个并发查询就可能会将数据库堵死。

redis> SLOWLOG get 5

1) 1) (integer) 42343

2) (integer) 1653659194

3) (integer) 73536

4) 1) “KEYS”

2) “Testper::userList:*”

5) “192.168.1.10:20504”

6) “”

2) 1) (integer) 42342

2) (integer) 1653659194

3) (integer) 73650

4) 1) “KEYS”

2) “TestuserPermission:*”

5) “192.168.1.11:20362”

6) “”

3) 1) (integer) 42341

2) (integer) 1653659193

3) (integer) 81505

4) 1) “KEYS”

2) “TestuserRole:*”

5) “192.168.1.12:19926”

6) “”

上述示例中使用Keys来模糊查询某些Key,每次的执行都在70ms以上,严重影响了正常的Redis响应时长和吞吐。

针对这种问题的一个解决方案是使用scan代替keys。这是一个查询迭代命令,用于迭代当前数据库中的缓存数据。它是一个基于游标的迭代器,每次被调用之后, 都会向用户返回一个新的游标, 用户在下次迭代时需要使用这个新游标作为Scan命令的游标参数, 以此来延续之前的迭代过程。具体的命令语法这里不再详述。

2.大量使用了复杂度较高的命令

(1)应用中高频使用了 O(N) 及以上复杂度的命令,例如:SUNION、SORT、ZUNIONSTORE、ZINTERSTORE 聚合类命令。SORT命令的时间复杂度:O(N+M*log(M)), N 为要排序的列表或集合内的元素数量, M 为要返回的元素数量。

这种导致Redis请求变慢的原因是,Redis 在操作数据排序时,时间复杂度过高,要花费更多的 CPU计算资源。

(2)使用 O(N) 复杂度的命令,但 N 的值非常大,比如hgetall、smembers、lrange、zrange等命令。

这种变慢的原因在于,Redis 一次需要返回给客户端的数据过多,需要花费更多时间在数据组装和网络传输中。对于hgetall、smembers这种命令,需要警惕项目刚上线之初hash、set或者list存储的成员个数较少,但是随着业务发展成员数量极有可能会膨胀的非常大,如果仍然采用上述命令不加控制,会极大拖累整个Redis服务的响应时间。

针对这两种情况还都可以从资源使用率层面来分析,如果应用程序访问 Redis 的QPS不是很大,但 Redis 实例的 CPU 使用率却很高,那么很有可能是使用了复杂度过高的命令导致的。

因为Redis 是单线程处理请求的,如果你经常使用以上复杂度较高的命令,那么当 Redis 处理程序请求时,一旦前面某个命令发生耗时较长,就会导致后面的请求发生阻塞排队,对于应用程序来说,响应延迟也会变长。

3.存储使用了bigkey

在分析慢日志发现很多请求并不是复杂度高的命令,都是一些del、set、hset等的低复杂度命令,那么就要评估是否写入了大key。

在往Redis写入数据时,需要为新数据分配内存块,相对应的,当删除数据时,Redis也会释放对应的内存空间。如果一个 key 写入Redis的值非常大,那么在分配内存时就会相对比较耗时。同样的当删除这个 key 时,释放内存也会比较耗时,这种被称为bigKey。

当然这个描述仍然比较宽泛,因为Redis中的数据库结构类型比较多,更完善的一些说法可以这么定义:将含有较大数据或含有大量成员、列表数的Key定义为bigkey。

我们一般要求研发使用Redis时,对于String类型Value大小不要超过1KB。

大Key带来的问题比较多,主要有下面几种情况:

  • 由于大Key的内存分配及释放开销变大,直接影响就是导致应用访问Redis的响应变慢;
  • 删除时会造成较长时间的阻塞并有可能造成集群主备节点切换【4.0之前的版本有这个问题】;
  • 内存占用过多甚至达到maxmemory配置,会造成新写入阻塞或一些不应该被提前删除的Key被逐出,甚至导致OOM发生;
  • 并发读请求因为Key过大会可能打满服务器带宽,如果单机多实例部署则同时会影响到该服务器上的其它服务【假设一个bigkey为1MB,客户端每秒访问量为1000,那么每秒产生1000MB的流量】;
  • 运维麻烦,比如RedisCluster的数据跨节点均衡,因为均衡迁移原理是通过migrate命令来完成的,这个命令实际是通过dump + restore + del三个命令组合成原子命令完成,如果是bigkey,可能会使迁移失败,而且较慢的migrate也会阻塞Redis正常请求;
  • 分片集群RedisCluster中的出现严重的数据倾斜,导致某个节点的内存使用过大;

那么对于已经写入的数据,如何分析找出里面的bigkey进行优化呢?可以通过Redis官方客户端redis-cli的bigkeys参数来定位大Key分布。

shell> redis-cli -h 127.0.0.1 -p 18708 -a xxxx –bigkeys -i 0.01

[00.00%] Biggest string found so far ‘urlcount:www.guprocessorSuccessMid’ with 1 bytes

[00.01%] Biggest string found so far ‘TestDomain:www:config:scheduler’ with 3847 bytes

[00.03%] Biggest string found so far ‘TestDomain:www:config:scheduler’ with 211306 bytes

[00.88%] Biggest set found so far ‘specialTestJobSet:www’ with 20 members

[01.69%] Biggest list found so far ‘TestDomain:www:urlList’ with 9762 items

[07.13%] Biggest list found so far ‘TestDomain:bx:urlList’ with 457676 items

[07.39%] Biggest set found so far ‘specialTestJobSet:www’ with 100 members

[13.99%] Biggest string found so far ‘TestDomain:wwwe:config:scheduler’ with 540731 bytes

[18.74%] Biggest set found so far ‘TestJobSet’ with 300 members

[58.09%] Biggest string found so far ‘TestDomain:wwwrt:config:scheduler’ with 739024 bytes

[64.19%] Biggest string found so far ‘TestDomain:bx:config:scheduler’ with 1335468 bytes

Original: https://www.cnblogs.com/databasepub/p/16691141.html
Author: 数据库架构师
Title: Redis变慢?深入浅出Redis性能诊断系列文章(二)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/714172/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球