4 votes

Améliorer les performances de Redis pour réduire les exceptions de timeout

J'ai une base de données Redis sur un serveur Centos, et 3 serveurs Windows y sont connectés avec environ 1 000 lectures/écritures par seconde, tous étant sur le même réseau local, de sorte que le temps de ping est inférieur à une milliseconde. Le problème est qu'au moins 5 % des opérations de lecture sont interrompues, alors que je lis au maximum 3 Ko de données dans une opération de lecture avec 'syncTimeout=15', ce qui est bien plus que la latence du réseau.

J'ai installé Redis sur bash sur mon Windows 10, et simulé le problème. J'ai également arrêté les opérations d'écriture. Cependant, le problème existe toujours avec des délais de 0,5 %, alors qu'il n'y a pas de latence réseau. J'ai également utilisé un serveur Centos sur mon réseau local pour simuler le problème, dans ce cas, j'ai besoin de 100 millisecondes pour 'syncTimeout' afin d'être sûr que le délai d'attente est inférieur à 1 %. J'ai envisagé d'utiliser des dictionnaires pour mettre en cache les données de Redis, de sorte qu'il n'est pas nécessaire de demander chaque élément, et je peux profiter du pipeline. Mais je suis tombé sur StackRedis.L1 qui est développé comme un cache L1 pour Redis, et il n'est pas sûr de la mise à jour du cache L1.

Voici mon code pour simuler le problème :

    var connectionMulti = ConnectionMultiplexer.Connect(
            "127.0.0.1:6379,127.0.0.1:6380,allowAdmin=true,syncTimeout=15");

    // 100,000 keys
    var testKeys = File.ReadAllLines("D:\\RedisTestKeys.txt");

    for (var i = 0; i < 3; i++)
    {
        var safeI = i;
        Task.Factory.StartNew(() =>
        {
            var serverName = $"server {safeI + 1}";
            var stringDatabase = connectionMulti.GetDatabase(12);
            PerformanceTest($"{serverName} -> String: ",
                key => stringDatabase.StringGet(key), testKeys);
        });
    }

et la méthode PerformanceTest est :

private static void PerformanceTest(string testName, Func<string, RedisValue> valueExtractor,
        IList<string> keys)
    {
        Task.Factory.StartNew(() =>
        {
            Console.WriteLine($"Starting {testName} ...");
            var timeouts = 0;
            var errors = 0;
            long totalElapsedMilliseconds = 0;

            var stopwatch = new Stopwatch();
            foreach (var key in keys)
            {
                var redisValue = new RedisValue();
                stopwatch.Restart();
                try
                {
                    redisValue = valueExtractor(key);

                }
                catch (Exception e)
                {
                    if (e is TimeoutException)
                        timeouts++;
                    else
                        errors++;
                }
                finally
                {
                    stopwatch.Stop();
                    totalElapsedMilliseconds += stopwatch.ElapsedMilliseconds;
                    lock (FileLocker)
                    {
                        File.AppendAllLines("D:\\TestResult.csv",
                            new[]
                            {
                                $"{stopwatch.ElapsedMilliseconds.ToString()},{redisValue.Length()},{key}"
                            });
                    }
                }
            }

            Console.WriteLine(
                $"{testName} {totalElapsedMilliseconds * 1.0 / keys.Count} (errors: {errors}), (timeouts: {timeouts})");
        });
    }

Je m'attends à ce que toutes les opérations de lecture soient effectuées avec succès en moins de 15 millisecondes. Pour y parvenir, considérer le cache L1 pour un cache Redis est-il une bonne solution ? (C'est très rapide, à l'échelle de la nanoseconde, mais comment faire pour la syncronisation) Ou bien Redis peut être amélioré par un clustering ou autre chose ? (Alors que je l'ai testé en bash sur mon PC, et je n'ai pas reçu le résultat attendu)

2voto

Marc Gravell Points 482669

Ou Redis peut-il être amélioré par la mise en grappe ou autre chose ?

Redis peut être regroupés, de différentes manières :

  • "redis "normal" peut être répliqué vers des nœuds secondaires en lecture seule, sur la même machine ou sur des machines différentes ; vous pouvez ensuite envoyer du trafic en lecture vers certaines des répliques.
  • redis "cluster" existe, ce qui vous permet de diviser (shard) l'espace clé sur plusieurs primaires, en envoyant des requêtes appropriées à chaque nœud.
  • redis "cluster" peut également utiliser des répliques en lecture seule des nœuds sharded.

Que ce soit approprié ou utile est contextuelle et nécessite des connaissances et des tests locaux.

Pour y parvenir, considérer le cache L1 pour un cache Redis est-il une bonne solution ?

Oui, c'est une bonne solution. Une demande que vous ne faites pas est beaucoup plus rapide (et a beaucoup moins d'impact sur l'impact) qu'une demande que vous do faire. Il existe des outils pour aider à l'invalidation du cache, y compris l'utilisation de l'API pub/sub pour les invalidations. Redis vNext se penche également sur des API de connaissance supplémentaires spécifiquement pour ce type de scénario L1.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X