Redis性能问题诊断以及scan命令耗时分析

发布时间 2023-04-10 14:08:52作者: 济南小老虎

Redis性能问题诊断以及scan命令耗时分析


摘要

最近公司有项目反馈卡顿.
卡顿一小时后自己被拉入群聊. 
同事已经基本上定位到问题原因.
我这边想使用朴素的性能观点进行一下性能问题的拆解
为了提高自己. 

用到的一些脚本

echo "info" |redis-cli -p 6379 -a Yourpassword >`date +%Y%m%d%H%M`.txt
# 获取信息并且保存
echo "slowlog get 100 " |redis-cli -p 6379 -a Yourpassword >`date +%Y%m%d%H%M`.txt
# 获取较慢的命令
redis-cli -a password -p port --bigkeys >`date +%Y%m%d%H%M`_bigkeys.txt
redis-cli -a password -p port --hotkeys >`date +%Y%m%d%H%M`_hotkeys.txt
# 注意大key基本上可以查到
# hotkeys需要有memory_policy. 需要注意.

scan耗时统计与分析-先说结论

scan 命令 时间复杂度为 O(n)
理论上这个O(n) 很多的是靠 count来进行指定. 
并且理论上一个键值对的消耗时间为 1 微秒左右. 

虽然他可以在 count 值的微秒数之内返回, 但是如果要遍历所有的键值对, 并且多个client同时遍历
那么时间损耗是非常恐怖的. 

又因为redis是单线程io复用的模型. 所以他会导致其他的核心业务卡断. 

如果非必要建议不要使用scan命令, 在高并发,大量key的场景下性能表现并不好. 

scan耗时统计与分析-预制数据

scan 命令说明
scan 其实是对redis所有的键值对进行一次遍历
他的时时间复杂度是 O(n) 当然了他的时间复杂度可以通过 count 进行控制.
但是我们的场景是 多台服务器同事连接, 如果都进行 scan的话 对主线程的影响就比较恶劣了. 

所以计划做一个测试. 首先命令为:
scan 0 match zhaobsh* count 1000000
命令说明
scan 
第一个0 表示游标开始
第二个 match zhaobsh* 便于对符合此类的进行查询
第三个 count 1000000 表示一次性查询所少个key

因为我是测试环境, 键值对不多, 所以想着能够一次性多查询一下以便验证时间

测试数据创建
创建三个脚本,分别是10万,100万,1000万个键值对.
time for i in {1..100000} ; do  echo set key${i} value${i} >>/root/100000set.txt ; done
time for i in {1..1000000} ; do  echo set key${i} value${i} >>/root/1000000set.txt ; done
time for i in {1..10000000} ; do  echo set key${i} value${i} >>/root/10000000set.txt ; done
执行导入:
time cat /root/1000000set.txt |redis-cli -a xxxxx --pipe

注意之前总结过 -pipe 可以快速预祝数据. 
1000万预制数据大约耗时: 20秒

scan耗时统计与分析-统计方法

分别插入三次数据量的数据. 然后都执行这个命令
scan 0 match zhaobsh* count 1000000
然后通过
slowlog get 1 后去最慢的命令 . 的出结论为
不同keys量情况下的速度

scan耗时统计与分析-部分结论

keys值数量 slowlog耗时
10万 40毫秒
100万 440毫秒
1000万 1008毫秒

scan 算法分析

自己进行了一下验证, 如果直接一次scan 一千万的记录
耗时为: 10.15秒. 
理论上scan 一个键值对的时间为 1微秒左右.  

如果redis里面有 1000万个key的话  60台服务器如果同时进行一次所有的scan
那么搞不好至少会有在 运行期间内产生总计 600S 的延迟时间. 

测试结果

127.0.0.1:6379> scan 0 match zhaobsh* count  10000000
1) "14680063"
2) (empty array)
(10.15s)
127.0.0.1:6379> slowlog get 1
1) 1) (integer) 13
   2) (integer) 1681104753
   3) (integer) 10147528
   4) 1) "scan"
      2) "0"
      3) "match"
      4) "zhaobsh*"
      5) "count"
      6) "10000000"
   5) "127.0.0.1:27925"
   6) ""
127.0.0.1:6379> dbsize
(integer) 10000001
127.0.0.1:6379> 

redis-benchmark同步验证

测试命令:
./redis-benchmark -a xxxx  -r 10000 -n 100 -c 8000 scan 0 match zhaobsh* count  10000
10000个随机key, 测试100次, 使用 80000个client进行测试验证.
被测试的命令为: scan 0 match zhaobsh* count  10000

一万个key时count 一万时:
Summary:
  throughput summary: 99.11 requests per second
  latency summary (msec):
          avg       min       p50       p95       p99       max
      993.304    17.472  1004.543  1005.055  1005.055  1005.055

十万个key时 count 十万时
Summary:
  throughput summary: 11.54 requests per second
  latency summary (msec):
          avg       min       p50       p95       p99       max
     2970.660   135.680  3000.319  3000.319  3000.319  3000.319
五十万个key时 count 五十万时
Summary:
  throughput summary: 3.46 requests per second
  latency summary (msec):
          avg       min       p50       p95       p99       max
     2972.532   322.816  3000.319  3000.319  3000.319  3000.319

测试时CPU的情况

   PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND 
 72489 root      20   0 2148060   1.4g   1504 R 99.9  0.1  80:22.77 redis-server 
 72490 root      20   0 2148060   1.4g   1504 S  0.0  0.1   0:00.00 bio_close_file 
 72491 root      20   0 2148060   1.4g   1504 S  0.0  0.1   0:00.00 bio_aof_fsync  
 72492 root      20   0 2148060   1.4g   1504 S  0.0  0.1   0:00.00  bio_lazy_free 
 72493 root      20   0 2148060   1.4g   1504 S  0.0  0.1   1:00.51 jemalloc_bg_th