我正在尝试使用 StackExchange.Redis 库扫描与特定模式匹配的所有 Redis 键。但是,await foreach
在扫描键时,我的代码在语句处挂起。以下是我得到的:
public class DataMigration(IConnectionMultiplexer connection) : IHostedService
{
public async Task StartAsync(CancellationToken cancellationToken)
{
const string pattern = "{hangfire}:recurring-job:EmailNotificationJob:*";
List<RedisKey> keys = [];
var db = connection.GetDatabase();
foreach (var endpoint in connection.GetEndPoints())
{
var server = connection.GetServer(endpoint);
if (!server.IsConnected || server.IsReplica)
{
continue;
}
// Using SCAN for pagination
await foreach (var key in server.KeysAsync(database: db.Database, pattern: pattern).WithCancellation(cancellationToken))
{
keys.Add(key); // this is never reached
}
}
Console.WriteLine($"Found {keys.Count} keys to update."); // this is never reached
}
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddSingleton<IConnectionMultiplexer>(serviceProvider =>
{
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
const string redisHost = ...
const int redisPort = ...
const string redisPassword = ...
var redisConfiguration = new ConfigurationOptions
{
EndPoints = { $"{redisHost}:{redisPort}" },
Password = redisPassword,
Ssl = true,
AbortOnConnectFail = false, // Keep trying to connect
ConnectTimeout = 15000, // 15 seconds timeout
SyncTimeout = 15000, // 15 seconds timeout for synchronous operations
LoggerFactory = loggerFactory
};
return ConnectionMultiplexer.Connect(redisConfiguration);
});
builder.Services.AddHostedService<DataMigration>();
var app = builder.Build();
app.Run();
这里要检查的第一件事是:幕后发生了什么事?最简单的方法是使用
monitor
控制台redis-cli
会话来观察正在进行的流量 - 我希望看到发出一系列SCAN ...
操作(每个操作都有不同的令牌)。如果服务器真的很忙,那么使用这种方法可能不切实际;另一种选择是在客户端monitor
使用RESP 日志记录。注意:redis 的本质是,如果数据库非常大但匹配项却很少,则过滤后
SCAN
可能需要进行很多次操作才能找到匹配项 - 它不能直接跳转到匹配项;相反,它会在找到第一个匹配项(如果有的话!)之前进行大量没有匹配项的往返。如果这是问题所在:您可以通过增加页面大小(即每次往返需要完成的工作量)来显著减少所需的时间,但请注意,它仍然可能找不到该页面的任何匹配项。因此,您可以检查 500 个键或 5000 个键,而不是在放弃之前检查 50 个键。API 上有用于此的可选参数。
另一种可能性是,您有一个低级服务器,它正在使用
KEYS
(而不是SCAN
);在这种情况下,同样的问题也适用,只是KEYS
操作还会阻止服务器处理其他连接上的并发请求;不好!如果您没有看到一系列
SCAN
操作,或者单个长时间运行的KEYS
操作......也许请让我知道您看到了什么?附注:通过
SCAN
或抓取 redis 键空间效率不高KEYS
,您应避免将其作为常规应用程序逻辑的一部分。它适用于管理目的,例如,查看或分类数据库以查看存在哪些类型的数据以及每种类型的数据占用多少空间。对于应用程序逻辑,您通常应使用 redis API 执行自己的索引,这样您就无需抓取;例如,保留适用于某些规则的键的哈希值或集合。