【Redis】KEYS命令存在的风险与通过SCAN命令进行安全处理

我尽可能简洁地介绍Redis中KEYS命令的危险性以及如何使用SCAN命令进行处理。

Redis,KEYS是什么

如果你能理解,请跳过。

Redis是一种KVS(键值存储)数据库,它是一种开源的(BSD许可证)内存数据结构存储(不是存储在磁盘上,而是在主内存中处理)。
据我所知,它经常用于存储Web常见的缓存。

这个结构非常简单,对于一个Key,只有一个对应的Value。

スクリーンショット 2020-06-26 19.06.13.png

处理缓存时,将“页面组件的URL”作为键,将“CSS和JavaScript等内容”保存为值。

KEYS是一种用于搜索与特定模式匹配的Key的方法(Redis(KEYS))。
由于需要与所有的Key和模式进行比较,时间复杂度为O(N)(N为数据库中Key的数量)。
换句话说,计算复杂度直接受保存的Key数量的影响。

以下是KEYS命令的示例。

redis> KEYS *name*
1) "lastname"
2) "firstname"

redis> KEYS a??
1) "age"

redis> KEYS *
1) "age"
2) "lastname"
3) "firstname"

通过使用KEYS进行全局搜索的风险

尽管这个操作的时间复杂度为O(N),但常数时间相当低。例如,Redis在一台入门级笔记本上可以在40毫秒内扫描一个100万个键的数据库。

根据 Redis 的公式参考资料(KEYS),简单的引用处理速度似乎很快。

当保存的键的数量达到“数百万、数千万”这样庞大的数字时,就会出现问题。这种悲剧的顺序如下所示。

スクリーンショット 2020-06-26 22.28.40.png

KEYS的响应太长,导致Redis无法返回任何结果。

Redis服务器通常只能进行单个操作,它是单线程的。因此,在查询“数百万、数千万”这样庞大数量的数据时,直到该操作完成才能返回响应。

2. 其他向Redis发送的GET请求等被堵塞住。

Redis在处理“数百万、数千万”数量级的引用时,对于Redis服务器的请求将不断增加。顺便说一下,如果Redis是多线程(与单线程相反,能够进行并行处理),即使有一个重型请求,但轻型处理仍然可以连续响应,从而实现最低限度的操作(稍后详细介绍)。

3. 期望Redis响应的应用端(如Rails)出现堵塞。

Redis无法长时间返回响应,因此应用程序将无法执行任何操作的时间增加,导致堵塞。

4. 无法向用户返回请求,最糟糕的情况下服务器崩溃。

当响应时间增加时,服务器的负载也会增加。结果,将花费更多时间来处理用户的请求,最坏的情况是由于高负载导致服务器崩溃的风险变得更高。

顺便提一句,由于考虑到这种危险性,官方参考资料中也不推荐使用。

警告:考虑将KEYS作为仅在生产环境中极度谨慎使用的命令。在执行大型数据库时,它可能会破坏性能。此命令旨在用于调试和特殊操作,比如更改键的布局。不要在普通应用代码中使用KEYS。如果您想要在键空间的子集中查找键的方法,考虑使用SCAN或sets。

使用扫描,在光标基础上进行检索。

在认识到通过KEYS获取密钥存在的风险之后,我们来考虑解决方案。

正如之前简单提到的,如果是使用多线程,即使有一个重型请求也不会造成堵塞。然而,这在现实中是不可能的,因此我们需要考虑另一种方法。这就是推荐使用的官方SCAN方法。

SCAN与KEYS类似,是一种搜索匹配模式的键的方法。 SCAN的特点是通过游标进行引用处理。这样可以分割引用而不是一次性引用。这里说的游标是指“指示下一个引用位置的东西”,如果没有下一个游标(引用结束),SCAN将返回“0”。详见Redis(SCAN)。

扫描命令的示例。

redis> scan 0
1) "17"
2)  1) "key:12"
    2) "key:8"
    3) "key:4"
    4) "key:14"
    5) "key:16"
    6) "key:17"
    7) "key:15"
    8) "key:10"
    9) "key:3"
   10) "key:7"
   11) "key:1"

redis> scan 17
1) "0"
2) 1) "key:5"
   2) "key:18"
   3) "key:0"
   4) "key:2"
   5) "key:19"
   6) "key:13"
   7) "key:6"
   8) "key:9"
   9) "key:11"

在编程实现中,我们需要通过SCAN重复请求直到光标达到”0″(即参考结束)。

结束

Redis的数据结构本身虽然简单,但是深入研究其内部处理会发现许多潜在的风险,这非常有趣。

如果有任何问题或编辑请求,请在评论中告知。

迟来的问候

我是@TsuboyaTaiki,在开始长期实习的第二个月。

我目前负责修复与Redis缓存相关的问题,这是在Rails应用程序的工作中。在此过程中,我学到了很多关于值搜索的知识,例如KEYS和SCAN,所以我想记录下来。

广告
将在 10 秒后关闭
bannerAds