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?
“`
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
yawait
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 objetoSemaphoreSlim
con el número máximo de llamadas concurrentes permitidas y luego utilizarlo para controlar las llamadas realizadas dentro de las tareasasync
.En el código anterior,
_databaseSemaphore
es un objetoSemaphoreSlim
creado con el valormaxConcurrency
. Cada tarea espera a que haya un espacio disponible en el semáforo utilizando el métodoWaitAsync()
. Una vez que ha adquirido un espacio, ejecuta la llamada a la base de datos y agrega el resultado adataSetQueue
. Finalmente, libera el espacio del semáforo utilizando el métodoRelease()
.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.