问题描述
最近碰到一前同事在群里聊天,说在因为错误使用keys
导致生产事故,下面是他的自白(事件回顾):
问题分析
为什么测试,灰度环境使用keys没有导致任何问题,而到生产环境会导致服务挂了呢?
这个问题首先得从keys
这个命令说起。
先看下官方对这个命令对说明:
Available since 1.0.0.
Time complexity: O(N) with N being the number of keys in the database, under the assumption that the key names in the database and the given pattern have limited length.
Returns all keys matching pattern.
While the time complexity for this operation is O(N), the constant times are fairly low. For example, Redis running on an entry level laptop can scan a 1 million key database in 40 milliseconds.
Warning
: consider KEYS as a command that should only be used in production environments with extreme care. It may ruin performance when it is executed against large databases. This command is intended for debugging and special operations, such as changing your keyspace layout. Don’t use KEYS in your regular application code. If you’re looking for a way to find keys in a subset of your keyspace, consider using SCAN or sets.
看不懂的同学我直接翻译下大致意思:
keys的时间复杂度是O(N),N为执行该命令下的数据库的key的数量,常数。
redis扫描key的速度很快,大约是40毫秒100w的key。
警告⚠️:keys
用在生产环境只能以极低频率执行。 在大数据库执行时会出现灾难性的性能。如果需要查询某些key,考虑使用SCAN
或者sets
。
一位老哥冒死执行的结果:
近1200w的key数量,执行时间约1.35s,大约100ms扫描100w的key。这速度比官方声明的要慢,可能是因为该服务器是单核的。
这个命令为什么会这么慢呢?
换个问法,为什么redis需要遍历所有的key才能找到我们需要的key呢?
- Redis是NoSQL型数据库,以hash数据结构存储的,所以才能实现高效的数据查询。而hash结构对于精确查找是非常快的,对于模糊查询,则无能为力。
- Redis的命令执行是单线程的,同一时间只能执行单个命令。单一长时间命令会堵塞后续。(可以通过
debug sleep 0.1
100ms 模拟执行长时间命令)
以上两点造成了KEYS
进行key查询需要遍历当前db的所有数据,以及当该命令执行完成的时候后续命令都会被堵塞。
因此在redis中执行的命令,尽量避免长时间堵塞命令。