在Redis中,建议不要使用{{3}}。为什么会这样?是因为它的时间复杂度是O(N)吗?或其他原因。
答案 0 :(得分:1)
是
时间复杂性非常糟糕。请注意N
中的O(N)
是指数据库中的键总数,而不是过滤器模式选择的键数。因此,对于生产数据库来说,这可能是一个非常大的数字。
更糟糕的是,由于只有一个命令可以同时运行(Redis不是多线程的),所以其他一切都必须等待该KEYS完成。
答案 1 :(得分:0)
我做了以下实验来证明KEYS命令有多危险。
当一个带有 KEYS 的命令运行时,其他 KEYS 命令正在等待运行时间。 KEYS命令的一次运行有2个阶段,第一个是从Redis获取信息,第二个是发送给客户端。
$ time src/redis-cli keys "*" | wc -l
1450832
real 0m17.943s
user 0m8.341s
$ src/redis-cli
127.0.0.1:6379> slowlog get
1) 1) (integer) 0
2) (integer) 1621437661
3) (integer) 8321405
4) 1) "keys"
2) "*"
因此,它在 Redis 上运行了 8 秒,然后通过管道传输到“wc”命令。 Redis 在 8 秒内完成了这个命令,但 'wc' 命令需要 17 秒的数据来完成计算。所以内存缓冲区必须至少存在 17 秒。现在,让我们想象一下网络上的客户端,这些数据也必须传送到客户端。如果我们有 10 个键命令,它们将一个一个地在 Redis 上运行,当第一个命令完成和下一个命令运行时,第一个命令的结果必须存储在内存中,然后客户端才会使用它们。这一切都需要内存,所以我可以想象这样一种情况,第 5 个客户端正在运行 KEYS 命令,但我们仍然需要保留第一个客户端的数据,因为它们仍然没有通过网络传输。
让我们测试一下。
场景:让我们有一个 200M 大小(1000M 物理内存)的 Redis DB,并检查一次执行 KEYS 需要多少内存,以及通过网络完成需要多长时间。然后模拟运行5个相同的KEYS命令,看是否杀死Redis。
$ src/redis-cli info memory
used_memory_human:214.17M
total_system_memory_human:926.08M
When run from the same node:
$ time src/redis-cli keys "*" | wc -l
1450832
real 0m17.702s
user 0m8.278s
$ free -m
total used free shared buff/cache available
Mem: 926 301 236 24 388 542
Mem: 926 336 200 24 388 507
Mem: 926 368 168 24 388 475
Mem: 926 445 91 24 388 398
Mem: 926 480 52 24 393 363
Mem: 926 491 35 24 399 352
-> looks like it consumed 190M for the KEYS command
-> 所以,Redis忙了8s的命令,但是这个命令消耗了17s的内存。 -> 只运行一个KEYS命令只会阻塞Redis 8s,但不会导致OOM
让我们(几乎)同时运行 2 个 KEYS 命令(无论如何都会一个接一个地运行)
$ time src/redis-cli keys "*" | wc -l &
$ time src/redis-cli keys "*" | wc -l &
$ free -m
total used free shared buff/cache available
Mem: 926 300 430 24 194 546
Mem: 926 370 361 24 194 477
Mem: 926 454 276 24 194 393
Mem: 926 589 141 24 194 258
Mem: 926 693 37 24 194 154
-> now we used 392M memory for 26s, while Redis is hung for 17s
-> but we still have a running Redis
让我们(几乎)同时运行 3 个 KEYS 命令(无论如何都会一个接一个地运行)
$ time src/redis-cli keys "*" | wc -l &
$ time src/redis-cli keys "*" | wc -l &
$ time src/redis-cli keys "*" | wc -l &
$ free -m
total used free shared buff/cache available
Mem: 926 299 474 23 152 549
Mem: 926 385 388 23 152 463
Mem: 926 512 261 23 152 336
Mem: 926 573 200 23 152 275
Mem: 926 711 61 23 152 136
Mem: 926 842 21 21 62 17
-> now we used 532M memory for 36s, while Redis is hung for 26s
-> but we still have a running Redis
Let's run 4 KEYS commands at the (almost) same time (that will run one after another anyway)
$ time src/redis-cli keys "*" | wc -l &
$ time src/redis-cli keys "*" | wc -l &
$ time src/redis-cli keys "*" | wc -l &
$ time src/redis-cli keys "*" | wc -l &
-> that kills Redis
Redis 日志中没有任何内容:
2251:C 19 May 16:03:05.355 * DB saved on disk
2251:C 19 May 16:03:05.379 * RDB: 2 MB of memory used by copy-on-write
1853:M 19 May 16:03:05.432 * Background saving terminated with success
在/var/log/messages
May 19 16:08:01 consumer2 kernel: [454881.744017] redis-cli invoked oom-killer: gfp_mask=0x6200ca(GFP_HIGHUSER_MOVABLE), nodemask=(null), order=0, oom_score_adj=0
May 19 16:08:01 consumer2 kernel: [454881.744180] [<8023bdb8>] (oom_kill_process) from [<8023c6e8>] (out_of_memory+0x134/0x36c)
结论: