为什么建议不要在Redis中使用密钥?

为什么建议不要在Redis中使用密钥?,redis,Redis,在Redis中,建议不要使用。为什么会这样?是因为它的时间复杂度是O(N)?或者其他原因。是的 时间复杂性非常糟糕。请注意,O(N)中的N指的是数据库中的密钥总数,而不是过滤器模式选择的密钥数。因此,对于生产数据库来说,这可能是一个非常大的数字 更糟糕的是,由于同一时间只能运行一个命令(Redis不是多线程的),其他所有操作都必须等待该键完成。我做了以下实验来证明KEYS命令是多么危险 当一个带有键的命令运行时,其他键命令正在等待运行时间。KEYS命令的一次运行有两个阶段,第一个阶段是从Redi

在Redis中,建议不要使用。为什么会这样?是因为它的时间复杂度是O(N)?或者其他原因。

是的

时间复杂性非常糟糕。请注意,
O(N)
中的
N
指的是数据库中的密钥总数,而不是过滤器模式选择的密钥数。因此,对于生产数据库来说,这可能是一个非常大的数字


更糟糕的是,由于同一时间只能运行一个命令(Redis不是多线程的),其他所有操作都必须等待该键完成。

我做了以下实验来证明KEYS命令是多么危险

当一个带有键的命令运行时,其他键命令正在等待运行时间。KEYS命令的一次运行有两个阶段,第一个阶段是从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个keys命令,将在Redis上逐个运行,当第一个命令完成,下一个命令运行时,第一个命令的结果必须存储在内存中,然后客户端才会使用它们。所有这些都需要内存,所以我可以想象一种情况,第五个客户端正在运行KEYS命令,但我们仍然需要保留第一个客户端的数据,因为它们仍然没有通过网络传输

让我们测试一下

场景:让我们使用200米大小(1000米物理内存)的Redis DB,检查一次按键执行需要多少内存,以及通过网络执行需要多长时间。然后模拟要运行的5个相同的按键命令,看看它是否会杀死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忙于执行该命令8秒,但该命令占用的内存为17秒。 ->只运行一个KEYS命令只会阻塞Redis 8秒,但不会导致OOM

让我们同时(几乎)运行两个KEYS命令(无论如何都会一个接一个地运行)

让我们同时(几乎)运行3个KEYS命令(无论如何都会一个接一个地运行)

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)
May 19 16:08:01 consumer2内核:[454881.744017]redis cli调用oom killer:gfp\u mask=0x6200ca(gfp\u HIGHUSER\u MOVABLE),nodemask=(null),order=0,oom\u score\u adj=0
May 19 16:08:01 consumer2内核:[454881.744180][](oom_kill_进程)来自[](内存不足+0x134/0x36c)
结论:

  • 我们可以杀死健康的Redis实例,它消耗了200万RAM,在操作系统上,只要运行一个接一个发出的4个键命令并一个接一个地运行,就可以释放70%的RAM。只是因为即使在Redis完成执行结果之后,结果也必须被缓冲
  • 使用maxmemory无法保护Redis不受这种行为的影响,因为内存使用不是SET命令的结果

好的。但即使SMEMBERS命令也是O(N),也可以使用它。因为这里N只是集合中的键数(不是整个数据库中的键数),N的最大值可以是2^32-1,这不是问题。我说的对吗?是的,除非那一套也很大(在你达到2^32套成员之前,你需要重新考虑设计)。谢谢。您是说redis一次只能执行一个命令,因为它是单线程的。但是通过时间复用,它不会同时处理其他命令吗?因此,另一个命令不必等到KEYS命令完成。请参考:此外,您所说的问题(等待命令完成)只有在EVAL/运行Lua脚本的情况下才会发生。我想您误解了链接的文章。所有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
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)