Database
 sql >> Teknologi Basis Data >  >> RDS >> Database

WhoIsActive Runner

Saat ini, dalam komunitas SQL Server DBA, kemungkinan besar kami menggunakan, atau setidaknya pernah mendengar, prosedur tersimpan yang terkenal sp_WhoIsActive dikembangkan oleh Adam Machanic.

Selama waktu saya sebagai DBA, saya menggunakan SP untuk segera memeriksa apa yang terjadi di dalam contoh SQL Server tertentu ketika mendapatkan semua "tunjuk jari" bahwa aplikasi tertentu berjalan lambat.

Namun, ada saat-saat di mana masalah seperti itu menjadi berulang dan memerlukan cara untuk menangkap apa yang terjadi untuk menemukan penyebab potensial. Ada juga skenario di mana Anda memiliki beberapa instans yang berfungsi sebagai backend untuk aplikasi pihak ke-3. Prosedur Tersimpan dapat bekerja dengan baik untuk menemukan pelakunya.

Pada artikel ini, saya akan menyajikan alat PowerShell yang dapat membantu DBA SQL Server apa pun untuk mengumpulkan kueri yang terdeteksi oleh sp_WhoIsActive di dalam contoh SQL Server tertentu. SP itu akan mencocokkannya dengan String Pencarian tertentu dan menyimpannya dalam file keluaran untuk pasca-analisis.

Pertimbangan Awal

Berikut adalah beberapa asumsi sebelum masuk ke detail skrip:

  • Skrip menerima nama instance sebagai parameter. Jika tidak ada yang lolos, localhost akan diasumsikan oleh skrip.
  • Skrip akan meminta Anda untuk string pencarian tertentu untuk membandingkannya dengan teks kueri yang dieksekusi dalam contoh SQL Server. Jika ada kecocokan dengan salah satu dari mereka, itu akan disimpan dalam file .txt yang dapat Anda analisis nanti.
  • File keluaran dengan semua informasi yang terkait dengan instans Anda dibuat untuk jalur yang tepat di mana PowerShell berada dan dipicu. Pastikan Anda memiliki tulis izin di sana.
  • Jika Anda menjalankan skrip PowerShell beberapa kali untuk instans yang sama, semua file keluaran yang sudah ada sebelumnya akan ditimpa. Hanya yang terbaru yang akan disimpan. Oleh karena itu, jika Anda perlu menyimpan file yang sangat spesifik, simpan di tempat lain secara manual.
  • Paket ini mencakup .sql file dengan kode untuk menerapkan WhoIsActive Stored Procedure ke database master instance yang Anda tentukan. Script memeriksa apakah prosedur tersimpan sudah ada dalam instance dan membuatnya jika tidak.
    • Anda dapat memilih untuk menyebarkannya ke database lain. Pastikan saja modifikasi yang diperlukan dalam skrip.
    • Unduh .sql ini file dari hosting yang aman.
  • Skrip akan mencoba mengambil informasi dari instance SQL Server setiap 10 detik secara default. Namun jika Anda ingin menggunakan nilai yang berbeda, sesuaikan saja.
  • Pastikan bahwa pengguna yang diterapkan untuk terhubung ke instance SQL Server memiliki izin untuk membuat dan menjalankan Stored Procedures. Jika tidak, ia akan gagal mencapai tujuannya.

Menggunakan Skrip PowerShell

Inilah yang dapat Anda harapkan dari skrip:

Pergi ke lokasi di mana Anda meletakkan file skrip PowerShell dan jalankan seperti ini:

PS C:\temp> .\WhoIsActive-Runner.ps1 SERVER\INSTANCE

Saya menggunakan C:\temp sebagai contoh

Satu-satunya hal yang akan ditanyakan skrip kepada Anda adalah jenis login yang ingin Anda gunakan untuk terhubung ke instance.

Catatan:Jika Anda menggunakan PowerShell ISE, maka petunjuknya akan terlihat seperti tangkapan layar. Jika Anda menjalankannya langsung dari konsol PowerShell, maka opsi akan diminta sebagai teks dalam jendela yang sama .

Tepercaya – koneksi ke instance SQL Server akan dibuat dengan pengguna yang sama seperti untuk eksekusi skrip PowerShell. Anda tidak perlu menentukan kredensial apa pun, itu akan menganggapnya berdasarkan konteksnya.

