StackExchange.Redis扫描x个键

6
我有一个 Redis 数据库,里面有成千上万个键,我目前正在运行以下代码来获取所有的键:
string[] keysArr = keys.Select(key => (string)key).ToArray();

但是由于我有很多密钥,这需要很长时间。我想限制读取密钥的数量。因此,我正在尝试运行一个执行命令,每次获取100个密钥:

var keys = Redis.Connection.GetDatabase(dbNum).Execute("scan", 0, "count", 100);

这个命令成功运行了,但是由于它是私有的,无法访问该值,即使RedisResult类提供了一个显式转换,也无法进行转换:

public static explicit operator string[] (RedisResult result);

有没有办法从 Redis 中一次获取 x 个键的值?

谢谢

2个回答

12

SE.Redis中的.Keys()方法位于IServer API上,完全封装了SCAN的语义。如果可能,请仅使用此方法,并每次以100个数据为单位进行处理。通常编写分批函数非常容易,即:

ExecuteInBatches(server.Keys(), 100, batch => DoSomething(batch));

使用:

public void ExecuteInBatches<T>(IEnumerable<T> source, int batchSize,
        Action<List<T>> action)
{
    List<T> batch = new List<T>();
    foreach(var item in source) {
        batch.Add(item);
        if(batch.Count == batchSize) {
             action(batch);
             batch = new List<T>(); // in case the callback stores it
        }
    }
    if (batch.Count != 0) {
        action(batch); // any leftovers
    }
}

枚举器会负责推进光标。


可以使用Execute,但这是很多工作!此外,SCAN不能保证每页返回多少项;可能是零 - 也可能是你要求的三倍。 它只是...提供指导。

顺便说一下,强制转换失败的原因是因为SCAN没有返回一个string[] - 它返回一个包含两个项的数组,第一个项是“下一个”光标,第二个是键。所以也许应该这样做:

var arr = (RedisResult[])server.Execute("scan", 0);
var nextCursor = (int)arr[0];
var keys = (RedisKey[])arr[1];

但这只是以更困难、效率显著低下的方式重新实现了IServer.KeysServerResult并不是存储数据的理想方式,只是在ExecuteScriptEvaluate情况下必需的)。


应该将RedisResult数组中的光标值转换为Int64,而不是(int)arr[0];。我认为这将与IScanningResult.Cursor是一个Int64类型保持一致。(天哪,我正在尝试使用ExecuteCommand直接使用SCAN命令)。 - Stephen Swensen
1
@StephenSwensen 可能;实际上,考虑到还有一种情况可以使用string——我记得曾经有一个Redis集群代理(可能现在已经被废弃了)使该值不是整数(尽管这是在本答案之后出现的)。 - Marc Gravell

1

我会使用由Microsoft在这里概述的.Take()方法。

从序列的开头返回指定数量的连续元素。

它看起来会像这样:

//limit to 100
var keysArr = keys.Select(key => (string)key).Take(100).ToArray();

我该如何获取接下来的一百个?从扫描命令中,您可以获得下一个查询的哈希值。 - Person
@Person 你需要实现一个分页的 .Skip() 程序,就像这样:https://dev59.com/hWUp5IYBdhLWcg3wPFtK - AussieJoe
5
在这种情况下,使用skip和take获取页面会非常不好。因为这种意图无法传达给服务器,所以你正在实现一个三角形查询(意味着第一个项目很便宜,下一页更贵,依此类推,直到最后一页非常昂贵)。在这个特定情况下,这不是解决问题的好方法。 - Marc Gravell
@MarcGravell 我认为这取决于所需输出的范围。如果你只需要几百个,那并不一定是坏事。 - AussieJoe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接