es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Ejecuta Task.WhenAll(tareas) para realizar llamadas a ADO.NET y obtener datos en paralelo para mejorar el rendimiento del tiempo.

Tengo un procedimiento almacenado que obtiene datos de 1,1 millones de registros. Está tardando más de 1 minuto.

Por favor, tenga en cuenta que no hay filtros de columna en la consulta, por lo que no puedo aplicar ningún índice no agrupado para mejorar el rendimiento del tiempo.

Pero tengo la opción de enviar parámetros de paginación “start” y “limit” al procedimiento almacenado, y usando esos puedo obtener unos pocos registros a la vez. Mi idea es obtener 200,000 registros a la vez, ya que tarda 10 segundos en obtener 200,000 registros. Mi idea es que debería tardar solo 10 segundos en obtener más de 1,1 millones de registros haciendo 6 llamadas a la base de datos en paralelo.

Intenté usar el siguiente código para obtener datos en paralelo, pero aún tarda más de 1 minuto.

¿Puede alguien explicar por qué tarda el mismo tiempo? ¿Cómo puedo mejorar el rendimiento del tiempo desde el código de llamada en C#?

Aquí “Ranges” es un diccionario con valores clave:

Ranges = [0, 200000], [200000, 400000], [400000, 600000], [600000, 800000], [800000, 1000000], [1000000, 15555]

“`
var dataSetQueue = new ConcurrentQueue<dataset>();
var tasks = ranges.Select(async range =>
{
var paramlist = new List<sqlparameter>();
paramlist.AddParameter("start", range.Key);
paramlist.AddParameter("limit", range.Value);
var dataSet = await _databaseManager.ExecuteDataSetAsync(MySP, paramlist, ConnectionStringName);
dataSetQueue.Enqueue(dataSet);
});

await Task.WhenAll(tasks); // <– Problema: Aún tarda más de 1 minuto en ejecutar. ¿Por qué y cómo?
“`

Tags:  , , , ,

Answer

  1. Avatar for davy.ai

    Explicación y Solución para el Procedimiento Almacenado Lento con Paralelismo

    El problema con el enfoque actual es que, aunque los datos se obtienen en paralelo utilizando múltiples llamadas a la base de datos, las llamadas se siguen realizando de forma secuencial en lugar de concurrente. Esto se debe a que las palabras clave async y await siguen esperando a que cada tarea finalice antes de pasar a la siguiente.

    Para ejecutar realmente las llamadas de forma concurrente, podemos utilizar la clase SemaphoreSlim para limitar el número de llamadas paralelas a la base de datos que se realizan. Podemos crear un objeto SemaphoreSlim con el número máximo de llamadas concurrentes permitidas y luego utilizarlo para controlar las llamadas realizadas dentro de las tareas async.

    int maxConcurrency = 6;
    var dataSetQueue = new ConcurrentQueue<dataset>();
    var tasks = ranges.Select(async range =>
    {
        await _databaseSemaphore.WaitAsync(); // Limitar llamadas concurrentes
        try
        {
            var paramlist = new List<sqlparameter>();
            paramlist.AddParameter("start", range.Key);
            paramlist.AddParameter("limit", range.Value);
            var dataSet = await _databaseManager.ExecuteDataSetAsync(MySP, paramlist, ConnectionStringName);
            dataSetQueue.Enqueue(dataSet);
        }
        finally
        {
            _databaseSemaphore.Release(); // Liberar semáforo cuando la llamada finalice
        }
    });
    
    await Task.WhenAll(tasks);
    

    En el código anterior, _databaseSemaphore es un objeto SemaphoreSlim creado con el valor maxConcurrency. Cada tarea espera a que haya un espacio disponible en el semáforo utilizando el método WaitAsync(). Una vez que ha adquirido un espacio, ejecuta la llamada a la base de datos y agrega el resultado a dataSetQueue. Finalmente, libera el espacio del semáforo utilizando el método Release().

    Al limitar el número de llamadas paralelas a la base de datos y ejecutarlas de forma concurrente, podemos reducir significativamente el tiempo necesario para obtener los 1.1+ millones de registros.

Comments are closed.