Masuk Windows – Anda harus memberikan login Windows untuk otentikasi yang benar.

Masuk SQL – Anda harus memberikan login SQL untuk otentikasi yang benar.

Apa pun opsi yang Anda pilih, pastikan opsi tersebut memiliki cukup hak istimewa untuk melakukan pemeriksaan .

Jika Anda memilih jenis login yang mengharuskan Anda memasukkan kredensial, skrip akan memberi tahu Anda jika terjadi kegagalan:

Dengan informasi yang benar ditentukan, skrip akan memeriksa apakah SP ada di database master dan melanjutkan untuk membuatnya jika tidak.

Pastikan file .sql dengan kode T-SQL untuk membuat SP berada di jalur yang sama dengan tempat skrip berada. .sql nama file harus sp_WhoIsActive.sql .

Jika Anda ingin menggunakan nama file .sql yang berbeda dan database target yang berbeda, pastikan modifikasi yang diperlukan di dalam skrip PowerShell:

Langkah selanjutnya adalah Permintaan String Pencarian . Anda harus memasukkannya untuk mengumpulkan kecocokan yang dikembalikan oleh setiap iterasi eksekusi Prosedur Tersimpan di dalam instance SQL Server.

Setelah itu, Anda harus memilih berapa lama waktu yang Anda inginkan untuk eksekusi skrip.

Untuk tujuan demonstrasi, saya akan memilih opsi #1 (5 menit). Saya akan membiarkan kueri dummy berjalan di instance saya. Kuerinya adalah TUNGGU PENUNDAAN '00:10′ . Saya akan menentukan String Pencarian WAITFOR sehingga Anda dapat mengetahui apa yang akan dilakukan skrip untuk Anda.

Setelah skrip menyelesaikan eksekusinya, Anda akan melihat .txt file yang berisi nama instance Anda dan WhoIsActive sebagai sufiks.

Berikut ini contoh skrip yang ditangkap dan disimpan di .txt berkas:

Kode Lengkap Skrip PowerShell

Jika Anda ingin mencoba skrip ini, silakan gunakan kode di bawah ini:

param(
    $instance = "localhost"
)

if (!(Get-Module -ListAvailable -Name "SQLPS")) {
    Write-Host -BackgroundColor Red -ForegroundColor White "Module Invoke-Sqlcmd is not loaded"
    exit
}

#Function to execute queries (depending on if the user will be using specific credentials or not)
function Execute-Query([string]$query,[string]$database,[string]$instance,[int]$trusted,[string]$username,[string]$password){
    if($trusted -eq 1){
        try{ 
            Invoke-Sqlcmd -Query $query -Database $database -ServerInstance $instance -ErrorAction Stop -ConnectionTimeout 5 -QueryTimeout 0      
        }
        catch{
            Write-Host -BackgroundColor Red -ForegroundColor White $_
            exit
        }
    }
    else{
        try{
            Invoke-Sqlcmd -Query $query -Database $database -ServerInstance $instance -Username $username -Password $password -ErrorAction Stop -ConnectionTimeout 5 -QueryTimeout 0
        }
         catch{
            Write-Host -BackgroundColor Red -ForegroundColor White $_
            exit
        }
    }
}

