代码之家  ›  专栏  ›  技术社区  ›  nop

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

  •  1
  • nop  · 技术社区  · 10 月前

    我试图使用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();
    
    1 回复  |  直到 10 月前
        1
  •  3
  •   Marc Gravell    10 月前

    首先要检查的是:幕后有什么事情发生吗?这个 最简单 做到这一点的方法是使用 monitor 在一个 redis-cli 控制台会话以查看正在进行的流量-我会 期待 看到一阵骚动 SCAN ... 正在发出的操作(每个操作都有不同的令牌)。如果服务器是 真的很忙 ,使用起来可能不切实际 班长 为此;另一种选择是使用 RESP logging 在客户。

    注:redis的本质是过滤 SCAN 可以要求 许多许多 如果它是一个非常大的数据库,只有很少的匹配项,它不能直接跳到匹配项之前的操作;相反,它会做的 太多了 在找到第一个匹配项(如果有的话!)之前,它会进行零匹配的往返。

    如果这是问题所在:您可以通过增加页面大小来显著减少所需的时间,即每次往返的工作量,请注意 仍然可能找不到该页面的任何匹配项 。因此,与其在屈服前检查50个密钥,不如检查500个密钥或5000个密钥。API上有用于此的可选参数。

    另一种可能性是,您有一个低级服务器,它正在使用 KEYS (而不是 扫描 ); 在里面 那个 除了以下情况外,同样的问题也适用 钥匙 操作还阻止服务器处理其他连接上的并发请求;不好的!

    如果你 不要 要么看到一阵骚动 扫描 操作,或单个长时间运行 钥匙 操作。。。也许让我知道你 看到了吗?

    附带说明:通过以下方式抓取redis键空间 扫描 钥匙 效率不高 ,您应该避免将此作为常规应用程序逻辑的一部分。它适用于管理目的,例如查看或分类数据库,以查看存在哪些类型的数据,以及每种数据占用了多少空间。For 应用 逻辑上,您通常应该使用redisneneneba API执行自己的索引,这样您 不需要 爬行;例如,保持 搞砸 设置 适用于某些规则的密钥。