我正在尝试使用 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
操作被发出(每个操作都有不同的令牌)。 注意:Redis 的本质是,如果它是一个非常大的数据库且只有很少的匹配项,那么经过过滤的 SCAN ...
在找到匹配项之前可能需要 很多很多操作 - 它不能简单地直接跳转到匹配项;相反,在找到第一个匹配项(如果有的话!)之前,它会执行 lots
零个匹配项的往返。
如果这是问题所在:您可以通过增加页面大小来显着减少所花费的时间,即每次往返要做的工作量,请注意它仍然可能找不到该页面的任何匹配项。因此,您可以在屈服之前检查 500 个键或 5000 个键,而不是检查 50 个键。 API 上有为此目的的可选参数。 另一种可能性是您有一个低级服务器并且它正在使用SCAN
(而不是 KEYS
);在
场景中,存在同样的问题,除了
SCAN
操作还会阻止服务器处理其他连接上的并发请求;不好!