function Get-Property([string]$property,[string]$instance){
    Write-Host -NoNewline "$($property) " 
    Write-Host @greenCheck
    Write-Host ""
    switch($loginChoice){
        0       {$output = Execute-Query "SELECT SERVERPROPERTY('$($property)')" "master" $instance 1 "" ""}
        default {$output = Execute-Query "SELECT SERVERPROPERTY('$($property)')" "master" $instance 0 $login $password}   
    }
    switch($property){ 
        "EngineEdition"{
            switch($output[0]){
                1 {"$($property): Personal or Desktop Engine" | Out-File -FilePath $filePath -Append}
                2 {"$($property): Standard" | Out-File -FilePath $filePath -Append}
                3 {"$($property): Enterprise" | Out-File -FilePath $filePath -Append}
                4 {"$($property): Express" | Out-File -FilePath $filePath -Append}
                5 {"$($property): SQL Database" | Out-File -FilePath $filePath -Append}
                6 {"$($property): Microsoft Azure Synapse Analytics" | Out-File -FilePath $filePath -Append}
                8 {"$($property): Azure SQL Managed Instance" | Out-File -FilePath $filePath -Append}
                9 {"$($property): Azure SQL Edge" | Out-File -FilePath $filePath -Append}
                11{"$($property): Azure Synapse serverless SQL pool" | Out-File -FilePath $filePath -Append}            
            }
        }
        "HadrManagerStatus"{
            switch($output[0]){
                0       {"$($property): Not started, pending communication." | Out-File -FilePath $filePath -Append}
                1       {"$($property): Started and running." | Out-File -FilePath $filePath -Append}
                2       {"$($property): Not started and failed." | Out-File -FilePath $filePath -Append}
                default {"$($property): Input is not valid, an error, or not applicable." | Out-File -FilePath $filePath -Append}            
            }
        }
        "IsIntegratedSecurityOnly"{
            switch($output[0]){
                1{"$($property): Integrated security (Windows Authentication)" | Out-File -FilePath $filePath -Append}
                0{"$($property): Not integrated security. (Both Windows Authentication and SQL Server Authentication.)" | Out-File -FilePath $filePath -Append}                
            }
        }
        default{                        
            if($output[0] -isnot [DBNull]){
                "$($property): $($output[0])" | Out-File -FilePath $filePath -Append
            }else{
                "$($property): N/A" | Out-File -FilePath $filePath -Append
            }
        }
    }
    
    return
}

$filePath = ".\$($instance.replace('\','_'))_WhoIsActive.txt"
Remove-Item $filePath -ErrorAction Ignore

$loginChoices = [System.Management.Automation.Host.ChoiceDescription[]] @("&Trusted", "&Windows Login", "&SQL Login")
$loginChoice = $host.UI.PromptForChoice('', 'Choose login type for instance', $loginChoices, 0)
switch($loginChoice)
{
    1 { 
        $login          = Read-Host -Prompt "Enter Windows Login"
        $securePassword = Read-Host -Prompt "Enter Password" -AsSecureString
        $password       = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($securePassword))
      }
    2 { 
        $login          = Read-Host -Prompt "Enter SQL Login"
        $securePassword = Read-Host -Prompt "Enter Password" -AsSecureString
        $password       = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($securePassword))
      }
}

#Attempt to connect to the SQL Server instance using the information provided by the user
try{
    switch($loginChoice){
        0{
            $spExists = Execute-Query "SELECT COUNT(*) FROM sys.objects WHERE type = 'P' AND name = 'sp_WhoIsActive'" "master" $instance 1 "" ""
            if($spExists[0] -eq 0){
                Write-Host "The Stored Procedure doesn't exist in the master database."
                Write-Host "Attempting its creation..."
                try{
                    Invoke-Sqlcmd -ServerInstance $instance -Database "master" -InputFile .\sp_WhoIsActive.sql
                    Write-Host -BackgroundColor Green -ForegroundColor White "Success!"
                }
                catch{
                    Write-Host -BackgroundColor Red -ForegroundColor White $_
                    exit
                }
            }
        }
        default{
            $spExists = Execute-Query "SELECT COUNT(*) FROM sys.objects WHERE type = 'P' AND name = 'sp_WhoIsActive'" "master" $instance 0 $login $password
            if($spExists[0] -eq 0){
                Write-Host "The Stored Procedure doesn't exist in the master database."
                Write-Host "Attempting its creation..."
                try{
                    Invoke-Sqlcmd -ServerInstance $instance -Database "master" -Username $login -Password $password -InputFile .\sp_WhoIsActive.sql
                    Write-Host -BackgroundColor Green -ForegroundColor White "Success!"
                }
                catch{
                    Write-Host -BackgroundColor Red -ForegroundColor White $_
                    exit
                }
            }
        }   
    }     
}
catch{
    Write-Host -BackgroundColor Red -ForegroundColor White $_
    exit
}

