为什么使用 StackExchange.Redis 扫描 Redis 密钥时 KeyAsync 循环会挂起?

问题描述 投票:0回答:1

我正在尝试使用 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();
c# .net redis stackexchange.redis
1个回答
0
投票

首先要检查的是:幕后是否发生了什么?最简单的方法是在 控制台会话中使用 monitor 来观察正在进行的流量 - 我希望看到一系列

redis-cli
操作被发出(每个操作都有不同的令牌)。
注意:Redis 的本质是,如果它是一个非常大的数据库且只有很少的匹配项,那么经过过滤的 SCAN ... 在找到匹配项之前可能需要 
很多很多
操作 - 它不能简单地直接跳转到匹配项;相反,在找到第一个匹配项(如果有的话!)之前,它会执行

lots

零个匹配项的往返。

如果这是问题所在:您可以通过增加页面大小来显着减少所花费的时间,即每次往返要做的工作量,请注意它仍然可能找不到该页面的任何匹配项。因此,您可以在屈服之前检查 500 个键或 5000 个键,而不是检查 50 个键。 API 上有为此目的的可选参数。
另一种可能性是您有一个低级服务器并且它正在使用 

SCAN

(而不是 KEYS);在

that

场景中,存在同样的问题,除了

SCAN
操作还会阻止服务器处理其他连接上的并发请求;不好!
    
© www.soinside.com 2019 - 2024. All rights reserved.