sql - threads - thread c# windows form




Como executar uma grande quantidade de consultas sql assíncronas e em threads (4)

  1. Agrupe suas consultas com base na tabela e nas operações nessa tabela. Usando isso, você pode identificar quantas consultas sql assíncronas você pode executar em suas diferentes tabelas.
  2. Certifique-se do tamanho de cada tabela em relação à qual você irá executar. Porque se a tabela contiver milhões de linhas e você fizer uma operação de junção com alguma outra tabela, também aumentará o tempo ou, se for uma operação CUD, também poderá bloquear sua tabela.
    1. E também escolha o número de threads com base nos núcleos da sua CPU e não com base em suposições. Porque o núcleo da CPU executará um processo de cada vez, então é melhor você criar um número de núcleos * 2 segmentos são eficientes.

Então, primeiro estude seu conjunto de dados e, em seguida, faça os dois itens acima para que você possa identificar com facilidade quais são todas as consultas executadas paralela e eficientemente.

Espero que isso dê algumas idéias. Melhor você poderia usar qualquer script python para que assim você poderia facilmente acionar mais de um processo e também monitorar suas atividades.

Problema: Eu tenho uma quantidade enorme de consultas sql (cerca de 10k-20k) e quero executá-las de forma assíncrona em 50 (ou mais) threads.

Eu escrevi um script PowerShell para este trabalho, mas é muito lento (Demorou cerca de 20 horas para executar todos). O resultado desejado é de 3-4 horas no máximo.

Pergunta: Como posso otimizar esse script do powershell? Devo reconsiderar e usar outra tecnologia como python ou c# ?

Eu acho que é questão de powershell, porque quando eu verifico com whoisactive as consultas estão executando rapidamente. Criar, sair e descarregar trabalhos leva muito tempo, porque para cada segmento é criado instâncias PS separadas.

Meu código:

$NumberOfParallerThreads = 50;


$Arr_AllQueries = @('Exec [mystoredproc] @param1=1, @param2=2',
                    'Exec [mystoredproc] @param1=11, @param2=22',
                    'Exec [mystoredproc] @param1=111, @param2=222')

#Creating the batches
$counter = [pscustomobject] @{ Value = 0 };
$Batches_AllQueries = $Arr_AllQueries | Group-Object -Property { 
    [math]::Floor($counter.Value++ / $NumberOfParallerThreads) 
};

forEach ($item in $Batches_AllQueries) {
    $tmpBatch = $item.Group;

    $tmpBatch | % {

        $ScriptBlock = {
            # accept the loop variable across the job-context barrier
            param($query) 
            # Execute a command

            Try 
            {
                Write-Host "[processing '$query']"
                $objConnection = New-Object System.Data.SqlClient.SqlConnection;
                $objConnection.ConnectionString = 'Data Source=...';

                $ObjCmd = New-Object System.Data.SqlClient.SqlCommand;
                $ObjCmd.CommandText = $query;
                $ObjCmd.Connection = $objConnection;
                $ObjCmd.CommandTimeout = 0;

                $objAdapter = New-Object System.Data.SqlClient.SqlDataAdapter;
                $objAdapter.SelectCommand = $ObjCmd;
                $objDataTable = New-Object System.Data.DataTable;
                $objAdapter.Fill($objDataTable)  | Out-Null;

                $objConnection.Close();
                $objConnection = $null;
            } 
            Catch 
            { 
                $ErrorMessage = $_.Exception.Message
                $FailedItem = $_.Exception.ItemName
                Write-Host "[Error processing: $($query)]" -BackgroundColor Red;
                Write-Host $ErrorMessage 
            }

        }

        # pass the loop variable across the job-context barrier
        Start-Job $ScriptBlock -ArgumentList $_ | Out-Null
    }

    # Wait for all to complete
    While (Get-Job -State "Running") { Start-Sleep 2 }

    # Display output from all jobs
    Get-Job | Receive-Job | Out-Null

    # Cleanup
    Remove-Job *

}

ATUALIZAÇÃO :