#If the connection succeeds, then proceed with the retrieval of the configuration for the instance
Write-Host " _______  _______                           _______ _________ _______  _______  _______ __________________          _______ "
Write-Host "(  ____ \(  ____ )       |\     /||\     /|(  ___  )\__   __/(  ____ \(  ___  )(  ____ \\__   __/\__   __/|\     /|(  ____ \"
Write-Host "| (    \/| (    )|       | )   ( || )   ( || (   ) |   ) (   | (    \/| (   ) || (    \/   ) (      ) (   | )   ( || (    \/"
Write-Host "| (_____ | (____)| _____ | | _ | || (___) || |   | |   | |   | (_____ | (___) || |         | |      | |   | |   | || (__    "
Write-Host "(_____  )|  _____)(_____)| |( )| ||  ___  || |   | |   | |   (_____  )|  ___  || |         | |      | |   ( (   ) )|  __)   "
Write-Host "      ) || (             | || || || (   ) || |   | |   | |         ) || (   ) || |         | |      | |    \ \_/ / | (      "
Write-Host "/\____) || )             | () () || )   ( || (___) |___) (___/\____) || )   ( || (____/\   | |   ___) (___  \   /  | (____/\"
Write-Host "\_______)|/              (_______)|/     \|(_______)\_______/\_______)|/     \|(_______/   )_(   \_______/   \_/   (_______/"                                                                                                                            
Write-Host ""
$searchString = Read-Host "Enter string to lookup"  
$timerChoices = [System.Management.Automation.Host.ChoiceDescription[]] @("&1)5m", "&2)10m", "&3)15m","&4)30m","&5)Indefinitely")
$timerChoice  = $host.UI.PromptForChoice('', 'How long should the script run?', $timerChoices, 0)

Write-Host -NoNewline "Script will run "
switch($timerChoice){
    0{
        Write-Host "for 5 minutes."
        $limit = 5
    }
    1{
        Write-Host "for 10 minutes."
        $limit = 10
    }
    2{
        Write-Host "for 15 minutes."
        $limit = 15
    }
    3{
        Write-Host "for 30 minutes."
        $limit = 30
    }
    4{
        Write-Host "indefinitely (press ctrl-c to exit)."
        $limit = 2000000
    }
}
Write-Host "Start TimeStamp: $(Get-Date)"

$StopWatch = [system.diagnostics.stopwatch]::StartNew()

while($StopWatch.Elapsed.TotalMinutes -lt $limit){
    $results = Execute-Query "EXEC sp_WhoIsActive" "master" $instance 1 "" ""
    Get-Date | Out-File -FilePath $filePath -Append
    "####################################################################" | Out-File -FilePath $filePath -Append
    foreach($result in $results){
        if($result.sql_text -match $searchString){
            $result | Out-File -FilePath $filePath -Append
        }
        "####################################################################" | Out-File -FilePath $filePath -Append
    }
    Start-Sleep -s 10
}
Get-Date | Out-File -FilePath $filePath -Append
"####################################################################" | Out-File -FilePath $filePath -Append
Write-Host "End TimeStamp  : $(Get-Date)"

Kesimpulan

Perlu diingat bahwa WhoIsActive tidak akan menangkap kueri yang dieksekusi sangat cepat oleh DB Engine. Namun, inti dari alat ini adalah untuk mendeteksi kueri bermasalah yang lambat dan dapat mengambil manfaat dari putaran (atau putaran) pengoptimalan.

Anda mungkin berpendapat bahwa pelacakan Profiler atau sesi Acara yang Diperpanjang dapat mencapai hal yang sama. Namun, saya merasa sangat nyaman bahwa Anda cukup menjalankan beberapa jendela PowerShell dan menjalankan masing-masing terhadap contoh yang berbeda pada waktu yang sama. Ini adalah sesuatu yang bisa menjadi sedikit membosankan untuk beberapa kasus.

Dengan menggunakan ini sebagai batu loncatan, Anda dapat melangkah lebih jauh dan mengonfigurasi mekanisme peringatan untuk mendapatkan pemberitahuan tentang setiap kejadian yang terdeteksi oleh skrip untuk kueri apa pun yang telah berjalan selama lebih dari X jumlah menit.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Bagaimana Menghubungkan Database ke Python

  2. Cara Memfilter Catatan dengan Fungsi Agregat SUM

  3. Menggunakan Wizard Penemuan Metadata

  4. Apa itu Kursor dalam SQL dan bagaimana mengimplementasikannya?

  5. Menetapkan dan Mengidentifikasi Sasaran Baris dalam Rencana Eksekusi