Python 和 Redis。为什么 scan_iter() 和 mget() 比 keys() 慢这么多?

Bag*_*era 9 python redis

我编写了一个脚本来比较 redis 库中方法之间的查询速度。

据我了解,我不应该使用 redis.keys() 因为它是一个阻塞函数。首选方法是 scan_iter(),它不会阻塞。这就说得通了。但我不明白为什么 scan_iter 这么慢。

该脚本使用三种技术来查询 Redis 数据库。

  1. 使用 redis.keys() 获取所有密钥。使用 redis.mget() 获取所有值。
  2. 使用带有 redis.scan_iter 和 redis.get() 的 for 循环来获取值。(注意我不需要键,只需要值)
  3. 使用 redis.scan_iter() 获取所有键。使用 redis.mget() 获取所有值。

选项 1 是迄今为止最快的。选项 2 的速度要慢 20 到 30 倍。选项 3 平均慢 4 倍。

为什么是这样?我的代码写错了吗?难道是我方法不对?redis.keys() 方法实际上是最佳选择吗?

from datetime import datetime
from time import sleep
import redis
r = redis.StrictRedis(host='192.168.3.16', port=6379, decode_responses=True, db= 0)

def query_keys():
    start = datetime.now()
    redis_keys = r.keys(pattern='*')
    redis_keys = [x for x in redis_keys if not x.startswith('1_')]
    values = r.mget(redis_keys)
    end = datetime.now()
    print(str(len(values)) + ' values queried in ' + str(end - start) + ' with keys and mget.')
    
def query_scaniter():
    start = datetime.now()
    values = []
    for s in r.scan_iter():
        if not s.startswith('1_'):
            values.append(r.get(s))
    end = datetime.now()
    print(str(len(values)) + ' values queried in ' + str(end - start) + ' with scan_iter.')
    
def query_scaniter_mget():
    start = datetime.now()
    redis_keys = []
    for s in r.scan_iter():
        if not s.startswith('1_'):
            redis_keys.append(s)
    values = r.mget(redis_keys)
    end = datetime.now()
    print(str(len(values)) + ' values queried in ' + str(end - start) + ' with scan_iter and mget.')

for i in range(3):
    query_keys()
    query_scaniter()
    query_scaniter_mget()
    print('\n')
    sleep(5)

Output:
3532 values queried in 0:00:00.046872 with keys and mget.
3532 values queried in 0:00:00.781314 with scan_iter.
3532 values queried in 0:00:00.109385 with scan_iter and mget.


3526 values queried in 0:00:00.031245 with keys and mget.
3522 values queried in 0:00:00.812616 with scan_iter.
3522 values queried in 0:00:00.125007 with scan_iter and mget.


3529 values queried in 0:00:00.031246 with keys and mget.
3531 values queried in 0:00:00.797011 with scan_iter.
3530 values queried in 0:00:00.109357 with scan_iter and mget.
Run Code Online (Sandbox Code Playgroud)

Kev*_*nry 7

这些结果符合预期。

避免的原因KEYS不是它速度慢,而是它会阻塞服务器,阻止它满足其他客户端请求。相比之下,SCAN一次仅返回一些结果,从而允许服务器保持对所有客户端的响应。

代价是进行大量SCAN调用会带来额外的开销(包括所有这些往返的网络开销)。所以你的客户端的总时间会更长,但你不会削弱服务器,这通常是一个更重要的考虑因素。

MGET请注意,如果您有大量密钥,您可能会遇到同样的问题。最可扩展的解决方案(与最快的不同)是使用 一次获取几个密钥SCAN,然后获取MGET这些密钥。