Redis
 sql >> Base de données >  >> NoSQL >> Redis

Exécution parallèle avec StackExchange.Redis ?

Actuellement, votre code utilise l'API synchrone (StringSet ), et est chargé par 10 threads simultanément. Cela ne présentera aucun défi appréciable pour SE.Redis - cela fonctionne très bien ici. Je soupçons qu'il s'agit véritablement d'un délai d'attente où le serveur a pris plus de temps que vous ne le souhaiteriez pour traiter certaines des données, probablement également lié à l'allocateur du serveur. Une option consiste donc simplement à augmenter un peu le délai d'attente . Pas beaucoup... essayez 5 secondes au lieu de 1 seconde par défaut. Il est probable que la plupart des opérations fonctionnent très rapidement de toute façon.

En ce qui concerne l'accélération :une option consiste à ne pas attendre - c'est-à-dire conserver les données en pipeline. Si vous vous contentez de ne pas vérifier chaque message pour un état d'erreur, alors une façon simple de le faire est d'ajouter , flags: CommandFlags.FireAndForget à la fin de votre StringSet appel. Dans mes tests locaux, cela a accéléré l'exemple 1M de 25 % (et je soupçonne que le reste du temps est en fait consacré à la sérialisation des chaînes).

Le plus gros problème que j'ai eu avec l'exemple 10M était simplement la surcharge de travailler avec l'exemple 10M - d'autant plus que cela prend énormément de mémoire pour le redis-server et l'application, qui (pour émuler votre configuration) sont sur la même machine. Cela crée une pression mémoire concurrente, avec des pauses GC, etc. dans le code géré. Mais peut-être plus important :il faut tout simplement une éternité pour commencer à faire quoi que ce soit . Par conséquent, j'ai refactorisé le code pour utiliser le yield return parallèle générateurs plutôt qu'une liste unique. Par exemple :

    static IEnumerable<Person> InventPeople(int seed, int count)
    {
        for(int i = 0; i < count; i++)
        {
            int f = 1 + seed + i;
            var item = new Person
            {
                Id = f,
                Name = Path.GetRandomFileName().Replace(".", "").Substring(0, appRandom.Value.Next(3, 6)) + " " + Path.GetRandomFileName().Replace(".", "").Substring(0, new Random(Guid.NewGuid().GetHashCode()).Next(3, 6)),
                Age = f % 90,
                Friends = ParallelEnumerable.Range(0, 100).Select(n => appRandom.Value.Next(1, f)).ToArray()
            };
            yield return item;
        }
    }

    static IEnumerable<T> Batchify<T>(this IEnumerable<T> source, int count)
    {
        var list = new List<T>(count);
        foreach(var item in source)
        {
            list.Add(item);
            if(list.Count == count)
            {
                foreach (var x in list) yield return x;
                list.Clear();
            }
        }
        foreach (var item in list) yield return item;
    }

avec :

foreach (var element in InventPeople(PER_THREAD * counter1, PER_THREAD).Batchify(1000))

Ici, le but de Batchify est de s'assurer qu'on n'aide pas trop le serveur en prenant un temps appréciable entre chaque opération - les données sont inventées par lots de 1000 et chaque lot est mis à disposition très rapidement.

J'étais également préoccupé par les performances de JSON, alors je suis passé à JIL :

    public static string ToJSON<T>(this T obj)
    {
        return Jil.JSON.Serialize<T>(obj);
    }

et puis juste pour le plaisir, j'ai déplacé le travail JSON dans le traitement par lots (pour que les boucles de traitement réelles :

 foreach (var element in InventPeople(PER_THREAD * counter1, PER_THREAD)
     .Select(x => new { x.Id, Json = x.ToJSON() }).Batchify(1000))

Cela a réduit un peu plus les temps, donc je peux charger 10M en 3 minutes 57 secondes, un taux de 42 194 rops. La plupart de ce temps est en fait un traitement local à l'intérieur de l'application. Si je le change pour que chaque thread charge le même item ITEMS / THREADS fois, puis cela passe à 1 minute 48 secondes - un taux de 92 592 rops.

Je ne sais pas si j'ai vraiment répondu à quelque chose, mais la version courte pourrait être simplement "essayez un délai d'attente plus long ; envisagez d'utiliser fire-and-forget).