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