在生产中使用KEYS
首先,重要的是要了解为什么不应在生产中使用KEYS。
KEYS 的时间复杂度为 O(N),其中 N 是整个数据库的元素个数。不是有多少满足模式。由于只能同时运行一个命令(Redis 不是多线程的),因此其他所有操作都必须等待该 KEYS 完成。
见:Why KEYS is advised not to be used in Redis?
根据文档:
虽然此操作的时间复杂度为 O(N),但常数时间相当低。例如,在入门级笔记本电脑上运行的 Redis 可以在 40 毫秒内扫描 100 万个密钥数据库。
警告:将 KEYS 视为仅应极其小心地在生产环境中使用的命令。当它针对大型数据库执行时,它可能会破坏性能。此命令用于调试和特殊操作,例如更改键空间布局。不要在常规应用程序代码中使用 KEYS。如果您正在寻找一种在您的键空间子集中查找键的方法,请考虑使用 SCAN 或集合。
这表明如果您的记录少于一百万,使用keys 应该没问题。但是随着您的数据库增长,或者您有更多的并发用户,可能会出现问题。
KEYS 的替代品
扫描
KEYS 的常见替代品是SCAN(您正在使用它)。请注意,这仍然是一个糟糕的选择,因为它与KEYS 非常相似,只是结果以块的形式返回,并且具有 O(N),其中 N 是整个数据库的元素数。
优点是不会阻塞服务器,但时间复杂度和KEYS一样。事实上,如果你只想得到结果,而不关心阻塞数据库,它可能会比KEYS 慢,因为它必须执行多个查询(如你所见)。
HSET
更好的选择是使用 HSET。
当您想将元素放入HSET 时,请使用:
HSET client/9999/products "id_547" "Book"
HSET client/9999/products "whatever_key_you_want" "Laptop"
$this->_redis_client->hset('client/9999/products', 'id_547', 'Book');
$this->_redis_client->hset('client/9999/products', 'whatever_key_you_want', 'Laptop');
当你想得到所有的钥匙时,只需使用HKEYS:
HKEYS client/9999/products
1) id_547
2) whatever_key_you_want
$this->_redis_client->hkeys('client/9999/products')
与 KEYS 不同,HKEYS 的复杂度为 O(N),其中 N 是散列的大小(不是整个数据库的大小)。
如果密钥变得非常大,您可能需要使用HSCAN。
性能测试
在大约 2,000,000 个项目的 redis 数据库中:
for ($i = 0; $i <= 100; $i++) {
$client->set("a:{$i}", "value{$i}");
}
for ($i = 0; $i <= 100; $i++) {
$client->hset("b", $i, "value{$i}");
}
测试 1:键
$start = microtime(true);
var_dump(count($client->keys('a:*')));
$end = microtime(TRUE);
echo ($end - $start) . "s\n";
测试 2:扫描
$start = microtime(true);
$count = 0;
foreach (new Keyspace($client, 'a:*') as $key) {
$count++;
}
$end = microtime(TRUE);
echo ($end - $start) . "s\n";
测试 3:HKEYS
$start = microtime(true);
var_dump(count($client->hkeys('b')));
$end = microtime(TRUE);
echo ($end - $start) . "s\n";
结果
- 键:~0.21s
- 扫描:~20 秒
- HKEYS:~0.01s
如您所见,HKEYS 速度更快,并且不受数据库大小的影响。
我还建议使用 redis PECL 扩展而不是 predis:
有了 Redis 扩展,我得到了:
- KEYS:~0.21s(变化不大)
- 扫描:~17 秒(小幅增加)
- HKEYS:~0.0004s(快得多!)