sql >> Base de Datos >  >> NoSQL >> Redis

¿Ejecución paralela con StackExchange.Redis?

Actualmente, su código está usando la API síncrona (StringSet ) y está siendo cargado por 10 subprocesos al mismo tiempo. Esto no presentará ningún desafío apreciable para SE.Redis; funciona bien aquí. sospecho que realmente es un tiempo de espera en el que el servidor ha tardado más de lo que le gustaría en procesar algunos de los datos, muy probablemente también relacionados con el asignador del servidor. Entonces, una opción es simplemente aumentar un poco el tiempo de espera . No mucho... intente 5 segundos en lugar del valor predeterminado de 1 segundo. Probablemente, la mayoría de las operaciones están funcionando muy rápido de todos modos.

Con respecto a acelerarlo:una opción aquí es no esperar - es decir, mantener los datos de canalización. Si está satisfecho con no verificar cada mensaje individual en busca de un estado de error, entonces una forma simple de hacerlo es agregar , flags: CommandFlags.FireAndForget hasta el final de su StringSet llamar. En mis pruebas locales, esto aceleró el ejemplo de 1 millón en un 25 % (y sospecho que gran parte del tiempo restante se dedica a la serialización de cadenas).

El mayor problema que tuve con el ejemplo de 10M fue simplemente la sobrecarga de trabajar con el ejemplo de 10M - especialmente porque esto requiere una gran cantidad de memoria tanto para el redis-server y la aplicación, que (para emular su configuración) están en la misma máquina. Esto crea una presión de memoria competitiva, con pausas de GC, etc. en el código administrado. Pero quizás lo más importante:simplemente se tarda una eternidad en empezar a hacer cualquier cosa . En consecuencia, refactoricé el código para usar yield return paralelo generadores en lugar de una sola lista. Por ejemplo:

    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;
    }

con:

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

Aquí, el propósito de Batchify es asegurarnos de que no estamos ayudando demasiado al servidor tomando un tiempo apreciable entre cada operación:los datos se inventan en lotes de 1000 y cada lote está disponible muy rápidamente.

También me preocupaba el rendimiento de JSON, así que cambié a JIL:

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

y luego, solo por diversión, moví el trabajo JSON al procesamiento por lotes (para que el procesamiento real se repita:

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

Esto redujo un poco más los tiempos, así que puedo cargar 10M en 3 minutos 57 segundos, una tasa de 42,194 saltos. La mayor parte de este tiempo es en realidad procesamiento local dentro de la aplicación. Si lo cambio para que cada hilo cargue lo mismo mismo artículo ITEMS / THREADS veces, luego cambia a 1 minuto 48 segundos - una tasa de 92,592 rops.

No estoy seguro de haber respondido algo realmente, pero la versión corta podría ser simplemente "intenta un tiempo de espera más largo; considera usar disparar y olvidar".