Recursos: O servidor de banco de dados está em uma máquina remota com:

  • 24 GB de RAM,
  • 8 núcleos
  • 500 GB de armazenamento,
  • SQL Server 2016

Queremos usar o poder máximo de CPU.

Limitação do Framework: A única limitação é não usar o SQL Server para executar as consultas. Os pedidos devem vir de fontes externas como: Powershell, C #, Python, etc.


Eu não sei muito sobre PowerShell, mas eu executo SQL em C # todo o tempo no trabalho.

As novas palavras-chave async / await do C # tornam extremamente fácil fazer o que você está falando. O C # também criará um conjunto de encadeamentos para você com a quantidade ideal de encadeamentos para sua máquina.

async Task<DataTable> ExecuteQueryAsync(query)
{
    return await Task.Run(() => ExecuteQuerySync(query));
}

async Task ExecuteAllQueriesAsync()
{
    IList<Task<DataTable>> queryTasks = new List<Task<DataTable>>();

    foreach query
    {
         queryTasks.Add(ExecuteQueryAsync(query));
    }

    foreach task in queryTasks
    {
         await task;
    }
}

O código acima irá adicionar todas as consultas à fila de trabalho do pool de threads. Então espere em todos eles antes de completar. O resultado é que o nível máximo de paralelismo será alcançado para o seu SQL.

Espero que isto ajude!


RunspacePool é o caminho a seguir aqui, tente isto:

$AllQueries = @( ... )
$MaxThreads = 5

# Each thread keeps its own connection but shares the query queue
$ScriptBlock = {
    Param($WorkQueue)

    $objConnection = New-Object System.Data.SqlClient.SqlConnection
    $objConnection.ConnectionString = 'Data Source=...'

    $objCmd = New-Object System.Data.SqlClient.SqlCommand
    $objCmd.Connection = $objConnection
    $objCmd.CommandTimeout = 0

    $query = ""

    while ($WorkQueue.TryDequeue([ref]$query)) {
        $objCmd.CommandText = $query
        $objAdapter = New-Object System.Data.SqlClient.SqlDataAdapter $objCmd
        $objDataTable = New-Object System.Data.DataTable
        $objAdapter.Fill($objDataTable) | Out-Null
    }

    $objConnection.Close()

}

# create a pool
$pool = [RunspaceFactory]::CreateRunspacePool(1, $MaxThreads)
$pool.ApartmentState  = 'STA'
$pool.Open()

# convert the query array into a concurrent queue
$workQueue = New-Object System.Collections.Concurrent.ConcurrentQueue[object]
$AllQueries | % { $workQueue.Enqueue($_) }

$threads = @()

# Create each powershell thread and add them to the pool
1..$MaxThreads | % {
    $ps = [powershell]::Create()
    $ps.RunspacePool = $pool
    $ps.AddScript($ScriptBlock) | Out-Null
    $ps.AddParameter('WorkQueue', $workQueue) | Out-Null
    $threads += [pscustomobject]@{
        Ps = $ps
        Handle = $null
    }
}

# Start all the threads
$threads | % { $_.Handle = $_.Ps.BeginInvoke() }

# Wait for all the threads to complete - errors will still set the IsCompleted flag
while ($threads | ? { !$_.Handle.IsCompleted }) {
    Start-Sleep -Seconds 1
}

# Get any results and display an errors
$threads | % {
    $_.Ps.EndInvoke($_.Handle) | Write-Output
    if ($_.Ps.HadErrors) {
        $_.Ps.Streams.Error.ReadAll() | Write-Error
    }
}

Ao contrário dos jobs do powershell, um RunspacePools pode compartilhar recursos. Portanto, há uma fila simultânea de todas as consultas e cada thread mantém sua própria conexão com o banco de dados.

Como outros já disseram - a menos que você esteja testando o banco de dados, provavelmente é melhor reorganizar as consultas em inserções em massa.


Tente usar o SqlCmd .

Você pode usar executar vários processos usando Process.Start() e usar o sqlcmd para executar consultas em processos paralelos.

Claro, se você é obrigado a fazer isso em tópicos, essa resposta não será mais a solução.





parallel-processing