记一次使用redis的keys命令导致redis雪崩的生产事故

  |  

出现原因

由于需要每时每刻监听三方报错超过十次发送机器人警告,所以每次报错我这边就存入一个(key+时间),同时过期时间为1小时,所以查询当前的一个小时内错误次数,使用 keys key*查询key的数量,就知道错误次数,本来一直没有问题,跑了大半年了,突然有一天redis的cpu100%,直接雪崩,导致客户无法登录,后面排查原因是有个三方一直报错,代码又重试,由于需要机器人提醒(这个时候我们redis key已经超过80w个key),导致一秒钟查上千次询keys命令,最后redis直接爆炸。

官方对keys这个命令的说明

keys的时间复杂度是O(N),N为执行该命令下的数据库的key的数量,常数。
redis扫描key的速度很快,在入门笔记本大约是40毫秒100w个。
警告⚠️:keys用在生产环境只能以极低频率执行。 在大数据库执行时会出现灾难性的性能。如果需要查询某些key,考虑使用SCAN或者sets。

keys命令为什么会这么慢呢?

(1) Redis是NoSQL型数据库,以hash数据结构存储的,所以才能实现高效的数据查询。而hash结构对于精确查找是非常快的,对于模糊查询,则无能为力。
(2) Redis的命令执行是单线程的,同一时间只能执行单个命令。单一长时间命令会堵塞后续。(可以通过debug sleep 0.1100ms 模拟执行长时间命令)
以上两点造成了KEYS进行key查询需要遍历当前db的所有数据,以及当该命令执行完成的时候后续命令都会被堵塞。
因此在redis中执行的命令,尽量避免长时间堵塞命令。

解决

后面实在没有办法退而求其次,把所有的keys命令移除了,只是简单记录次数了,一直自己自增,半个小时自动过期。还有一种方法就是保存数据库,每进来一条错误日志,就保存一次,按照时间查询这样也可以达到上面的效果,每天或者每月去清理之前的旧数据,防止数据太多。

看网上推荐使用Scan来替换keys,它是增量地迭代 ,它们每次执行都只会返回少量元素,不会阻塞服务器, 所以这些命令可以用于生产环境, 而不会出现像 KEYS 命令、 SMEMBERS 命令带来的问题。但是会出现.同一个元素可能会被返回多次,所以就没有使用。

文章目录
  1. 1. 出现原因
  2. 2. 官方对keys这个命令的说明
  3. 3. keys命令为什么会这么慢呢?
  4. 4. 解决